@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
|
@@ -75,7 +75,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
75
75
|
{
|
|
76
76
|
type: "vellum",
|
|
77
77
|
address: "vellum-principal-abc",
|
|
78
|
-
externalUserId: "vellum-principal-abc",
|
|
79
78
|
},
|
|
80
79
|
],
|
|
81
80
|
});
|
|
@@ -91,7 +90,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
91
90
|
{
|
|
92
91
|
type: "slack",
|
|
93
92
|
address: "u123456",
|
|
94
|
-
externalUserId: "U123456",
|
|
95
93
|
externalChatId: "D987654",
|
|
96
94
|
},
|
|
97
95
|
],
|
|
@@ -109,7 +107,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
109
107
|
{
|
|
110
108
|
type: "slack",
|
|
111
109
|
address: "ualice",
|
|
112
|
-
externalUserId: "UALICE",
|
|
113
110
|
externalChatId: "DALICE",
|
|
114
111
|
},
|
|
115
112
|
],
|
|
@@ -125,7 +122,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
125
122
|
{
|
|
126
123
|
type: "slack",
|
|
127
124
|
address: "ubob1",
|
|
128
|
-
externalUserId: "UBOB1",
|
|
129
125
|
externalChatId: "DBOB1",
|
|
130
126
|
},
|
|
131
127
|
],
|
|
@@ -137,7 +133,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
137
133
|
{
|
|
138
134
|
type: "slack",
|
|
139
135
|
address: "ubob2",
|
|
140
|
-
externalUserId: "UBOB2",
|
|
141
136
|
externalChatId: "DBOB2",
|
|
142
137
|
},
|
|
143
138
|
],
|
|
@@ -164,7 +159,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
164
159
|
{
|
|
165
160
|
type: "phone",
|
|
166
161
|
address: "+15550000",
|
|
167
|
-
externalUserId: "+15550000",
|
|
168
162
|
externalChatId: "+15550000",
|
|
169
163
|
},
|
|
170
164
|
],
|
|
@@ -240,6 +240,35 @@ describe("contact_search tool", () => {
|
|
|
240
240
|
});
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
+
// ── search_contacts route (HTTP/IPC compat shim) ─────────────────────
|
|
244
|
+
|
|
245
|
+
describe("search_contacts route", () => {
|
|
246
|
+
beforeEach(clearContacts);
|
|
247
|
+
|
|
248
|
+
test("includes externalUserId (= address) on channels for older clients", () => {
|
|
249
|
+
const seeded = upsertFixture({
|
|
250
|
+
display_name: "Dana",
|
|
251
|
+
channels: [{ type: "slack", address: "U12345ABC" }],
|
|
252
|
+
});
|
|
253
|
+
const seededAddress = seeded.channels[0]!.address;
|
|
254
|
+
|
|
255
|
+
const searchRoute = ROUTES.find(
|
|
256
|
+
(r) => r.operationId === "search_contacts",
|
|
257
|
+
)!;
|
|
258
|
+
const contacts = searchRoute.handler({
|
|
259
|
+
body: { channelAddress: seededAddress },
|
|
260
|
+
}) as unknown as Array<{
|
|
261
|
+
channels: Array<{ address: string; externalUserId?: string }>;
|
|
262
|
+
}>;
|
|
263
|
+
|
|
264
|
+
expect(contacts.length).toBeGreaterThanOrEqual(1);
|
|
265
|
+
const channel = contacts[0]!.channels[0]!;
|
|
266
|
+
// The route re-derives the compat field from address, so SDK/macOS
|
|
267
|
+
// clients that read externalUserId keep working.
|
|
268
|
+
expect(channel.externalUserId).toBe(channel.address);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
243
272
|
// ── contact_merge ───────────────────────────────────────────────────
|
|
244
273
|
|
|
245
274
|
describe("contact_merge tool", () => {
|
|
@@ -1748,6 +1748,64 @@ describe("session-agent-loop", () => {
|
|
|
1748
1748
|
// THEN the queue is drained with the loop-complete reason
|
|
1749
1749
|
expect(drainReason).toBe("loop_complete");
|
|
1750
1750
|
});
|
|
1751
|
+
|
|
1752
|
+
test("abort watchdog drives a wedged turn to its finally", async () => {
|
|
1753
|
+
// GIVEN a provider whose call wedges: it acknowledges the user cancel
|
|
1754
|
+
// (aborts the signal) but its promise never settles and never observes
|
|
1755
|
+
// the signal — the exact condition that latched `processing` true.
|
|
1756
|
+
const events: ServerMessage[] = [];
|
|
1757
|
+
const abortController = new AbortController();
|
|
1758
|
+
let drainReason: string | undefined;
|
|
1759
|
+
// The provider's call wedges on this promise. It settles only on test
|
|
1760
|
+
// teardown so the abandoned `run()` can unwind cleanly instead of leaking
|
|
1761
|
+
// background work (e.g. partial-persist debounce timers) into later tests.
|
|
1762
|
+
let releaseHang: (reason: unknown) => void = () => {};
|
|
1763
|
+
const hang = new Promise<never>((_, reject) => {
|
|
1764
|
+
releaseHang = reject;
|
|
1765
|
+
});
|
|
1766
|
+
const provider: Provider = {
|
|
1767
|
+
name: "mock-provider",
|
|
1768
|
+
sendMessage(_messages, _options) {
|
|
1769
|
+
abortController.abort();
|
|
1770
|
+
// Never observes the signal — the exact condition that latched
|
|
1771
|
+
// `processing` true before the watchdog existed.
|
|
1772
|
+
return hang;
|
|
1773
|
+
},
|
|
1774
|
+
};
|
|
1775
|
+
const ctx = makeCtx({
|
|
1776
|
+
loopProvider: provider,
|
|
1777
|
+
abortController,
|
|
1778
|
+
// Fire the watchdog quickly instead of the ~45s production default.
|
|
1779
|
+
abortWatchdogMs: 30,
|
|
1780
|
+
drainQueue: (reason: string) => {
|
|
1781
|
+
drainReason = reason;
|
|
1782
|
+
},
|
|
1783
|
+
} as unknown as Partial<Conversation>);
|
|
1784
|
+
|
|
1785
|
+
try {
|
|
1786
|
+
// WHEN the orchestrator runs the turn
|
|
1787
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", (msg) => events.push(msg));
|
|
1788
|
+
|
|
1789
|
+
// THEN the watchdog forces the turn to its finally: processing clears,
|
|
1790
|
+
// the abort controller is torn down, the queue drains, and the user
|
|
1791
|
+
// sees a cancellation (not an error).
|
|
1792
|
+
expect(ctx.isProcessing()).toBe(false);
|
|
1793
|
+
expect(ctx.abortController).toBeNull();
|
|
1794
|
+
expect(drainReason).toBe("loop_complete");
|
|
1795
|
+
expect(
|
|
1796
|
+
events.find((e) => e.type === "generation_cancelled"),
|
|
1797
|
+
).toBeDefined();
|
|
1798
|
+
expect(
|
|
1799
|
+
events.find((e) => e.type === "conversation_error"),
|
|
1800
|
+
).toBeUndefined();
|
|
1801
|
+
} finally {
|
|
1802
|
+
// Let the abandoned run() reject and unwind, then flush microtasks.
|
|
1803
|
+
releaseHang(
|
|
1804
|
+
new DOMException("The operation was aborted", "AbortError"),
|
|
1805
|
+
);
|
|
1806
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1807
|
+
}
|
|
1808
|
+
});
|
|
1751
1809
|
});
|
|
1752
1810
|
|
|
1753
1811
|
describe("stale pending surface cleanup", () => {
|
|
@@ -674,15 +674,13 @@ describe("loadFromDb metadata injection rehydration", () => {
|
|
|
674
674
|
});
|
|
675
675
|
|
|
676
676
|
test("internal-channel trusted_contact view still rehydrates memoryV2StaticBlock", async () => {
|
|
677
|
-
//
|
|
678
|
-
//
|
|
679
|
-
//
|
|
680
|
-
// arriving over the internal `"vellum"` channel
|
|
681
|
-
//
|
|
682
|
-
//
|
|
683
|
-
//
|
|
684
|
-
// rehydrate gate must match so a daemon-restart reload of the same
|
|
685
|
-
// conversation produces an identical prefix.
|
|
677
|
+
// Rehydration keys on `sourceChannel`, not `trustClass`: injection uses
|
|
678
|
+
// `shouldExposePersonalMemory`, which exposes personal memory whenever
|
|
679
|
+
// `sourceChannel === "vellum"` regardless of actor trust class. So a
|
|
680
|
+
// trusted_contact view arriving over the internal `"vellum"` channel
|
|
681
|
+
// rehydrates `memoryV2StaticBlock`. The rehydrate gate must match
|
|
682
|
+
// injection so a daemon-restart reload of the same conversation produces
|
|
683
|
+
// an identical prefix.
|
|
686
684
|
mockConversation = defaultConv();
|
|
687
685
|
mockDbMessages = [
|
|
688
686
|
{
|
|
@@ -552,3 +552,104 @@ describe("loadFromDb history repair", () => {
|
|
|
552
552
|
]);
|
|
553
553
|
});
|
|
554
554
|
});
|
|
555
|
+
|
|
556
|
+
describe("loadFromDb turn-count rehydration", () => {
|
|
557
|
+
beforeEach(() => {
|
|
558
|
+
nextMockMessageId = 1;
|
|
559
|
+
mockConversation = {
|
|
560
|
+
id: "conv-1",
|
|
561
|
+
contextSummary: null,
|
|
562
|
+
contextCompactedMessageCount: 0,
|
|
563
|
+
totalInputTokens: 0,
|
|
564
|
+
totalOutputTokens: 0,
|
|
565
|
+
totalEstimatedCost: 0,
|
|
566
|
+
};
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
const userText = (text: string) => ({
|
|
570
|
+
role: "user",
|
|
571
|
+
content: JSON.stringify([{ type: "text", text }]),
|
|
572
|
+
});
|
|
573
|
+
const assistantText = (text: string) => ({
|
|
574
|
+
role: "assistant",
|
|
575
|
+
content: JSON.stringify([{ type: "text", text }]),
|
|
576
|
+
});
|
|
577
|
+
const assistantToolUse = (id: string) => ({
|
|
578
|
+
role: "assistant",
|
|
579
|
+
content: JSON.stringify([
|
|
580
|
+
{ type: "tool_use", id, name: "bash", input: { cmd: "ls" } },
|
|
581
|
+
]),
|
|
582
|
+
});
|
|
583
|
+
const toolResult = (id: string) => ({
|
|
584
|
+
role: "user",
|
|
585
|
+
content: JSON.stringify([
|
|
586
|
+
{ type: "tool_result", tool_use_id: id, content: "ok" },
|
|
587
|
+
]),
|
|
588
|
+
});
|
|
589
|
+
const withIds = (msgs: Array<{ role: string; content: string }>) =>
|
|
590
|
+
msgs.map((m, i) => ({ id: `m${i}`, ...m }));
|
|
591
|
+
|
|
592
|
+
test("restores turnCount from persisted history rather than resetting to 0", async () => {
|
|
593
|
+
// Three completed human turns persisted before this conversation object
|
|
594
|
+
// was (re)created — e.g. after an idle eviction or daemon restart.
|
|
595
|
+
mockDbMessages = withIds([
|
|
596
|
+
userText("Hello"),
|
|
597
|
+
assistantText("Hi"),
|
|
598
|
+
userText("How are you?"),
|
|
599
|
+
assistantText("Good"),
|
|
600
|
+
userText("Bye"),
|
|
601
|
+
assistantText("Later"),
|
|
602
|
+
]);
|
|
603
|
+
|
|
604
|
+
const conversation = makeConversation();
|
|
605
|
+
// Fresh object starts at 0 (the bug: it would stay 0 after reload).
|
|
606
|
+
expect(conversation.turnCount).toBe(0);
|
|
607
|
+
|
|
608
|
+
await conversation.loadFromDb();
|
|
609
|
+
|
|
610
|
+
expect(conversation.turnCount).toBe(3);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test("counts a multi-iteration tool-use turn as a single turn", async () => {
|
|
614
|
+
// One real user message; the tool_result user messages are continuations
|
|
615
|
+
// within the same turn, not new turns.
|
|
616
|
+
mockDbMessages = withIds([
|
|
617
|
+
userText("convert the voice memo"),
|
|
618
|
+
assistantToolUse("tu_1"),
|
|
619
|
+
toolResult("tu_1"),
|
|
620
|
+
assistantToolUse("tu_2"),
|
|
621
|
+
toolResult("tu_2"),
|
|
622
|
+
assistantText("done"),
|
|
623
|
+
]);
|
|
624
|
+
|
|
625
|
+
const conversation = makeConversation();
|
|
626
|
+
await conversation.loadFromDb();
|
|
627
|
+
|
|
628
|
+
expect(conversation.turnCount).toBe(1);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test("counts only real user turns when tool iterations are interleaved", async () => {
|
|
632
|
+
mockDbMessages = withIds([
|
|
633
|
+
userText("q1"),
|
|
634
|
+
assistantToolUse("tu_1"),
|
|
635
|
+
toolResult("tu_1"),
|
|
636
|
+
assistantText("a1"),
|
|
637
|
+
userText("q2"),
|
|
638
|
+
assistantText("a2"),
|
|
639
|
+
]);
|
|
640
|
+
|
|
641
|
+
const conversation = makeConversation();
|
|
642
|
+
await conversation.loadFromDb();
|
|
643
|
+
|
|
644
|
+
expect(conversation.turnCount).toBe(2);
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
test("empty history yields turnCount 0", async () => {
|
|
648
|
+
mockDbMessages = [];
|
|
649
|
+
|
|
650
|
+
const conversation = makeConversation();
|
|
651
|
+
await conversation.loadFromDb();
|
|
652
|
+
|
|
653
|
+
expect(conversation.turnCount).toBe(0);
|
|
654
|
+
});
|
|
655
|
+
});
|
|
@@ -222,13 +222,16 @@ describe("handleSendMessage canonical guardian reply interception", () => {
|
|
|
222
222
|
const routerCall = (routeGuardianReplyMock as any).mock
|
|
223
223
|
.calls[0][0] as Record<string, unknown>;
|
|
224
224
|
expect(routerCall.messageText).toBe("05BECB approve");
|
|
225
|
-
expect(routerCall.
|
|
225
|
+
expect(routerCall.pendingScope).toEqual({
|
|
226
|
+
mode: "scoped",
|
|
227
|
+
requestIds: ["access-req-1"],
|
|
228
|
+
});
|
|
226
229
|
expect(addMessageMock).toHaveBeenCalledTimes(2);
|
|
227
230
|
expect(persistUserMessage).toHaveBeenCalledTimes(0);
|
|
228
231
|
expect(runAgentLoop).toHaveBeenCalledTimes(0);
|
|
229
232
|
});
|
|
230
233
|
|
|
231
|
-
test("passes
|
|
234
|
+
test("passes a blocked scope when no canonical hints are found", async () => {
|
|
232
235
|
listPendingByDestinationMock.mockReturnValue([]);
|
|
233
236
|
listCanonicalMock.mockReturnValue([]);
|
|
234
237
|
routeGuardianReplyMock.mockResolvedValue({
|
|
@@ -301,7 +304,7 @@ describe("handleSendMessage canonical guardian reply interception", () => {
|
|
|
301
304
|
expect(routeGuardianReplyMock).toHaveBeenCalledTimes(1);
|
|
302
305
|
const routerCall = (routeGuardianReplyMock as any).mock
|
|
303
306
|
.calls[0][0] as Record<string, unknown>;
|
|
304
|
-
expect(routerCall.
|
|
307
|
+
expect(routerCall.pendingScope).toEqual({ mode: "blocked" });
|
|
305
308
|
expect(persistUserMessage).toHaveBeenCalledTimes(1);
|
|
306
309
|
expect(runAgentLoop).toHaveBeenCalledTimes(1);
|
|
307
310
|
});
|
|
@@ -384,15 +387,15 @@ describe("handleSendMessage canonical guardian reply interception", () => {
|
|
|
384
387
|
expect(routeGuardianReplyMock).toHaveBeenCalledTimes(1);
|
|
385
388
|
const routerCall = (routeGuardianReplyMock as any).mock
|
|
386
389
|
.calls[0][0] as Record<string, unknown>;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
expect(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
).toBe(false);
|
|
390
|
+
const scope = routerCall.pendingScope as {
|
|
391
|
+
mode: string;
|
|
392
|
+
requestIds: string[];
|
|
393
|
+
};
|
|
394
|
+
expect(scope).toEqual({
|
|
395
|
+
mode: "scoped",
|
|
396
|
+
requestIds: ["tool-approval-live", "access-req-1"],
|
|
397
|
+
});
|
|
398
|
+
expect(scope.requestIds.includes("tool-approval-stale")).toBe(false);
|
|
396
399
|
});
|
|
397
400
|
|
|
398
401
|
test("text fallback: request-code approve routes through guardian reply router", async () => {
|
|
@@ -10,9 +10,11 @@ mock.module("../util/logger.js", () => ({
|
|
|
10
10
|
}),
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
// Analytics consent is granted so recordActivationEvent writes rows.
|
|
14
|
+
let shareAnalytics = true;
|
|
15
|
+
|
|
16
|
+
mock.module("../platform/consent-cache.js", () => ({
|
|
17
|
+
getCachedShareAnalytics: () => shareAnalytics,
|
|
16
18
|
}));
|
|
17
19
|
|
|
18
20
|
let broadcastedMessages: ServerMessage[] = [];
|
|
@@ -117,6 +119,7 @@ async function showTaggedChoice(
|
|
|
117
119
|
|
|
118
120
|
describe("activation moment emission from ui_show surface commits", () => {
|
|
119
121
|
beforeEach(() => {
|
|
122
|
+
shareAnalytics = true;
|
|
120
123
|
broadcastedMessages = [];
|
|
121
124
|
resetTables();
|
|
122
125
|
});
|
|
@@ -69,10 +69,38 @@ import {
|
|
|
69
69
|
describe("conversation-title-service", () => {
|
|
70
70
|
beforeEach(() => {
|
|
71
71
|
mockRunBtwSidechain.mockClear();
|
|
72
|
+
mockRunBtwSidechain.mockImplementation(
|
|
73
|
+
async (_params: Record<string, unknown>) => ({
|
|
74
|
+
text: "Project kickoff",
|
|
75
|
+
hadTextDeltas: true,
|
|
76
|
+
response: {
|
|
77
|
+
content: [{ type: "text", text: "Project kickoff" }],
|
|
78
|
+
model: "test-model",
|
|
79
|
+
usage: { inputTokens: 10, outputTokens: 5 },
|
|
80
|
+
stopReason: "end_turn",
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
72
84
|
mockGetConversation.mockClear();
|
|
85
|
+
mockGetConversation.mockImplementation(
|
|
86
|
+
(_conversationId: string) =>
|
|
87
|
+
({
|
|
88
|
+
title: "Generating title...",
|
|
89
|
+
isAutoTitle: 1,
|
|
90
|
+
}) as {
|
|
91
|
+
title: string;
|
|
92
|
+
isAutoTitle: number;
|
|
93
|
+
},
|
|
94
|
+
);
|
|
73
95
|
mockGetMessages.mockClear();
|
|
96
|
+
mockGetMessages.mockImplementation(() => [
|
|
97
|
+
{ role: "user", content: "first message" },
|
|
98
|
+
{ role: "assistant", content: "first reply" },
|
|
99
|
+
{ role: "user", content: "follow-up" },
|
|
100
|
+
]);
|
|
74
101
|
mockUpdateConversationTitle.mockClear();
|
|
75
102
|
mockGetConfiguredProvider.mockClear();
|
|
103
|
+
mockGetConfiguredProvider.mockImplementation(async () => null);
|
|
76
104
|
mockPublishConversationTitleChanged.mockClear();
|
|
77
105
|
});
|
|
78
106
|
|
|
@@ -232,6 +260,30 @@ describe("conversation-title-service", () => {
|
|
|
232
260
|
);
|
|
233
261
|
});
|
|
234
262
|
|
|
263
|
+
test("fallback retry skips regeneration after a successful initial title", async () => {
|
|
264
|
+
mockGetConversation.mockReturnValueOnce({
|
|
265
|
+
title: "Project kickoff",
|
|
266
|
+
isAutoTitle: 1,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const provider = {
|
|
270
|
+
name: "test-provider",
|
|
271
|
+
sendMessage: mock(async () => {
|
|
272
|
+
throw new Error("should not call directly");
|
|
273
|
+
}),
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const result = await regenerateConversationTitle({
|
|
277
|
+
conversationId: "conv-1",
|
|
278
|
+
provider,
|
|
279
|
+
onlyIfReplaceable: true,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
expect(result).toEqual({ title: "Project kickoff", updated: false });
|
|
283
|
+
expect(mockRunBtwSidechain).not.toHaveBeenCalled();
|
|
284
|
+
expect(mockUpdateConversationTitle).not.toHaveBeenCalled();
|
|
285
|
+
});
|
|
286
|
+
|
|
235
287
|
test("rejects meta-failure outputs like 'Missing Context' and uses fallback", async () => {
|
|
236
288
|
mockRunBtwSidechain.mockImplementationOnce(async () => ({
|
|
237
289
|
text: "Missing Context",
|
|
@@ -482,6 +534,16 @@ describe("conversation-title-service", () => {
|
|
|
482
534
|
|
|
483
535
|
// Both calls went through — failure didn't break the chain
|
|
484
536
|
expect(mockRunBtwSidechain).toHaveBeenCalledTimes(2);
|
|
537
|
+
const firstUpdate = (
|
|
538
|
+
mockUpdateConversationTitle.mock.calls as unknown as Array<
|
|
539
|
+
[string, string, number?]
|
|
540
|
+
>
|
|
541
|
+
).find((c) => c[0] === "conv-1");
|
|
542
|
+
expect(firstUpdate).toEqual([
|
|
543
|
+
"conv-1",
|
|
544
|
+
"Untitled Conversation",
|
|
545
|
+
AUTO_TITLE_DETERMINISTIC,
|
|
546
|
+
]);
|
|
485
547
|
// Second conversation got a proper title
|
|
486
548
|
const secondUpdate = (
|
|
487
549
|
mockUpdateConversationTitle.mock.calls as unknown as string[][]
|
|
@@ -11,27 +11,33 @@
|
|
|
11
11
|
|
|
12
12
|
import { describe, expect, test } from "bun:test";
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { resolveCapabilities } from "../runtime/capabilities.js";
|
|
15
15
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
// Trust class categorization (foundational for lockdown decisions)
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
|
|
20
20
|
describe("trust class categorization for CES lockdown", () => {
|
|
21
|
-
test("guardian
|
|
22
|
-
expect(
|
|
21
|
+
test("guardian runs an unsandboxed shell", () => {
|
|
22
|
+
expect(resolveCapabilities("guardian").canRunUnsandboxedShell).toBe(true);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
test("trusted_contact is
|
|
26
|
-
expect(
|
|
25
|
+
test("trusted_contact shell is sandboxed", () => {
|
|
26
|
+
expect(resolveCapabilities("trusted_contact").canRunUnsandboxedShell).toBe(false);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
test("
|
|
30
|
-
expect(
|
|
29
|
+
test("unverified_contact shell is sandboxed", () => {
|
|
30
|
+
expect(resolveCapabilities("unverified_contact").canRunUnsandboxedShell).toBe(
|
|
31
|
+
false,
|
|
32
|
+
);
|
|
31
33
|
});
|
|
32
34
|
|
|
33
|
-
test("
|
|
34
|
-
expect(
|
|
35
|
+
test("unknown shell is sandboxed", () => {
|
|
36
|
+
expect(resolveCapabilities("unknown").canRunUnsandboxedShell).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("undefined shell is sandboxed", () => {
|
|
40
|
+
expect(resolveCapabilities(undefined).canRunUnsandboxedShell).toBe(false);
|
|
35
41
|
});
|
|
36
42
|
});
|
|
37
43
|
|
|
@@ -91,7 +97,7 @@ describe("VELLUM_UNTRUSTED_SHELL env flag", () => {
|
|
|
91
97
|
describe("CES shell lockdown activation", () => {
|
|
92
98
|
test("lockdown is active only when both flag is enabled AND actor is untrusted", () => {
|
|
93
99
|
// Simulates the condition used in shell.ts:
|
|
94
|
-
// const shellLockdownActive = isCesShellLockdownEnabled(config) &&
|
|
100
|
+
// const shellLockdownActive = isCesShellLockdownEnabled(config) && !resolveCapabilities(context.trustClass).canRunUnsandboxedShell;
|
|
95
101
|
const cases: Array<{
|
|
96
102
|
flagEnabled: boolean;
|
|
97
103
|
trustClass: "guardian" | "trusted_contact" | "unknown";
|
|
@@ -106,7 +112,8 @@ describe("CES shell lockdown activation", () => {
|
|
|
106
112
|
];
|
|
107
113
|
|
|
108
114
|
for (const { flagEnabled, trustClass, expected } of cases) {
|
|
109
|
-
const active =
|
|
115
|
+
const active =
|
|
116
|
+
flagEnabled && !resolveCapabilities(trustClass).canRunUnsandboxedShell;
|
|
110
117
|
expect(active).toBe(expected);
|
|
111
118
|
}
|
|
112
119
|
});
|
|
@@ -158,6 +158,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
158
158
|
"credential-execution/prompted-credential.ts", // shared prompt-action persistence (stores secret via setSecureKeyAsync)
|
|
159
159
|
"tools/credentials/broker.ts", // brokered credential access
|
|
160
160
|
"tools/network/web-search.ts", // web search API key lookup
|
|
161
|
+
"tools/network/web-fetch.ts", // web fetch provider (Firecrawl) API key lookup
|
|
161
162
|
"daemon/handlers/config-telegram.ts", // Telegram bot token management
|
|
162
163
|
"daemon/handlers/config-vercel.ts", // Vercel API token management
|
|
163
164
|
"runtime/routes/integrations/twilio.ts", // Twilio credential management (HTTP control-plane)
|
|
@@ -237,6 +238,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
237
238
|
"tools/credential-execution/make-authenticated-request.ts", // resolves the CES RPC client via getCesClient
|
|
238
239
|
"tools/credential-execution/manage-secure-command-tool.ts", // resolves the CES RPC client via getCesClient
|
|
239
240
|
"tools/executor.ts", // CES approval bridge resolves the CES RPC client via getCesClient
|
|
241
|
+
"tools/network/web-fetch.ts", // Firecrawl /scrape BYOK fetch provider API key lookup (firecrawl provider key)
|
|
240
242
|
]);
|
|
241
243
|
|
|
242
244
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -108,6 +108,18 @@ describe("classifyDiskPressureTurnPolicy", () => {
|
|
|
108
108
|
},
|
|
109
109
|
expected: { action: "block", reason: "trusted-contact" },
|
|
110
110
|
},
|
|
111
|
+
{
|
|
112
|
+
name: "unverified contact is blocked",
|
|
113
|
+
status: status(),
|
|
114
|
+
metadata: {
|
|
115
|
+
...guardianTurn,
|
|
116
|
+
trustContext: {
|
|
117
|
+
sourceChannel: "telegram",
|
|
118
|
+
trustClass: "unverified_contact",
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
expected: { action: "block", reason: "trusted-contact" },
|
|
122
|
+
},
|
|
111
123
|
{
|
|
112
124
|
name: "non-guardian contact is blocked",
|
|
113
125
|
status: status(),
|
|
@@ -6,6 +6,8 @@ const GIB = 1024 * MIB;
|
|
|
6
6
|
let existingPaths = new Set<string>();
|
|
7
7
|
const workspaceDir = "/workspace";
|
|
8
8
|
let minikubeStorageSize: string | undefined;
|
|
9
|
+
let isContainerized = false;
|
|
10
|
+
let isPlatform = false;
|
|
9
11
|
let statfsResult = {
|
|
10
12
|
bsize: 4096,
|
|
11
13
|
blocks: 0,
|
|
@@ -34,6 +36,8 @@ mock.module("node:child_process", () => ({
|
|
|
34
36
|
|
|
35
37
|
mock.module("../config/env-registry.js", () => ({
|
|
36
38
|
getMinikubeStorageSize: () => minikubeStorageSize,
|
|
39
|
+
getIsContainerized: () => isContainerized,
|
|
40
|
+
getIsPlatform: () => isPlatform,
|
|
37
41
|
}));
|
|
38
42
|
|
|
39
43
|
mock.module("../util/platform.js", () => ({
|
|
@@ -55,6 +59,8 @@ describe("disk usage sampler", () => {
|
|
|
55
59
|
beforeEach(() => {
|
|
56
60
|
existingPaths = new Set([workspaceDir]);
|
|
57
61
|
minikubeStorageSize = undefined;
|
|
62
|
+
isContainerized = false;
|
|
63
|
+
isPlatform = false;
|
|
58
64
|
statfsResult = statfsFor(100 * MIB, 25 * MIB);
|
|
59
65
|
spawnResult = { status: 0, stdout: "" };
|
|
60
66
|
spawnCalls = [];
|
|
@@ -119,6 +125,65 @@ describe("disk usage sampler", () => {
|
|
|
119
125
|
]);
|
|
120
126
|
});
|
|
121
127
|
|
|
128
|
+
test("measures workspace du usage on a local Docker hatch", () => {
|
|
129
|
+
isContainerized = true;
|
|
130
|
+
isPlatform = false;
|
|
131
|
+
statfsResult = statfsFor(100 * GIB, 40 * GIB);
|
|
132
|
+
spawnResult = {
|
|
133
|
+
status: 0,
|
|
134
|
+
stdout: `${2 * GIB}\t/workspace\n`,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const usage = getDiskUsageInfo();
|
|
138
|
+
|
|
139
|
+
// Used reflects only the workspace (du), free reflects the host headroom
|
|
140
|
+
// the volume can grow into, and total is their sum.
|
|
141
|
+
expect(usage).toEqual({
|
|
142
|
+
path: "/workspace",
|
|
143
|
+
totalMb: 2048 + 40960,
|
|
144
|
+
usedMb: 2048,
|
|
145
|
+
freeMb: 40960,
|
|
146
|
+
});
|
|
147
|
+
expect(spawnCalls).toEqual([
|
|
148
|
+
{ command: "du", args: ["-sb", "/workspace"] },
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("does not use du for platform-managed containerized instances", () => {
|
|
153
|
+
isContainerized = true;
|
|
154
|
+
isPlatform = true;
|
|
155
|
+
statfsResult = statfsFor(10 * GIB, 6 * GIB);
|
|
156
|
+
|
|
157
|
+
const usage = getDiskUsageInfo();
|
|
158
|
+
|
|
159
|
+
expect(usage).toEqual({
|
|
160
|
+
path: "/workspace",
|
|
161
|
+
totalMb: 10240,
|
|
162
|
+
usedMb: 4096,
|
|
163
|
+
freeMb: 6144,
|
|
164
|
+
});
|
|
165
|
+
expect(spawnCalls).toHaveLength(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("falls back to statfs when du fails on a local Docker hatch", () => {
|
|
169
|
+
isContainerized = true;
|
|
170
|
+
isPlatform = false;
|
|
171
|
+
statfsResult = statfsFor(100 * GIB, 40 * GIB);
|
|
172
|
+
spawnResult = { status: 1, stdout: "" };
|
|
173
|
+
|
|
174
|
+
const usage = getDiskUsageInfo();
|
|
175
|
+
|
|
176
|
+
expect(usage).toEqual({
|
|
177
|
+
path: "/workspace",
|
|
178
|
+
totalMb: 102400,
|
|
179
|
+
usedMb: 61440,
|
|
180
|
+
freeMb: 40960,
|
|
181
|
+
});
|
|
182
|
+
expect(spawnCalls).toEqual([
|
|
183
|
+
{ command: "du", args: ["-sb", "/workspace"] },
|
|
184
|
+
]);
|
|
185
|
+
});
|
|
186
|
+
|
|
122
187
|
test("returns null for malformed Kubernetes memory strings", () => {
|
|
123
188
|
expect(parseK8sMemoryBytes("")).toBeNull();
|
|
124
189
|
expect(parseK8sMemoryBytes("abc")).toBeNull();
|