@vellumai/assistant 0.8.5 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +33 -1
- package/ARCHITECTURE.md +1 -1
- package/bunfig.toml +6 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +1900 -166
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +3 -2
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +12 -14
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
- package/src/__tests__/conversation-agent-loop.test.ts +488 -2
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +69 -8
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -5
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +26 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +5 -4
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-resolver.test.ts +331 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +3 -2
- package/src/__tests__/openai-provider.test.ts +8 -9
- package/src/__tests__/openai-responses-provider.test.ts +70 -10
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/plugin-bootstrap.test.ts +9 -11
- package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
- package/src/__tests__/process-message-background-slack.test.ts +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +14 -16
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +5 -4
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +38 -0
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/loop.ts +80 -0
- package/src/api/README.md +124 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/{events → api/events}/relationship-state-updated.ts +3 -3
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +128 -3
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/memory-v3-render.ts +147 -0
- package/src/cli/commands/memory-v3.ts +255 -4
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +23 -42
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +1 -1
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +90 -1
- package/src/config/schemas/memory-v2.ts +26 -0
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/token-estimator.ts +10 -5
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
- package/src/daemon/conversation-agent-loop.ts +132 -28
- package/src/daemon/conversation-error.ts +33 -5
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +29 -9
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +33 -48
- package/src/daemon/conversation.ts +26 -46
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +27 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/lifecycle.ts +12 -12
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-types/conversations.ts +20 -22
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -2
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +23 -38
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +28 -29
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +352 -112
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +4 -0
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-worker.ts +12 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
- package/src/memory/llm-request-log-source-local.ts +24 -0
- package/src/memory/llm-request-log-source.ts +31 -0
- package/src/memory/llm-request-log-store.ts +188 -3
- package/src/memory/memory-v2-activation-log-store.ts +95 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edges.test.ts +144 -1
- package/src/memory/v3/__tests__/filter.test.ts +48 -0
- package/src/memory/v3/__tests__/gate.test.ts +96 -33
- package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
- package/src/memory/v3/__tests__/loop.test.ts +250 -5
- package/src/memory/v3/__tests__/scouts.test.ts +49 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
- package/src/memory/v3/__tests__/traversal.test.ts +39 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
- package/src/memory/v3/__tests__/validate.test.ts +32 -0
- package/src/memory/v3/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edges.ts +58 -21
- package/src/memory/v3/filter.ts +27 -22
- package/src/memory/v3/gate.ts +51 -36
- package/src/memory/v3/index-composition.ts +18 -5
- package/src/memory/v3/loop.ts +65 -17
- package/src/memory/v3/scouts.ts +15 -4
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +44 -2
- package/src/memory/v3/traversal.ts +6 -1
- package/src/memory/v3/tree-walk.ts +6 -1
- package/src/memory/v3/validate.ts +56 -33
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugins/defaults/injectors.ts +2 -8
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/types.ts +57 -13
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +7 -11
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/call-site-routing.ts +33 -9
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +24 -3
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +110 -12
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +53 -53
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +18 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -4
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +9 -0
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/auth/route-policy.ts +10 -0
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/background-wake-routes.ts +188 -20
- package/src/runtime/routes/btw-routes.ts +4 -4
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +147 -0
- package/src/runtime/routes/conversation-management-routes.ts +39 -14
- package/src/runtime/routes/conversation-query-routes.ts +60 -10
- package/src/runtime/routes/conversation-routes.ts +186 -140
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/documents-routes.ts +10 -1
- package/src/runtime/routes/group-routes.ts +11 -0
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-v3-routes.ts +160 -2
- package/src/runtime/routes/migration-routes.ts +20 -13
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/tools/apps/definitions.ts +35 -21
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +268 -266
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +2 -5
- package/src/tools/executor.ts +18 -55
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-search.ts +199 -44
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +146 -103
- package/src/tools/schedule/create.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +17 -15
- package/src/tools/ui-surface/definitions.ts +98 -86
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/util/browser.ts +7 -2
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -9,6 +9,7 @@ import { getLogger } from "../util/logger.js";
|
|
|
9
9
|
import { BYOOAuthConnection } from "./byo-connection.js";
|
|
10
10
|
import type { OAuthConnection } from "./connection.js";
|
|
11
11
|
import { getConnectionAccessTokenResult } from "./credential-token-resolver.js";
|
|
12
|
+
import { syncManualTokenConnection } from "./manual-token-connection.js";
|
|
12
13
|
import { getActiveConnection, getProvider } from "./oauth-store.js";
|
|
13
14
|
import { PlatformOAuthConnection } from "./platform-connection.js";
|
|
14
15
|
|
|
@@ -82,6 +83,10 @@ export async function resolveOAuthConnection(
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
// BYO path — requires a local connection row, access token, and base URL.
|
|
86
|
+
if (providerRow?.authorizeUrl === "urn:manual-token") {
|
|
87
|
+
await syncManualTokenConnection(provider);
|
|
88
|
+
}
|
|
89
|
+
|
|
85
90
|
const conn = getActiveConnection(provider, { clientId, account });
|
|
86
91
|
if (!conn) {
|
|
87
92
|
const filters = [
|
|
@@ -185,20 +190,26 @@ interface ResolvePlatformConnectionIdOptions {
|
|
|
185
190
|
account?: string;
|
|
186
191
|
}
|
|
187
192
|
|
|
193
|
+
interface PlatformConnectionEntry {
|
|
194
|
+
id: string;
|
|
195
|
+
account_label?: string | null;
|
|
196
|
+
}
|
|
197
|
+
|
|
188
198
|
/**
|
|
189
|
-
* Fetch
|
|
190
|
-
*
|
|
199
|
+
* Fetch active platform connections for a managed provider by calling the
|
|
200
|
+
* List Connections endpoint.
|
|
191
201
|
*/
|
|
192
|
-
async function
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
202
|
+
async function fetchPlatformConnections(options: {
|
|
203
|
+
client: VellumPlatformClient;
|
|
204
|
+
provider: string;
|
|
205
|
+
accountIdentifier?: string;
|
|
206
|
+
}): Promise<PlatformConnectionEntry[]> {
|
|
207
|
+
const { client, provider, accountIdentifier } = options;
|
|
197
208
|
const params = new URLSearchParams();
|
|
198
209
|
params.set("provider", provider);
|
|
199
210
|
params.set("status", "ACTIVE");
|
|
200
|
-
if (
|
|
201
|
-
params.set("account_identifier",
|
|
211
|
+
if (accountIdentifier) {
|
|
212
|
+
params.set("account_identifier", accountIdentifier);
|
|
202
213
|
}
|
|
203
214
|
|
|
204
215
|
const path = `/v1/assistants/${client.platformAssistantId}/oauth/connections/?${params.toString()}`;
|
|
@@ -219,7 +230,35 @@ async function resolvePlatformConnectionId(
|
|
|
219
230
|
Array.isArray(body)
|
|
220
231
|
? body
|
|
221
232
|
: ((body as Record<string, unknown>).results ?? [])
|
|
222
|
-
) as
|
|
233
|
+
) as PlatformConnectionEntry[];
|
|
234
|
+
return connections;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Fetch the platform-side connection ID for a managed provider by calling
|
|
239
|
+
* the List Connections endpoint.
|
|
240
|
+
*/
|
|
241
|
+
async function resolvePlatformConnectionId(
|
|
242
|
+
options: ResolvePlatformConnectionIdOptions,
|
|
243
|
+
): Promise<string> {
|
|
244
|
+
const { client, provider, account } = options;
|
|
245
|
+
|
|
246
|
+
let connections = await fetchPlatformConnections({
|
|
247
|
+
client,
|
|
248
|
+
provider,
|
|
249
|
+
accountIdentifier: account,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
if (account && connections.length === 0) {
|
|
253
|
+
const unfilteredConnections = await fetchPlatformConnections({
|
|
254
|
+
client,
|
|
255
|
+
provider,
|
|
256
|
+
});
|
|
257
|
+
connections = unfilteredConnections.filter(
|
|
258
|
+
(connection) =>
|
|
259
|
+
connection.account_label === account || connection.id === account,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
223
262
|
|
|
224
263
|
if (connections.length === 0) {
|
|
225
264
|
throw new Error(
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { credentialKey } from "../security/credential-key.js";
|
|
12
12
|
import { getSecureKeyResultAsync } from "../security/secure-keys.js";
|
|
13
|
+
import { getTelegramBotUsername } from "../telegram/bot-username.js";
|
|
13
14
|
import { getLogger } from "../util/logger.js";
|
|
14
15
|
import {
|
|
15
16
|
createConnection,
|
|
@@ -24,6 +25,44 @@ const log = getLogger("manual-token-connection");
|
|
|
24
25
|
/** Sentinel client_id used for non-OAuth providers that don't have a real app. */
|
|
25
26
|
const MANUAL_TOKEN_CLIENT_ID = "manual-config";
|
|
26
27
|
|
|
28
|
+
type ResolvedAccountInfoSource = "provided" | "derived" | "none";
|
|
29
|
+
|
|
30
|
+
interface ResolvedAccountInfo {
|
|
31
|
+
value?: string;
|
|
32
|
+
source: ResolvedAccountInfoSource;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveManualTokenAccountInfo(
|
|
36
|
+
provider: string,
|
|
37
|
+
accountInfo?: string,
|
|
38
|
+
): ResolvedAccountInfo {
|
|
39
|
+
if (accountInfo !== undefined) {
|
|
40
|
+
return { value: accountInfo, source: "provided" };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (provider === "telegram") {
|
|
44
|
+
const botUsername = getTelegramBotUsername();
|
|
45
|
+
if (!botUsername) return { source: "none" };
|
|
46
|
+
return {
|
|
47
|
+
value: botUsername.startsWith("@") ? botUsername : `@${botUsername}`,
|
|
48
|
+
source: "derived",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { source: "none" };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function accountInfoForManualTokenSync(
|
|
56
|
+
provider: string,
|
|
57
|
+
resolved: ResolvedAccountInfo,
|
|
58
|
+
): string | undefined {
|
|
59
|
+
if (resolved.source !== "derived") return resolved.value;
|
|
60
|
+
|
|
61
|
+
const existing = getConnectionByProvider(provider);
|
|
62
|
+
if (existing?.accountInfo) return undefined;
|
|
63
|
+
return resolved.value;
|
|
64
|
+
}
|
|
65
|
+
|
|
27
66
|
/**
|
|
28
67
|
* Ensure an active oauth_connection row exists for the given manual-token
|
|
29
68
|
* provider. Creates the synthetic oauth_app row on first use.
|
|
@@ -79,6 +118,15 @@ export async function syncManualTokenConnection(
|
|
|
79
118
|
provider: string,
|
|
80
119
|
accountInfo?: string,
|
|
81
120
|
): Promise<void> {
|
|
121
|
+
const resolvedAccountInfo = resolveManualTokenAccountInfo(
|
|
122
|
+
provider,
|
|
123
|
+
accountInfo,
|
|
124
|
+
);
|
|
125
|
+
const accountInfoToStore = accountInfoForManualTokenSync(
|
|
126
|
+
provider,
|
|
127
|
+
resolvedAccountInfo,
|
|
128
|
+
);
|
|
129
|
+
|
|
82
130
|
switch (provider) {
|
|
83
131
|
case "telegram": {
|
|
84
132
|
const botTokenResult = await getSecureKeyResultAsync(
|
|
@@ -94,7 +142,7 @@ export async function syncManualTokenConnection(
|
|
|
94
142
|
return;
|
|
95
143
|
}
|
|
96
144
|
if (botTokenResult.value && webhookSecretResult.value) {
|
|
97
|
-
await ensureManualTokenConnection(provider,
|
|
145
|
+
await ensureManualTokenConnection(provider, accountInfoToStore);
|
|
98
146
|
} else {
|
|
99
147
|
removeManualTokenConnection(provider);
|
|
100
148
|
}
|
|
@@ -115,7 +163,7 @@ export async function syncManualTokenConnection(
|
|
|
115
163
|
return;
|
|
116
164
|
}
|
|
117
165
|
if (botTokenResult.value && appTokenResult.value) {
|
|
118
|
-
await ensureManualTokenConnection(provider,
|
|
166
|
+
await ensureManualTokenConnection(provider, accountInfoToStore);
|
|
119
167
|
} else {
|
|
120
168
|
removeManualTokenConnection(provider);
|
|
121
169
|
}
|
|
@@ -133,7 +181,7 @@ export async function syncManualTokenConnection(
|
|
|
133
181
|
return;
|
|
134
182
|
}
|
|
135
183
|
if (tokenResult.value) {
|
|
136
|
-
await ensureManualTokenConnection(provider,
|
|
184
|
+
await ensureManualTokenConnection(provider, accountInfoToStore);
|
|
137
185
|
} else {
|
|
138
186
|
removeManualTokenConnection(provider);
|
|
139
187
|
}
|
|
@@ -199,6 +199,9 @@ export const PROVIDER_SEED_DATA: Record<
|
|
|
199
199
|
},
|
|
200
200
|
],
|
|
201
201
|
appType: "Public integration",
|
|
202
|
+
setupNotes: [
|
|
203
|
+
"Enable Token Rotation on your Notion integration (developer dashboard → your integration → Configuration → Token rotation). Without it, Notion does not issue a refresh token and the connection cannot auto-recover if Notion revokes the access token server-side — you will silently lose access and need to reconnect manually.",
|
|
204
|
+
],
|
|
202
205
|
identityUrl: "https://api.notion.com/v1/users/me",
|
|
203
206
|
identityHeaders: { "Notion-Version": "2022-06-28" },
|
|
204
207
|
identityResponsePaths: ["name", "person.email"],
|
|
@@ -334,6 +334,19 @@ describe("no rule — third-party skill tool", () => {
|
|
|
334
334
|
expect(result.reason).toContain("Skill tool");
|
|
335
335
|
});
|
|
336
336
|
|
|
337
|
+
test("plugin origin → treated as extension-owned (prompt at strict threshold)", () => {
|
|
338
|
+
// Plugins join skills in the "extension-owned" bucket — both prompt by
|
|
339
|
+
// default. `isSkillBundled` is irrelevant for plugins (always false).
|
|
340
|
+
const result = evaluate({
|
|
341
|
+
riskLevel: RiskLevel.Low,
|
|
342
|
+
toolName: "custom_plugin_tool",
|
|
343
|
+
toolOrigin: "plugin",
|
|
344
|
+
autoApproveUpTo: "none",
|
|
345
|
+
});
|
|
346
|
+
expect(result.decision).toBe("prompt");
|
|
347
|
+
expect(result.reason).toContain("Skill tool");
|
|
348
|
+
});
|
|
349
|
+
|
|
337
350
|
test("no tool origin but hasManifestOverride, strict threshold → prompt (unregistered skill tool)", () => {
|
|
338
351
|
const result = evaluate({
|
|
339
352
|
riskLevel: RiskLevel.Low,
|
|
@@ -699,16 +712,17 @@ describe("edge cases", () => {
|
|
|
699
712
|
expect(result.reason).toContain("Skill tool");
|
|
700
713
|
});
|
|
701
714
|
|
|
702
|
-
test("hasManifestOverride with toolOrigin
|
|
715
|
+
test("hasManifestOverride with toolOrigin=mcp — falls through (not extension-owned)", () => {
|
|
703
716
|
const result = evaluate({
|
|
704
717
|
riskLevel: RiskLevel.Low,
|
|
705
718
|
toolName: "manifest_tool",
|
|
706
|
-
toolOrigin: "
|
|
719
|
+
toolOrigin: "mcp",
|
|
707
720
|
hasManifestOverride: true,
|
|
708
721
|
});
|
|
709
|
-
// toolOrigin is "
|
|
710
|
-
//
|
|
711
|
-
//
|
|
722
|
+
// toolOrigin is "mcp", which is not extension-class (skill/plugin), so
|
|
723
|
+
// the third-party skill check doesn't trigger. The hasManifestOverride
|
|
724
|
+
// sub-check requires !toolOrigin, but toolOrigin is set. Falls through
|
|
725
|
+
// to risk-based: Low → allow (within default "low" threshold).
|
|
712
726
|
expect(result.decision).toBe("allow");
|
|
713
727
|
expect(result.reason).toContain("low risk");
|
|
714
728
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OwnerKind } from "../tools/types.js";
|
|
1
2
|
import type { TrustRule } from "./types.js";
|
|
2
3
|
import { RiskLevel } from "./types.js";
|
|
3
4
|
|
|
@@ -13,8 +14,13 @@ export interface ApprovalContext {
|
|
|
13
14
|
matchedRule?: TrustRule;
|
|
14
15
|
isContainerized: boolean;
|
|
15
16
|
isWorkspaceScoped: boolean;
|
|
16
|
-
/**
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Owner kind of the tool, as recorded by the tool registry — "skill" /
|
|
19
|
+
* "plugin" / "mcp" for extension-owned tools, `undefined` for core tools
|
|
20
|
+
* (and for tools that aren't registered, e.g. unregistered skill tools
|
|
21
|
+
* matched only via `hasManifestOverride`).
|
|
22
|
+
*/
|
|
23
|
+
toolOrigin?: OwnerKind;
|
|
18
24
|
/** Whether the tool's owning skill is a first-party bundled skill. */
|
|
19
25
|
isSkillBundled?: boolean;
|
|
20
26
|
/** Whether the tool has a manifest override (unregistered skill tool). */
|
|
@@ -173,8 +179,13 @@ export class DefaultApprovalPolicy implements ApprovalPolicy {
|
|
|
173
179
|
|
|
174
180
|
// ── 6. No rule + third-party skill tool → prompt (unless threshold covers it)
|
|
175
181
|
if (!matchedRule) {
|
|
182
|
+
// Plugin- and skill-owned tools are both treated as extension-class
|
|
183
|
+
// for approval purposes: external by default, prompt unless bundled.
|
|
184
|
+
// MCP-owned tools fall through to the core risk-based path.
|
|
185
|
+
const isExtensionOwned =
|
|
186
|
+
toolOrigin === "skill" || toolOrigin === "plugin";
|
|
176
187
|
const isThirdPartySkill =
|
|
177
|
-
(
|
|
188
|
+
(isExtensionOwned && !isSkillBundled) ||
|
|
178
189
|
(hasManifestOverride && !toolOrigin);
|
|
179
190
|
if (isThirdPartySkill) {
|
|
180
191
|
if (isRiskWithinThreshold(riskLevel, context.autoApproveUpTo)) {
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
looksLikeHostPortShorthand,
|
|
16
16
|
looksLikePathOnlyInput,
|
|
17
17
|
} from "../tools/network/url-safety.js";
|
|
18
|
-
import { getTool } from "../tools/registry.js";
|
|
18
|
+
import { getTool, getToolOwner } from "../tools/registry.js";
|
|
19
|
+
import type { Tool } from "../tools/types.js";
|
|
19
20
|
import {
|
|
20
21
|
getDeprecatedDir,
|
|
21
22
|
getProtectedDir,
|
|
@@ -150,6 +151,23 @@ function resolveSkillIdAndHash(
|
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Resolve whether the skill that owns this tool is bundled (first-party).
|
|
156
|
+
* Returns false when the tool has no owning skill or the skill is not in
|
|
157
|
+
* the catalog. Derived from `loadSkillCatalog()` at check time so the
|
|
158
|
+
* answer reflects current catalog truth (managed overrides flip the bit
|
|
159
|
+
* without needing to re-register tools). Owner is looked up from the tool
|
|
160
|
+
* registry (`getToolOwner(name)`) rather than read from the `Tool` object,
|
|
161
|
+
* since ownership lives on the registry, not on the tool itself.
|
|
162
|
+
*/
|
|
163
|
+
function isToolOwnerSkillBundled(tool: Tool | undefined): boolean {
|
|
164
|
+
if (!tool) return false;
|
|
165
|
+
const owner = getToolOwner(tool.name);
|
|
166
|
+
if (owner?.kind !== "skill") return false;
|
|
167
|
+
const skill = loadSkillCatalog().find((s) => s.id === owner.id);
|
|
168
|
+
return skill?.bundled ?? false;
|
|
169
|
+
}
|
|
170
|
+
|
|
153
171
|
/**
|
|
154
172
|
* Check whether a skill (by id) has parsed inline command expansions.
|
|
155
173
|
* Returns false when the skill is not found in the catalog.
|
|
@@ -529,13 +547,8 @@ export async function check(
|
|
|
529
547
|
risk === RiskLevel.Low
|
|
530
548
|
? isWorkspaceScopedInvocation(toolName, input, workingDir)
|
|
531
549
|
: false,
|
|
532
|
-
toolOrigin:
|
|
533
|
-
|
|
534
|
-
? "skill"
|
|
535
|
-
: tool
|
|
536
|
-
? "builtin"
|
|
537
|
-
: undefined,
|
|
538
|
-
isSkillBundled: tool?.ownerSkillBundled ?? false,
|
|
550
|
+
toolOrigin: getToolOwner(toolName)?.kind,
|
|
551
|
+
isSkillBundled: isToolOwnerSkillBundled(tool),
|
|
539
552
|
hasManifestOverride: !!manifestOverride,
|
|
540
553
|
autoApproveUpTo: threshold,
|
|
541
554
|
hasSandboxAutoApprove,
|
|
@@ -10,6 +10,7 @@ let mockManagedProxyCtx = {
|
|
|
10
10
|
assistantApiKey: "",
|
|
11
11
|
};
|
|
12
12
|
let mockAssistantId = "";
|
|
13
|
+
let mockSecureKeys: Record<string, string | null | undefined> = {};
|
|
13
14
|
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
// Module mocks
|
|
@@ -26,7 +27,7 @@ mock.module("../config/env.js", () => ({
|
|
|
26
27
|
// Stub the credential-store fallback so tests stay hermetic and do not
|
|
27
28
|
// read real values from the host credential backend.
|
|
28
29
|
mock.module("../security/secure-keys.js", () => ({
|
|
29
|
-
getSecureKeyAsync: async () => null,
|
|
30
|
+
getSecureKeyAsync: async (key: string) => mockSecureKeys[key] ?? null,
|
|
30
31
|
}));
|
|
31
32
|
|
|
32
33
|
mock.module("../security/credential-key.js", () => ({
|
|
@@ -54,6 +55,7 @@ describe("VellumPlatformClient", () => {
|
|
|
54
55
|
assistantApiKey: "sk-test-key",
|
|
55
56
|
};
|
|
56
57
|
mockAssistantId = "asst-123";
|
|
58
|
+
mockSecureKeys = {};
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
afterEach(() => {
|
|
@@ -94,6 +96,27 @@ describe("VellumPlatformClient", () => {
|
|
|
94
96
|
const client = await VellumPlatformClient.create();
|
|
95
97
|
expect(client!.baseUrl).toBe("https://platform.example.com");
|
|
96
98
|
});
|
|
99
|
+
|
|
100
|
+
test("falls back to credential store values when managed context is not rehydrated", async () => {
|
|
101
|
+
mockManagedProxyCtx = {
|
|
102
|
+
enabled: false,
|
|
103
|
+
platformBaseUrl: "",
|
|
104
|
+
assistantApiKey: "",
|
|
105
|
+
};
|
|
106
|
+
mockAssistantId = "";
|
|
107
|
+
mockSecureKeys = {
|
|
108
|
+
"vellum:platform_base_url": "https://stored-platform.example.com/",
|
|
109
|
+
"vellum:assistant_api_key": "stored-api-key",
|
|
110
|
+
"vellum:platform_assistant_id": " stored-assistant-id ",
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const client = await VellumPlatformClient.create();
|
|
114
|
+
|
|
115
|
+
expect(client).not.toBeNull();
|
|
116
|
+
expect(client!.baseUrl).toBe("https://stored-platform.example.com");
|
|
117
|
+
expect(client!.assistantApiKey).toBe("stored-api-key");
|
|
118
|
+
expect(client!.platformAssistantId).toBe("stored-assistant-id");
|
|
119
|
+
});
|
|
97
120
|
});
|
|
98
121
|
|
|
99
122
|
describe("fetch()", () => {
|
package/src/platform/client.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { resolveManagedProxyContext } from "../providers/platform-proxy/context.
|
|
|
10
10
|
import { credentialKey } from "../security/credential-key.js";
|
|
11
11
|
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
12
12
|
import { getLogger } from "../util/logger.js";
|
|
13
|
+
import { arePlatformFeaturesEnabled } from "./feature-gate.js";
|
|
13
14
|
|
|
14
15
|
const log = getLogger("platform-client");
|
|
15
16
|
|
|
@@ -43,6 +44,13 @@ export class VellumPlatformClient {
|
|
|
43
44
|
* should check `platformAssistantId` themselves.
|
|
44
45
|
*/
|
|
45
46
|
static async create(): Promise<VellumPlatformClient | null> {
|
|
47
|
+
if (!arePlatformFeaturesEnabled()) {
|
|
48
|
+
log.debug(
|
|
49
|
+
"platform-features-in-local-mode is disabled — returning null",
|
|
50
|
+
);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
const ctx = await resolveManagedProxyContext();
|
|
47
55
|
|
|
48
56
|
let baseUrl = ctx.enabled ? ctx.platformBaseUrl : "";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
2
|
+
import { getIsPlatform } from "../config/env-registry.js";
|
|
3
|
+
import type { AssistantConfig } from "../config/schema.js";
|
|
4
|
+
|
|
5
|
+
const FLAG_KEY = "platform-features-in-local-mode" as const;
|
|
6
|
+
|
|
7
|
+
export function arePlatformFeaturesEnabled(
|
|
8
|
+
config?: AssistantConfig,
|
|
9
|
+
): boolean {
|
|
10
|
+
if (getIsPlatform()) return true;
|
|
11
|
+
return isAssistantFeatureFlagEnabled(
|
|
12
|
+
FLAG_KEY,
|
|
13
|
+
(config ?? {}) as AssistantConfig,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
|
|
49
49
|
import { resolve } from "node:path";
|
|
50
50
|
|
|
51
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
52
51
|
import { getConfig } from "../../config/loader.js";
|
|
53
52
|
import { getInContextPkbPaths } from "../../daemon/pkb-context-tracker.js";
|
|
54
53
|
import { buildPkbReminder } from "../../daemon/pkb-reminder-builder.js";
|
|
@@ -107,24 +106,19 @@ function readInjectionInputs(ctx: TurnContext): TurnInjectionInputs {
|
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
export const DISK_PRESSURE_WARNING_PROMPT = `<disk_pressure_warning>
|
|
110
|
-
Disk usage is critically low: this assistant is in storage cleanup mode because the workspace volume is
|
|
109
|
+
Disk usage is critically low: this assistant is in storage cleanup mode because the workspace volume is critically full.
|
|
111
110
|
|
|
112
111
|
In your first paragraph, warn the user that storage is critically low and that normal work is suspended until space is freed.
|
|
113
112
|
|
|
114
113
|
Then help the user clean up storage. Prefer safe inspection steps first, such as checking available space and finding large directories. Ask before deleting files or caches unless the user has already clearly approved the specific cleanup action.
|
|
115
114
|
|
|
116
|
-
Do not work on unrelated tasks until
|
|
115
|
+
Do not work on unrelated tasks until enough space is freed to clear the lock or the user explicitly overrides it. Background processes and messages from trusted contacts are blocked while this cleanup mode is active.
|
|
117
116
|
</disk_pressure_warning>`;
|
|
118
117
|
|
|
119
|
-
function isSafeStorageLimitsEnabled(): boolean {
|
|
120
|
-
return isAssistantFeatureFlagEnabled("safe-storage-limits", getConfig());
|
|
121
|
-
}
|
|
122
|
-
|
|
123
118
|
const diskPressureWarningInjector: Injector = {
|
|
124
119
|
name: "disk-pressure-warning",
|
|
125
120
|
order: DEFAULT_INJECTOR_ORDER.diskPressureWarning,
|
|
126
121
|
async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
|
|
127
|
-
if (!isSafeStorageLimitsEnabled()) return null;
|
|
128
122
|
const inputs = readInjectionInputs(ctx);
|
|
129
123
|
if (!inputs.diskPressureContext?.cleanupModeActive) return null;
|
|
130
124
|
return {
|
|
@@ -15,12 +15,17 @@
|
|
|
15
15
|
*
|
|
16
16
|
* The terminal dispatches on the discriminated {@link PersistArgs.op} field:
|
|
17
17
|
*
|
|
18
|
-
* - `add`
|
|
19
|
-
*
|
|
20
|
-
* - `
|
|
21
|
-
*
|
|
22
|
-
* - `
|
|
23
|
-
*
|
|
18
|
+
* - `add` → {@link addMessage}, optionally followed by
|
|
19
|
+
* {@link syncMessageToDisk} when `args.syncToDisk` is true.
|
|
20
|
+
* - `reserve` → {@link reserveMessage} — pre-allocates an empty row
|
|
21
|
+
* for assistant anchor stamping.
|
|
22
|
+
* - `updateContent` → {@link updateMessageContent} — overwrites an existing
|
|
23
|
+
* row's content (returns `void`, wrapped as
|
|
24
|
+
* `{ op: "updateContent" }`).
|
|
25
|
+
* - `update` → {@link updateMessageMetadata} (returns `void`, wrapped
|
|
26
|
+
* as `{ op: "update" }`).
|
|
27
|
+
* - `delete` → {@link deleteMessageById} (returns the segment/summary
|
|
28
|
+
* IDs the caller must clean up out-of-band).
|
|
24
29
|
*
|
|
25
30
|
* Manifest declares `provides.persistence: "v1"` so other plugins can
|
|
26
31
|
* negotiate against the pipeline surface and `requires.pluginRuntime: "v1"`
|
|
@@ -36,6 +41,8 @@
|
|
|
36
41
|
import {
|
|
37
42
|
addMessage,
|
|
38
43
|
deleteMessageById,
|
|
44
|
+
reserveMessage,
|
|
45
|
+
updateMessageContent,
|
|
39
46
|
updateMessageMetadata,
|
|
40
47
|
} from "../../memory/conversation-crud.js";
|
|
41
48
|
import { syncMessageToDisk } from "../../memory/conversation-disk-view.js";
|
|
@@ -74,6 +81,18 @@ export async function defaultPersistenceTerminal(
|
|
|
74
81
|
}
|
|
75
82
|
return { op: "add", message };
|
|
76
83
|
}
|
|
84
|
+
case "reserve": {
|
|
85
|
+
const message = await reserveMessage(
|
|
86
|
+
args.conversationId,
|
|
87
|
+
args.role,
|
|
88
|
+
args.metadata,
|
|
89
|
+
);
|
|
90
|
+
return { op: "reserve", message };
|
|
91
|
+
}
|
|
92
|
+
case "updateContent": {
|
|
93
|
+
updateMessageContent(args.messageId, args.content);
|
|
94
|
+
return { op: "updateContent" };
|
|
95
|
+
}
|
|
77
96
|
case "update": {
|
|
78
97
|
updateMessageMetadata(args.messageId, args.updates);
|
|
79
98
|
return { op: "update" };
|
package/src/plugins/types.ts
CHANGED
|
@@ -32,6 +32,7 @@ import type { RepairResult } from "../daemon/history-repair.js";
|
|
|
32
32
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
33
33
|
import type { PkbContextConversation } from "../daemon/pkb-context-tracker.js";
|
|
34
34
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
35
|
+
import type { MessageRole } from "../memory/conversation-crud.js";
|
|
35
36
|
import type { QdrantSparseVector } from "../memory/qdrant-client.js";
|
|
36
37
|
import type {
|
|
37
38
|
ContentBlock,
|
|
@@ -439,18 +440,25 @@ export interface OverflowReduceResult {
|
|
|
439
440
|
* Pipeline arguments for `persistence` — a discriminated union over the
|
|
440
441
|
* message-CRUD operations plugins may observe, redirect, or short-circuit:
|
|
441
442
|
*
|
|
442
|
-
* - `add`
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
* - `
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
443
|
+
* - `add` — append a new message (`addMessage`). Mirrors
|
|
444
|
+
* `addMessage(conversationId, role, content, metadata?, opts?)`.
|
|
445
|
+
* When `syncToDisk` is set, the default plugin also runs
|
|
446
|
+
* {@link syncMessageToDisk} against the just-persisted row
|
|
447
|
+
* so the JSONL disk view stays consistent. The
|
|
448
|
+
* `createdAtMs` field carries the conversation's creation
|
|
449
|
+
* timestamp — needed to resolve the disk-view directory.
|
|
450
|
+
* - `reserve` — pre-allocate an empty assistant anchor row
|
|
451
|
+
* (`reserveMessage`) so the agent loop can stamp streaming
|
|
452
|
+
* events with stable identity before any content is
|
|
453
|
+
* produced. Returns the same row shape as `add`.
|
|
454
|
+
* - `updateContent` — overwrite the content of an existing message
|
|
455
|
+
* (`updateMessageContent`). Used to finalize a previously
|
|
456
|
+
* reserved row, and by consolidation paths.
|
|
457
|
+
* - `update` — shallow-merge metadata into an existing message
|
|
458
|
+
* (`updateMessageMetadata`). Returns `void`.
|
|
459
|
+
* - `delete` — remove a single message (`deleteMessageById`). Returns
|
|
460
|
+
* the {@link DeletedMemoryIds}-shaped segment/summary IDs
|
|
461
|
+
* the caller must clean up out-of-band.
|
|
454
462
|
*
|
|
455
463
|
* The discriminated `op` field lets plugin middleware narrow the union and
|
|
456
464
|
* tailor behavior per-operation (e.g. "only observe deletes", "redirect
|
|
@@ -459,7 +467,7 @@ export interface OverflowReduceResult {
|
|
|
459
467
|
export type PersistAddArgs = {
|
|
460
468
|
readonly op: "add";
|
|
461
469
|
readonly conversationId: string;
|
|
462
|
-
readonly role:
|
|
470
|
+
readonly role: MessageRole;
|
|
463
471
|
readonly content: string;
|
|
464
472
|
readonly metadata?: Record<string, unknown>;
|
|
465
473
|
readonly addOptions?: { readonly skipIndexing?: boolean };
|
|
@@ -473,6 +481,19 @@ export type PersistAddArgs = {
|
|
|
473
481
|
readonly createdAtMs?: number;
|
|
474
482
|
};
|
|
475
483
|
|
|
484
|
+
export type PersistReserveArgs = {
|
|
485
|
+
readonly op: "reserve";
|
|
486
|
+
readonly conversationId: string;
|
|
487
|
+
readonly role: MessageRole;
|
|
488
|
+
readonly metadata?: Record<string, unknown>;
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
export type PersistUpdateContentArgs = {
|
|
492
|
+
readonly op: "updateContent";
|
|
493
|
+
readonly messageId: string;
|
|
494
|
+
readonly content: string;
|
|
495
|
+
};
|
|
496
|
+
|
|
476
497
|
export type PersistUpdateArgs = {
|
|
477
498
|
readonly op: "update";
|
|
478
499
|
readonly messageId: string;
|
|
@@ -486,6 +507,8 @@ export type PersistDeleteArgs = {
|
|
|
486
507
|
|
|
487
508
|
export type PersistArgs =
|
|
488
509
|
| PersistAddArgs
|
|
510
|
+
| PersistReserveArgs
|
|
511
|
+
| PersistUpdateContentArgs
|
|
489
512
|
| PersistUpdateArgs
|
|
490
513
|
| PersistDeleteArgs;
|
|
491
514
|
|
|
@@ -506,6 +529,25 @@ export type PersistAddResult = {
|
|
|
506
529
|
};
|
|
507
530
|
};
|
|
508
531
|
|
|
532
|
+
/**
|
|
533
|
+
* Result row returned by a `reserve` op — same row shape as `add` but with
|
|
534
|
+
* empty `content` (`"[]"`) and tagged distinctly so middleware can branch
|
|
535
|
+
* on intent.
|
|
536
|
+
*/
|
|
537
|
+
export type PersistReserveResult = {
|
|
538
|
+
readonly op: "reserve";
|
|
539
|
+
readonly message: {
|
|
540
|
+
readonly id: string;
|
|
541
|
+
readonly conversationId: string;
|
|
542
|
+
readonly role: string;
|
|
543
|
+
readonly content: string;
|
|
544
|
+
readonly createdAt: number;
|
|
545
|
+
readonly metadata?: string;
|
|
546
|
+
};
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
export type PersistUpdateContentResult = { readonly op: "updateContent" };
|
|
550
|
+
|
|
509
551
|
export type PersistUpdateResult = { readonly op: "update" };
|
|
510
552
|
|
|
511
553
|
/** IDs of segments/summaries the caller must remove from Qdrant. */
|
|
@@ -517,6 +559,8 @@ export type PersistDeleteResult = {
|
|
|
517
559
|
|
|
518
560
|
export type PersistResult =
|
|
519
561
|
| PersistAddResult
|
|
562
|
+
| PersistReserveResult
|
|
563
|
+
| PersistUpdateContentResult
|
|
520
564
|
| PersistUpdateResult
|
|
521
565
|
| PersistDeleteResult;
|
|
522
566
|
|
|
@@ -163,6 +163,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
163
163
|
addMessageCalls.push({ conversationId, role, content, metadata, opts });
|
|
164
164
|
return { id: `msg-${addMessageCalls.length}` };
|
|
165
165
|
},
|
|
166
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
166
167
|
}));
|
|
167
168
|
|
|
168
169
|
// emitNotificationSignal mock
|
|
@@ -58,7 +58,7 @@ mock.module("../../config/loader.js", () => ({
|
|
|
58
58
|
setNestedValue: () => {},
|
|
59
59
|
}));
|
|
60
60
|
|
|
61
|
-
const { buildSystemPrompt,
|
|
61
|
+
const { buildSystemPrompt, maybeReseedBootstrap } =
|
|
62
62
|
await import("../system-prompt.js");
|
|
63
63
|
|
|
64
64
|
describe("buildSystemPrompt — tool routing guidance", () => {
|
|
@@ -73,12 +73,12 @@ describe("buildSystemPrompt — tool routing guidance", () => {
|
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
describe("
|
|
76
|
+
describe("maybeReseedBootstrap — content-automation template", () => {
|
|
77
77
|
const templatesDir = join(import.meta.dirname!, "..", "templates");
|
|
78
78
|
|
|
79
79
|
beforeEach(() => {
|
|
80
80
|
mkdirSync(TEST_DIR, { recursive: true });
|
|
81
|
-
// Seed the workspace with the generic BOOTSTRAP.md so the
|
|
81
|
+
// Seed the workspace with the generic BOOTSTRAP.md so the bootstrap
|
|
82
82
|
// reseed detects it as an unmodified template and overwrites it.
|
|
83
83
|
copyFileSync(
|
|
84
84
|
join(templatesDir, "BOOTSTRAP.md"),
|
|
@@ -87,7 +87,7 @@ describe("maybeReseedBootstrapForCohort — content-automation template", () =>
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
function reseedAndRead(): string {
|
|
90
|
-
|
|
90
|
+
maybeReseedBootstrap("BOOTSTRAP-CONTENT-AUTOMATION.md");
|
|
91
91
|
return readFileSync(join(TEST_DIR, "BOOTSTRAP.md"), "utf-8");
|
|
92
92
|
}
|
|
93
93
|
|