@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
package/package.json
CHANGED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
buildAccessRequestCardView,
|
|
5
|
+
parseAccessRequestPayload,
|
|
6
|
+
} from "../notifications/access-request-copy.js";
|
|
7
|
+
|
|
8
|
+
function view(raw: Record<string, unknown>) {
|
|
9
|
+
return buildAccessRequestCardView(parseAccessRequestPayload(raw));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const TAB = String.fromCharCode(9);
|
|
13
|
+
|
|
14
|
+
describe("buildAccessRequestCardView", () => {
|
|
15
|
+
test("prefers actorDisplayName, falls back to senderIdentifier then 'Someone'", () => {
|
|
16
|
+
expect(
|
|
17
|
+
view({ actorDisplayName: "Alice", senderIdentifier: "U999" }).displayName,
|
|
18
|
+
).toBe("Alice");
|
|
19
|
+
expect(view({ senderIdentifier: "U999" }).displayName).toBe("U999");
|
|
20
|
+
expect(view({}).displayName).toBe("Someone");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("sanitizes identity fields (strips control characters)", () => {
|
|
24
|
+
const v = view({
|
|
25
|
+
actorDisplayName: `Al${TAB}ice`,
|
|
26
|
+
actorUsername: `a${TAB}lice`,
|
|
27
|
+
actorExternalId: `U9${TAB}99`,
|
|
28
|
+
});
|
|
29
|
+
expect(v.displayName).toBe("Al ice");
|
|
30
|
+
expect(v.username).toBe("a lice");
|
|
31
|
+
expect(v.externalId).toBe("U9 99");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("username and externalId are undefined when absent", () => {
|
|
35
|
+
const v = view({ actorDisplayName: "Alice" });
|
|
36
|
+
expect(v.username).toBeUndefined();
|
|
37
|
+
expect(v.externalId).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("detects Slack DM conversations", () => {
|
|
41
|
+
expect(
|
|
42
|
+
view({ sourceChannel: "slack", conversationExternalId: "D01XYZ" })
|
|
43
|
+
.isSlackDm,
|
|
44
|
+
).toBe(true);
|
|
45
|
+
expect(
|
|
46
|
+
view({ sourceChannel: "slack", conversationExternalId: "C01ABC" })
|
|
47
|
+
.isSlackDm,
|
|
48
|
+
).toBe(false);
|
|
49
|
+
expect(
|
|
50
|
+
view({ sourceChannel: "telegram", conversationExternalId: "D01XYZ" })
|
|
51
|
+
.isSlackDm,
|
|
52
|
+
).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("builds a Slack permalink only with slack source + conversation + ts", () => {
|
|
56
|
+
expect(
|
|
57
|
+
view({
|
|
58
|
+
sourceChannel: "slack",
|
|
59
|
+
conversationExternalId: "C01ABC",
|
|
60
|
+
messageTs: "1700000000.000100",
|
|
61
|
+
}).messagePermalink,
|
|
62
|
+
).toBe("https://slack.com/archives/C01ABC/p1700000000000100");
|
|
63
|
+
expect(
|
|
64
|
+
view({ sourceChannel: "slack", conversationExternalId: "C01ABC" })
|
|
65
|
+
.messagePermalink,
|
|
66
|
+
).toBeUndefined();
|
|
67
|
+
expect(
|
|
68
|
+
view({
|
|
69
|
+
sourceChannel: "telegram",
|
|
70
|
+
conversationExternalId: "C01ABC",
|
|
71
|
+
messageTs: "1.2",
|
|
72
|
+
}).messagePermalink,
|
|
73
|
+
).toBeUndefined();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("sanitizes message preview and yields undefined when blank after sanitizing", () => {
|
|
77
|
+
expect(view({ messagePreview: " hello " }).messagePreview).toBe("hello");
|
|
78
|
+
// Blank / control-character-only previews sanitize to empty → undefined
|
|
79
|
+
// (no empty quote block is rendered downstream).
|
|
80
|
+
expect(view({ messagePreview: "" }).messagePreview).toBeUndefined();
|
|
81
|
+
expect(view({ messagePreview: " " }).messagePreview).toBeUndefined();
|
|
82
|
+
expect(view({}).messagePreview).toBeUndefined();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("collects trust/security warnings", () => {
|
|
86
|
+
const v = view({
|
|
87
|
+
isStranger: true,
|
|
88
|
+
isRestricted: true,
|
|
89
|
+
previousMemberStatus: "revoked",
|
|
90
|
+
});
|
|
91
|
+
expect(v.warnings).toEqual([
|
|
92
|
+
"This user was previously revoked.",
|
|
93
|
+
"External Slack user (not in this workspace).",
|
|
94
|
+
"Guest / restricted account.",
|
|
95
|
+
]);
|
|
96
|
+
expect(view({}).warnings).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
parseAccessRequestPayload,
|
|
6
|
-
} from "../notifications/access-request-copy.js";
|
|
3
|
+
import { parseAccessRequestPayload } from "../notifications/access-request-copy.js";
|
|
4
|
+
import { buildAccessRequestSeedContentBlocks } from "../notifications/approval-card-data.js";
|
|
7
5
|
|
|
8
6
|
describe("buildAccessRequestSeedContentBlocks", () => {
|
|
9
7
|
const basePayload: Record<string, unknown> = {
|
|
@@ -43,7 +43,6 @@ const PHONE = "+15559871234";
|
|
|
43
43
|
function makeContact(
|
|
44
44
|
role: "guardian" | "contact" = "contact",
|
|
45
45
|
status: "unverified" | "active" = "unverified",
|
|
46
|
-
externalUserId: string | null = null,
|
|
47
46
|
): ContactWithChannels {
|
|
48
47
|
const channelId = "ch-test";
|
|
49
48
|
return {
|
|
@@ -64,7 +63,6 @@ function makeContact(
|
|
|
64
63
|
contactId: "contact-test",
|
|
65
64
|
type: "phone",
|
|
66
65
|
address: PHONE,
|
|
67
|
-
externalUserId,
|
|
68
66
|
externalChatId: null,
|
|
69
67
|
isPrimary: true,
|
|
70
68
|
status,
|
|
@@ -94,7 +92,7 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
94
92
|
|
|
95
93
|
test("finds unverified channel via address when externalUserId is null", () => {
|
|
96
94
|
// Simulate a contact registered by name-capture: address set, externalUserId null.
|
|
97
|
-
_byAddress = makeContact("contact", "unverified"
|
|
95
|
+
_byAddress = makeContact("contact", "unverified");
|
|
98
96
|
|
|
99
97
|
const result = resolveActorTrust({
|
|
100
98
|
assistantId: "asst-1",
|
|
@@ -106,12 +104,13 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
106
104
|
expect(result.memberRecord).not.toBeNull();
|
|
107
105
|
expect(result.memberRecord?.contact.displayName).toBe("Patrick Test");
|
|
108
106
|
expect(result.memberRecord?.channel.status).toBe("unverified");
|
|
109
|
-
// trustClass is '
|
|
110
|
-
|
|
107
|
+
// trustClass is 'unverified_contact' for a member whose channel is
|
|
108
|
+
// pending or unverified — known to the guardian but not yet verified.
|
|
109
|
+
expect(result.trustClass).toBe("unverified_contact");
|
|
111
110
|
});
|
|
112
111
|
|
|
113
112
|
test("address lookup is the sole member resolution path", () => {
|
|
114
|
-
_byAddress = makeContact("contact", "active"
|
|
113
|
+
_byAddress = makeContact("contact", "active");
|
|
115
114
|
|
|
116
115
|
const result = resolveActorTrust({
|
|
117
116
|
assistantId: "asst-1",
|
|
@@ -141,7 +140,7 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
141
140
|
test("address-found active channel elevates trust to trusted_contact", () => {
|
|
142
141
|
// An active channel found via address (e.g. after manual verify without externalUserId set)
|
|
143
142
|
// should still yield trusted_contact trust class.
|
|
144
|
-
_byAddress = makeContact("contact", "active"
|
|
143
|
+
_byAddress = makeContact("contact", "active");
|
|
145
144
|
|
|
146
145
|
const result = resolveActorTrust({
|
|
147
146
|
assistantId: "asst-1",
|
|
@@ -154,4 +153,57 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
154
153
|
expect(result.memberRecord?.channel.status).toBe("active");
|
|
155
154
|
expect(result.trustClass).toBe("trusted_contact");
|
|
156
155
|
});
|
|
156
|
+
|
|
157
|
+
test("pending-status member is classified as unverified_contact", () => {
|
|
158
|
+
// Mirrors the unverified branch but for `pending` status (e.g. a phone
|
|
159
|
+
// contact registered by name-capture awaiting the DTMF challenge).
|
|
160
|
+
const contact = makeContact("contact", "unverified");
|
|
161
|
+
// Override status to "pending" — makeContact only accepts unverified/active
|
|
162
|
+
contact.channels[0]!.status = "pending";
|
|
163
|
+
_byAddress = contact;
|
|
164
|
+
|
|
165
|
+
const result = resolveActorTrust({
|
|
166
|
+
assistantId: "asst-1",
|
|
167
|
+
sourceChannel: "phone",
|
|
168
|
+
conversationExternalId: PHONE,
|
|
169
|
+
actorExternalId: PHONE,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(result.memberRecord?.channel.status).toBe("pending");
|
|
173
|
+
expect(result.trustClass).toBe("unverified_contact");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("blocked-status member is classified as unknown (not unverified_contact)", () => {
|
|
177
|
+
// Hard-deny statuses (blocked, revoked) stay `unknown` — admission-layer
|
|
178
|
+
// re-checks channel.status and emits the hard-deny reasons.
|
|
179
|
+
const contact = makeContact("contact", "unverified");
|
|
180
|
+
contact.channels[0]!.status = "blocked";
|
|
181
|
+
_byAddress = contact;
|
|
182
|
+
|
|
183
|
+
const result = resolveActorTrust({
|
|
184
|
+
assistantId: "asst-1",
|
|
185
|
+
sourceChannel: "phone",
|
|
186
|
+
conversationExternalId: PHONE,
|
|
187
|
+
actorExternalId: PHONE,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(result.memberRecord?.channel.status).toBe("blocked");
|
|
191
|
+
expect(result.trustClass).toBe("unknown");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("revoked-status member is classified as unknown", () => {
|
|
195
|
+
const contact = makeContact("contact", "unverified");
|
|
196
|
+
contact.channels[0]!.status = "revoked";
|
|
197
|
+
_byAddress = contact;
|
|
198
|
+
|
|
199
|
+
const result = resolveActorTrust({
|
|
200
|
+
assistantId: "asst-1",
|
|
201
|
+
sourceChannel: "phone",
|
|
202
|
+
conversationExternalId: PHONE,
|
|
203
|
+
actorExternalId: PHONE,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
expect(result.memberRecord?.channel.status).toBe("revoked");
|
|
207
|
+
expect(result.trustClass).toBe("unknown");
|
|
208
|
+
});
|
|
157
209
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The compaction summarizer's input
|
|
2
|
+
* The compaction summarizer's input.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Both the budget gate and overflow recovery summarize the *injected* history
|
|
5
|
+
* (the genuine conversation as the agent saw it) so the summary call's prompt
|
|
6
|
+
* prefix matches the agent's warm prefix cache — a cache read rather than a
|
|
7
|
+
* fresh cache write. The agent loop hands the full injected history to
|
|
8
|
+
* compaction unconditionally; the post-compaction re-injection hook owns
|
|
9
|
+
* injection idempotency by stripping the tail's per-turn blocks before
|
|
10
|
+
* re-applying them.
|
|
10
11
|
*/
|
|
11
12
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
12
13
|
|
|
@@ -46,17 +47,17 @@ const testPostCompactPlugin = {
|
|
|
46
47
|
const CONVERSATION_ID = "compaction-strip-conversation";
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
|
-
* A runtime `<workspace>` injection block.
|
|
50
|
-
*
|
|
51
|
-
*
|
|
50
|
+
* A runtime `<workspace>` injection block. Its presence in the summarizer's
|
|
51
|
+
* input proves compaction received the injected history rather than a stripped
|
|
52
|
+
* copy.
|
|
52
53
|
*/
|
|
53
54
|
const WORKSPACE_INJECTION =
|
|
54
55
|
"<workspace>\nActive workspace: project-x\n</workspace>";
|
|
55
56
|
const TURN_BODY = "Hello there, this is the turn body.";
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
|
-
* A user turn carrying a real text block plus a runtime injection block
|
|
59
|
-
*
|
|
59
|
+
* A user turn carrying a real text block plus a runtime injection block, both
|
|
60
|
+
* of which ride into compaction's input as-is.
|
|
60
61
|
*/
|
|
61
62
|
const injectedUserMessage: Message = {
|
|
62
63
|
role: "user",
|
|
@@ -184,7 +185,7 @@ describe("AgentLoop compaction summarizer input", () => {
|
|
|
184
185
|
expect(events.some((e) => e.type === "history_stripped")).toBe(true);
|
|
185
186
|
});
|
|
186
187
|
|
|
187
|
-
test("overflow-driven compaction summarizes the
|
|
188
|
+
test("overflow-driven compaction summarizes the injected history", async () => {
|
|
188
189
|
// GIVEN a history whose user turn carries a runtime-injection block
|
|
189
190
|
const capture: CompactionInputCapture = { budget: null, overflow: null };
|
|
190
191
|
|
|
@@ -229,10 +230,10 @@ describe("AgentLoop compaction summarizer input", () => {
|
|
|
229
230
|
...installCapturingManager(capture),
|
|
230
231
|
});
|
|
231
232
|
|
|
232
|
-
// THEN overflow recovery received the history
|
|
233
|
+
// THEN overflow recovery received the injected history, injection intact
|
|
233
234
|
expect(capture.overflow).not.toBeNull();
|
|
234
|
-
expect(serialize(capture.overflow)).
|
|
235
|
-
// AND the real turn body
|
|
235
|
+
expect(serialize(capture.overflow)).toContain("<workspace>");
|
|
236
|
+
// AND the real turn body is present alongside the injection
|
|
236
237
|
expect(serialize(capture.overflow)).toContain(TURN_BODY);
|
|
237
238
|
// AND the budget summarizer was not invoked
|
|
238
239
|
expect(capture.budget).toBeNull();
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Verifies that the
|
|
3
|
-
* `SendMessageOptions.config` the loop emits. When
|
|
2
|
+
* Verifies that the memory-v3-live cache-anchor signal surfaces on every
|
|
3
|
+
* `SendMessageOptions.config` the loop emits. When v3-live is on, the latest
|
|
4
4
|
* user message carries a volatile `<memory>` block, so the loop sets
|
|
5
5
|
* `providerConfig.mutableLatestUserMessage` to tell the provider to anchor its
|
|
6
6
|
* long-TTL cache breakpoint on the most recent STABLE message instead.
|
|
7
7
|
*
|
|
8
|
-
* The loop reads
|
|
9
|
-
* signal is sourced from
|
|
10
|
-
* off the field is omitted (not `false`/`undefined`) so
|
|
11
|
-
* byte-identical to today.
|
|
8
|
+
* The loop reads `config.memory.v3.live` directly where it assembles
|
|
9
|
+
* `providerConfig`, so the signal is sourced from config rather than a run
|
|
10
|
+
* option. When v3-live is off the field is omitted (not `false`/`undefined`) so
|
|
11
|
+
* the wire stays byte-identical to today.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -19,11 +19,14 @@ mock.module("../util/logger.js", () => ({
|
|
|
19
19
|
getLogger: () => makeMockLogger(),
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
+
// AgentLoop reads the v3-live gate (`config.memory.v3.live`) via
|
|
23
|
+
// `isMemoryV3Live` to decide the cache-anchor signal; drive it per-test.
|
|
24
|
+
let memoryV3LiveSlot = false;
|
|
25
|
+
mock.module("../config/memory-v3-gate.js", () => ({
|
|
26
|
+
isMemoryV3Live: () => memoryV3LiveSlot,
|
|
27
|
+
}));
|
|
28
|
+
|
|
22
29
|
import { AgentLoop } from "../agent/loop.js";
|
|
23
|
-
import {
|
|
24
|
-
clearCachedOverrides,
|
|
25
|
-
setCachedOverrides,
|
|
26
|
-
} from "../config/feature-flag-cache.js";
|
|
27
30
|
import type {
|
|
28
31
|
Message,
|
|
29
32
|
Provider,
|
|
@@ -82,12 +85,12 @@ function makeRecordingProvider(responses: ProviderResponse[]): {
|
|
|
82
85
|
|
|
83
86
|
describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () => {
|
|
84
87
|
afterEach(() => {
|
|
85
|
-
|
|
88
|
+
memoryV3LiveSlot = false;
|
|
86
89
|
});
|
|
87
90
|
|
|
88
91
|
test("sets mutableLatestUserMessage on every LLM call when memory-v3-live is on (multi-turn)", async () => {
|
|
89
92
|
// GIVEN memory-v3-live is enabled
|
|
90
|
-
|
|
93
|
+
memoryV3LiveSlot = true;
|
|
91
94
|
|
|
92
95
|
// AND a provider that records the config of each LLM call across a tool round-trip
|
|
93
96
|
const { provider, configs } = makeRecordingProvider([
|
|
@@ -131,7 +134,7 @@ describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () =>
|
|
|
131
134
|
|
|
132
135
|
test("omits mutableLatestUserMessage when memory-v3-live is off (flag-off byte-identity)", async () => {
|
|
133
136
|
// GIVEN memory-v3-live is off (no override; registry default is false)
|
|
134
|
-
|
|
137
|
+
memoryV3LiveSlot = false;
|
|
135
138
|
|
|
136
139
|
// AND a provider that records the config of each LLM call
|
|
137
140
|
const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
|
|
@@ -2,9 +2,17 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
afterAll,
|
|
7
|
+
beforeAll,
|
|
8
|
+
describe,
|
|
9
|
+
expect,
|
|
10
|
+
setDefaultTimeout,
|
|
11
|
+
test,
|
|
12
|
+
} from "bun:test";
|
|
6
13
|
|
|
7
14
|
import { compileApp } from "../bundler/app-compiler.js";
|
|
15
|
+
import { ensureCompilerTools } from "../bundler/compiler-tools.js";
|
|
8
16
|
import {
|
|
9
17
|
ALLOWED_PACKAGES,
|
|
10
18
|
getCacheDir,
|
|
@@ -19,8 +27,14 @@ import {
|
|
|
19
27
|
|
|
20
28
|
let tempDir: string;
|
|
21
29
|
|
|
30
|
+
// Compiler-tool download (esbuild + preact from npm) can take 10s+ on a cold
|
|
31
|
+
// CI cache. Raise the file-level default so beforeAll isn't killed mid-install.
|
|
32
|
+
// Each test file runs in its own process, so this doesn't leak.
|
|
33
|
+
setDefaultTimeout(30_000);
|
|
34
|
+
|
|
22
35
|
beforeAll(async () => {
|
|
23
36
|
tempDir = await mkdtemp(join(tmpdir(), "app-compiler-test-"));
|
|
37
|
+
await ensureCompilerTools();
|
|
24
38
|
});
|
|
25
39
|
|
|
26
40
|
afterAll(async () => {
|
|
@@ -7,18 +7,10 @@ mock.module("../util/logger.js", () => ({
|
|
|
7
7
|
}),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
|
-
let
|
|
10
|
+
let shareAnalytics = true;
|
|
11
11
|
|
|
12
|
-
mock.module("../
|
|
13
|
-
|
|
14
|
-
ui: {},
|
|
15
|
-
model: "test",
|
|
16
|
-
provider: "test",
|
|
17
|
-
memory: { enabled: false },
|
|
18
|
-
rateLimit: { maxRequestsPerMinute: 0 },
|
|
19
|
-
secretDetection: { enabled: false },
|
|
20
|
-
collectUsageData,
|
|
21
|
-
}),
|
|
12
|
+
mock.module("../platform/consent-cache.js", () => ({
|
|
13
|
+
getCachedShareAnalytics: () => shareAnalytics,
|
|
22
14
|
}));
|
|
23
15
|
|
|
24
16
|
import {
|
|
@@ -53,7 +45,7 @@ const SAMPLE: AuthFallbackCount[] = [
|
|
|
53
45
|
|
|
54
46
|
describe("auth-fallback-events-store", () => {
|
|
55
47
|
beforeEach(() => {
|
|
56
|
-
|
|
48
|
+
shareAnalytics = true;
|
|
57
49
|
resetTable();
|
|
58
50
|
});
|
|
59
51
|
|
|
@@ -78,8 +70,8 @@ describe("auth-fallback-events-store", () => {
|
|
|
78
70
|
});
|
|
79
71
|
});
|
|
80
72
|
|
|
81
|
-
test("honors the
|
|
82
|
-
|
|
73
|
+
test("honors the share_analytics opt-out (records nothing)", () => {
|
|
74
|
+
shareAnalytics = false;
|
|
83
75
|
const recorded = recordAuthFallbackCounts(1000, 2000, SAMPLE);
|
|
84
76
|
expect(recorded).toBe(0);
|
|
85
77
|
expect(queryUnreportedAuthFallbackEvents(0, undefined, 100).length).toBe(0);
|
|
@@ -343,6 +343,34 @@ describe("addPointerMessage", () => {
|
|
|
343
343
|
expect(processorCalled).toBe(true);
|
|
344
344
|
});
|
|
345
345
|
|
|
346
|
+
test("unverified_contact provenance round-trips through the metadata schema and is treated as trusted audience", async () => {
|
|
347
|
+
// Persisted unverified_contact metadata must survive the schema parse so
|
|
348
|
+
// downstream consumers (e.g. memory write gate, pointer audience trust)
|
|
349
|
+
// see the durable trust snapshot rather than a silently-dropped undefined.
|
|
350
|
+
const convId = "conv-ptr-uvc-provenance";
|
|
351
|
+
ensureConversation(convId);
|
|
352
|
+
await addMessage(convId, "user", "hello", {
|
|
353
|
+
metadata: { provenanceTrustClass: "unverified_contact" },
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Confirm the durable snapshot round-trips through the schema parser.
|
|
357
|
+
const { getConversationRecentProvenanceTrustClass } = await import(
|
|
358
|
+
"../memory/conversation-crud.js"
|
|
359
|
+
);
|
|
360
|
+
expect(getConversationRecentProvenanceTrustClass(convId)).toBe(
|
|
361
|
+
"unverified_contact",
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// And that pointer-audience trust treats it identically to trusted_contact.
|
|
365
|
+
let processorCalled = false;
|
|
366
|
+
setPointerMessageProcessor(async () => {
|
|
367
|
+
processorCalled = true;
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
await addPointerMessage(convId, "completed", "+15559876543");
|
|
371
|
+
expect(processorCalled).toBe(true);
|
|
372
|
+
});
|
|
373
|
+
|
|
346
374
|
test("unknown provenance trust class does not grant trusted audience", () => {
|
|
347
375
|
const convId = "conv-ptr-unknown-provenance";
|
|
348
376
|
ensureConversation(convId);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cancel contract: a user cancel raises the conversation's AbortController and
|
|
3
|
+
* defers clearing the `processing` flag to the in-flight turn's `finally`.
|
|
4
|
+
*
|
|
5
|
+
* `cancelGeneration` no longer force-clears `processing` itself. Abort now
|
|
6
|
+
* propagates into the provider call and tool execution — and is backed by the
|
|
7
|
+
* agent loop's abort watchdog — so a cancelled turn always reaches its
|
|
8
|
+
* `finally` within a bounded time and tears down its own state there. The
|
|
9
|
+
* watchdog-driven path (turn reaches `finally`, processing clears) is covered
|
|
10
|
+
* by `conversation-agent-loop.test.ts`; this file pins the handler-side
|
|
11
|
+
* contract: cancel signals abort and leaves the flag to the turn.
|
|
12
|
+
*/
|
|
13
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
14
|
+
|
|
15
|
+
import type { Conversation } from "../daemon/conversation.js";
|
|
16
|
+
import {
|
|
17
|
+
deleteConversation,
|
|
18
|
+
setConversation,
|
|
19
|
+
} from "../daemon/conversation-registry.js";
|
|
20
|
+
import { cancelGeneration } from "../daemon/handlers/conversations.js";
|
|
21
|
+
|
|
22
|
+
interface CancelledConversation {
|
|
23
|
+
isProcessing: () => boolean;
|
|
24
|
+
setProcessingCalls: () => boolean[];
|
|
25
|
+
abortCount: () => number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Register a conversation whose in-flight turn is processing. The fake records
|
|
30
|
+
* abort calls and any `setProcessing` writes so the test can assert that
|
|
31
|
+
* `cancelGeneration` signals abort without flipping the flag itself.
|
|
32
|
+
*/
|
|
33
|
+
function registerProcessingTurn(id: string): CancelledConversation {
|
|
34
|
+
let processing = true;
|
|
35
|
+
let abortCount = 0;
|
|
36
|
+
const setProcessingCalls: boolean[] = [];
|
|
37
|
+
const fake = {
|
|
38
|
+
isProcessing: () => processing,
|
|
39
|
+
setProcessing: (value: boolean) => {
|
|
40
|
+
setProcessingCalls.push(value);
|
|
41
|
+
processing = value;
|
|
42
|
+
},
|
|
43
|
+
abort: () => {
|
|
44
|
+
abortCount += 1;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
setConversation(id, fake as unknown as Conversation);
|
|
48
|
+
return {
|
|
49
|
+
isProcessing: () => processing,
|
|
50
|
+
setProcessingCalls: () => setProcessingCalls,
|
|
51
|
+
abortCount: () => abortCount,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe("cancelGeneration", () => {
|
|
56
|
+
const conversationId = "cancel-clears-processing-test-conversation";
|
|
57
|
+
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
deleteConversation(conversationId);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("raises abort and defers clearing processing to the turn's finally", () => {
|
|
63
|
+
// GIVEN a registered conversation that is processing
|
|
64
|
+
const fake = registerProcessingTurn(conversationId);
|
|
65
|
+
expect(fake.isProcessing()).toBe(true);
|
|
66
|
+
|
|
67
|
+
// WHEN the user cancels generation
|
|
68
|
+
const cancelled = cancelGeneration(conversationId);
|
|
69
|
+
|
|
70
|
+
// THEN the cancel is acknowledged
|
|
71
|
+
expect(cancelled).toBe(true);
|
|
72
|
+
// AND the abort signal is raised on the conversation
|
|
73
|
+
expect(fake.abortCount()).toBe(1);
|
|
74
|
+
// AND cancelGeneration does NOT force-clear the flag itself — the in-flight
|
|
75
|
+
// turn's `finally` owns that teardown once abort drives it there.
|
|
76
|
+
expect(fake.setProcessingCalls()).not.toContain(false);
|
|
77
|
+
expect(fake.isProcessing()).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("returns false for a conversation that is not registered", () => {
|
|
81
|
+
// GIVEN no conversation registered under the id
|
|
82
|
+
|
|
83
|
+
// WHEN the user cancels generation for the unknown id
|
|
84
|
+
const cancelled = cancelGeneration("cancel-clears-processing-unknown-id");
|
|
85
|
+
|
|
86
|
+
// THEN the cancel reports that nothing was found
|
|
87
|
+
expect(cancelled).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -224,14 +224,12 @@ function ensureTestContact(): void {
|
|
|
224
224
|
{
|
|
225
225
|
type: "telegram",
|
|
226
226
|
address: "telegram-user-default",
|
|
227
|
-
externalUserId: "telegram-user-default",
|
|
228
227
|
status: "active",
|
|
229
228
|
policy: "allow",
|
|
230
229
|
},
|
|
231
230
|
{
|
|
232
231
|
type: "slack",
|
|
233
232
|
address: "slack-user-default",
|
|
234
|
-
externalUserId: "slack-user-default",
|
|
235
233
|
status: "active",
|
|
236
234
|
policy: "allow",
|
|
237
235
|
},
|
|
@@ -2054,7 +2052,6 @@ describe("requester cancel of guardian-gated pending request", () => {
|
|
|
2054
2052
|
{
|
|
2055
2053
|
type: "telegram",
|
|
2056
2054
|
address: "requester-cancel-user",
|
|
2057
|
-
externalUserId: "requester-cancel-user",
|
|
2058
2055
|
status: "active",
|
|
2059
2056
|
policy: "allow",
|
|
2060
2057
|
},
|
|
@@ -2981,7 +2978,6 @@ describe("trusted-contact self-approval blocked before guardian approval row exi
|
|
|
2981
2978
|
{
|
|
2982
2979
|
type: "telegram",
|
|
2983
2980
|
address: "tc-selfapproval-user",
|
|
2984
|
-
externalUserId: "tc-selfapproval-user",
|
|
2985
2981
|
status: "active",
|
|
2986
2982
|
policy: "allow",
|
|
2987
2983
|
},
|
|
@@ -97,7 +97,6 @@ function seedTrustedContact(policy: "allow" | "escalate" = "allow"): void {
|
|
|
97
97
|
{
|
|
98
98
|
type: "telegram",
|
|
99
99
|
address: "telegram-user-1",
|
|
100
|
-
externalUserId: "telegram-user-1",
|
|
101
100
|
status: "active",
|
|
102
101
|
policy,
|
|
103
102
|
},
|
|
@@ -237,14 +236,13 @@ describe("channel inbound disk pressure gate", () => {
|
|
|
237
236
|
expect(db.select().from(messages).all()).toHaveLength(0);
|
|
238
237
|
});
|
|
239
238
|
|
|
240
|
-
test("blocks non-guardian Slack reactions before persistence while locked", async () => {
|
|
239
|
+
test("blocks non-guardian Slack reactions silently (no reply) before persistence while locked", async () => {
|
|
241
240
|
upsertContact({
|
|
242
241
|
displayName: "Example Slack User",
|
|
243
242
|
channels: [
|
|
244
243
|
{
|
|
245
244
|
type: "slack",
|
|
246
245
|
address: "slack-user-1",
|
|
247
|
-
externalUserId: "slack-user-1",
|
|
248
246
|
status: "active",
|
|
249
247
|
policy: "allow",
|
|
250
248
|
},
|
|
@@ -276,18 +274,10 @@ describe("channel inbound disk pressure gate", () => {
|
|
|
276
274
|
reason: "trusted-contact",
|
|
277
275
|
});
|
|
278
276
|
expect(processMessage).not.toHaveBeenCalled();
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
chatId: "slack-channel-1",
|
|
284
|
-
text: expectedRemoteBlockReply,
|
|
285
|
-
assistantId: "self",
|
|
286
|
-
ephemeral: true,
|
|
287
|
-
user: "slack-user-1",
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
]);
|
|
277
|
+
// Reactions are blocked silently during disk pressure: a passive signal
|
|
278
|
+
// has nothing to "try again", so no block reply is delivered (unlike the
|
|
279
|
+
// message path, which does reply).
|
|
280
|
+
expect(deliverChannelReplyMock).not.toHaveBeenCalled();
|
|
291
281
|
|
|
292
282
|
const db = getDb();
|
|
293
283
|
const event = db
|
|
@@ -52,7 +52,7 @@ mock.module("../memory/v2/skill-store.js", () => ({
|
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
53
53
|
|
|
54
54
|
const { registerMemoryV2Command } =
|
|
55
|
-
await import("../cli/commands/memory-v2.js");
|
|
55
|
+
await import("../cli/commands/memory/memory-v2.js");
|
|
56
56
|
const { ROUTES: memoryV2Routes, MEMORY_V2_DISABLED_CODE } =
|
|
57
57
|
await import("../runtime/routes/memory-v2-routes.js");
|
|
58
58
|
const { RouteError } = await import("../runtime/routes/errors.js");
|
|
@@ -68,9 +68,8 @@ function buildProgram(): Command {
|
|
|
68
68
|
writeErr: () => {},
|
|
69
69
|
writeOut: () => {},
|
|
70
70
|
});
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
registerMemoryV2Command(program);
|
|
71
|
+
const memory = program.command("memory");
|
|
72
|
+
registerMemoryV2Command(memory);
|
|
74
73
|
return program;
|
|
75
74
|
}
|
|
76
75
|
|