@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5
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/bun.lock +20 -0
- package/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/openapi.yaml +196 -56
- package/package.json +4 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +42 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +164 -78
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/code-search-tool.test.ts +585 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/config-loader-backfill.test.ts +123 -10
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +19 -24
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +134 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/exploration-drift-hook.test.ts +3 -2
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
- package/src/__tests__/guardian-dispatch.test.ts +140 -1
- package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +290 -8
- package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +471 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
- package/src/__tests__/mcp-health-check.test.ts +2 -1
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +275 -5
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +191 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/openai-provider.test.ts +22 -12
- package/src/__tests__/openai-responses-provider.test.ts +12 -2
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +21 -83
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/plugin-pipeline.test.ts +96 -0
- package/src/__tests__/plugin-route-contribution.test.ts +6 -19
- package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
- package/src/__tests__/plugin-types.test.ts +2 -4
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-error-scenarios.test.ts +5 -4
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +4 -1
- package/src/__tests__/relay-server.test.ts +758 -32
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +49 -15
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -6
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +398 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +216 -20
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +154 -0
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +164 -1
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/events/conversation-notice.ts +26 -0
- package/src/api/index.ts +19 -1
- package/src/api/responses/llm-request-log-entry.ts +29 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/api/surfaces.ts +39 -3
- package/src/approvals/guardian-channel-delivery.ts +30 -0
- package/src/approvals/guardian-expiry-notifier.ts +148 -0
- package/src/approvals/guardian-request-resolvers.ts +17 -15
- package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +14 -9
- package/src/calls/inbound-trust-reader.ts +24 -2
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +188 -51
- package/src/calls/relay-setup-router.ts +47 -17
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/channels/types.ts +10 -20
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +242 -0
- package/src/cli/commands/plugins.ts +125 -13
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/cli/lib/publish-plugin.ts +633 -0
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/feature-flag-registry.json +5 -13
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/loader.ts +38 -5
- package/src/config/prune-seeded-callsite-defaults.ts +110 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +30 -34
- package/src/config/skills.ts +27 -5
- package/src/config/sync-gated-profiles.ts +12 -16
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -55
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/contacts/member-write-relay.ts +183 -0
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/assistant-attachments.ts +27 -4
- package/src/daemon/conversation-agent-loop.ts +28 -0
- package/src/daemon/conversation-notices.ts +60 -0
- package/src/daemon/conversation-process.ts +35 -16
- package/src/daemon/conversation-surfaces.ts +111 -38
- package/src/daemon/conversation-tool-setup.ts +71 -30
- package/src/daemon/conversation.ts +23 -1
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +17 -41
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +67 -26
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/lifecycle.ts +16 -0
- package/src/daemon/mcp-reload-service.ts +10 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/hooks/hook-loader.ts +341 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
- package/src/mcp/client.ts +15 -1
- package/src/mcp/mcp-auth-orchestrator.ts +6 -1
- package/src/mcp/mcp-header-store.ts +134 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -8
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +129 -61
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +37 -503
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +104 -0
- package/src/memory/llm-request-log-store.ts +26 -1
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +14 -8
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +106 -10
- package/src/memory/migrations/validate-migration-state.ts +105 -67
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +37 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +1294 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +204 -0
- package/src/memory/worker-process.ts +89 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/providers/slack/binding-metadata.ts +60 -0
- package/src/notifications/__tests__/broadcaster.test.ts +8 -8
- package/src/notifications/__tests__/connected-channels.test.ts +86 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +53 -25
- package/src/notifications/emit-signal.ts +34 -15
- package/src/onboarding/checkin-event.test.ts +222 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +12 -12
- package/src/plugin-api/types.ts +12 -14
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/mtime-cache.ts +186 -330
- package/src/plugins/pipeline.ts +111 -13
- package/src/plugins/registry.ts +59 -16
- package/src/plugins/surface-import.ts +121 -0
- package/src/plugins/types.ts +7 -9
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +45 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
- package/src/providers/openai/api-error-normalization.ts +270 -0
- package/src/providers/openai/chat-completions-provider.ts +37 -83
- package/src/providers/openai/responses-provider.ts +50 -46
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +10 -0
- package/src/providers/ratelimit.ts +10 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +10 -0
- package/src/providers/types.ts +22 -0
- package/src/providers/usage-tracking.ts +10 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
- package/src/runtime/access-request-helper.ts +18 -39
- package/src/runtime/actor-trust-resolver.ts +46 -19
- package/src/runtime/anchored-guardian.test.ts +109 -0
- package/src/runtime/anchored-guardian.ts +86 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +69 -8
- package/src/runtime/invite-redemption-service.ts +213 -187
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +85 -13
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
- package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +48 -57
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-query-routes.ts +72 -0
- package/src/runtime/routes/conversation-routes.ts +84 -85
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +16 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +83 -0
- package/src/runtime/routes/mcp-auth-routes.ts +171 -19
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +42 -56
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +13 -58
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +111 -40
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +254 -33
- package/src/subagent/types.ts +11 -4
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/executor.ts +4 -4
- package/src/tools/filesystem/search.ts +543 -0
- package/src/tools/registry.ts +38 -0
- package/src/tools/shared/filesystem/path-policy.ts +12 -5
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/tool-manifest.ts +3 -0
- package/src/tools/types.ts +131 -25
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/errors.ts +26 -1
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +19 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workflows/library.test.ts +140 -0
- package/src/workflows/library.ts +82 -28
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -93
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
- package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
import * as Sentry from "@sentry/node";
|
|
35
35
|
|
|
36
|
+
import { recordWatchdogEvent } from "../memory/watchdog-events-store.js";
|
|
36
37
|
import { getLogger } from "../util/logger.js";
|
|
37
38
|
|
|
38
39
|
const log = getLogger("event-loop-watchdog");
|
|
@@ -74,11 +75,37 @@ export function evaluateTick(
|
|
|
74
75
|
return { blockedMs, exceeded: blockedMs >= thresholdMs };
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Check name emitted for event-loop block events. The platform's
|
|
80
|
+
* `watchdog__event_loop_blocking_daily` admin query filters `check_name` to
|
|
81
|
+
* this exact string, so it is the primary group-by dimension downstream —
|
|
82
|
+
* keep it stable.
|
|
83
|
+
*/
|
|
84
|
+
export const EVENT_LOOP_BLOCKED_CHECK_NAME = "event_loop_blocked";
|
|
85
|
+
|
|
77
86
|
function reportBlock(blockedMs: number, thresholdMs: number): void {
|
|
78
87
|
log.warn(
|
|
79
88
|
{ blockedMs, thresholdMs, tickIntervalMs: TICK_INTERVAL_MS },
|
|
80
89
|
"event loop blocked",
|
|
81
90
|
);
|
|
91
|
+
// Persist a `watchdog` telemetry event so the platform can surface
|
|
92
|
+
// event-loop blocking in the infrastructure admin chart. `recordWatchdogEvent`
|
|
93
|
+
// no-ops when usage-data collection is disabled (the event is dropped to
|
|
94
|
+
// honor the opt-out), so the watchdog runs unconditionally without leaking
|
|
95
|
+
// health data for opted-out owners. Never let a telemetry failure escape
|
|
96
|
+
// the timer callback — wrap it alongside the Sentry capture below.
|
|
97
|
+
try {
|
|
98
|
+
recordWatchdogEvent({
|
|
99
|
+
checkName: EVENT_LOOP_BLOCKED_CHECK_NAME,
|
|
100
|
+
value: blockedMs,
|
|
101
|
+
detail: {
|
|
102
|
+
threshold_ms: thresholdMs,
|
|
103
|
+
tick_interval_ms: TICK_INTERVAL_MS,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
} catch {
|
|
107
|
+
// Never let a telemetry failure escape the timer callback.
|
|
108
|
+
}
|
|
82
109
|
try {
|
|
83
110
|
Sentry.withScope((scope) => {
|
|
84
111
|
scope.setLevel("warning");
|
|
@@ -88,7 +115,7 @@ function reportBlock(blockedMs: number, thresholdMs: number): void {
|
|
|
88
115
|
threshold_ms: thresholdMs,
|
|
89
116
|
tick_interval_ms: TICK_INTERVAL_MS,
|
|
90
117
|
});
|
|
91
|
-
Sentry.captureMessage(
|
|
118
|
+
Sentry.captureMessage(EVENT_LOOP_BLOCKED_CHECK_NAME);
|
|
92
119
|
});
|
|
93
120
|
} catch {
|
|
94
121
|
// Never let a telemetry failure escape the timer callback.
|
|
@@ -19,24 +19,20 @@
|
|
|
19
19
|
* dropped from the registry via {@link unregisterPlugin} so none of its
|
|
20
20
|
* hooks participate in the turn lifecycle. This is the primary mechanism for
|
|
21
21
|
* shipping experimental plugins behind a feature flag.
|
|
22
|
-
* 4.
|
|
23
|
-
* credential store helper ({@link getSecureKeyAsync}). In Docker mode
|
|
24
|
-
* that helper goes through the CES HTTP API transparently; in local mode
|
|
25
|
-
* it hits the encrypted file store / CES RPC backend.
|
|
26
|
-
* 5. Validates the config block under `plugins.<name>` against
|
|
22
|
+
* 4. Validates the config block under `plugins.<name>` against
|
|
27
23
|
* `manifest.config` if the manifest supplies a parser-like validator
|
|
28
24
|
* (Zod schemas with `.parse()` are supported; anything else is passed
|
|
29
25
|
* through untouched).
|
|
30
|
-
*
|
|
31
|
-
* writable state and exposes it via {@link
|
|
32
|
-
*
|
|
26
|
+
* 5. Creates `<workspaceDir>/plugins-data/<plugin>/` on demand for per-plugin
|
|
27
|
+
* writable state and exposes it via {@link InitContext.pluginStorageDir}.
|
|
28
|
+
* 6. For each surviving plugin, registers its contributed tools and routes
|
|
33
29
|
* into their global registries via {@link registerPluginTools} and
|
|
34
30
|
* {@link registerSkillRoute}. Contributions land BEFORE `init()` so
|
|
35
31
|
* the plugin's hook can observe a registry where its own model-visible
|
|
36
32
|
* surface is already wired — useful for plugins that want to attach
|
|
37
33
|
* metadata, warm caches, or otherwise interact with their own
|
|
38
34
|
* contributions during initialization.
|
|
39
|
-
*
|
|
35
|
+
* 7. Awaits `plugin.init(ctx)` sequentially. An init failure is contained to
|
|
40
36
|
* the offending plugin: its already-registered tools and routes are rolled
|
|
41
37
|
* back, it is dropped from the registry, the failure is logged, and
|
|
42
38
|
* bootstrap continues with the remaining plugins. A single plugin's failure
|
|
@@ -68,7 +64,7 @@ import { getRegisteredPlugins, unregisterPlugin } from "../plugins/registry.js";
|
|
|
68
64
|
import {
|
|
69
65
|
type Plugin,
|
|
70
66
|
PluginExecutionError,
|
|
71
|
-
type
|
|
67
|
+
type ShutdownContext,
|
|
72
68
|
} from "../plugins/types.js";
|
|
73
69
|
import { loadUserPlugins } from "../plugins/user-loader.js";
|
|
74
70
|
import {
|
|
@@ -76,7 +72,6 @@ import {
|
|
|
76
72
|
type SkillRouteHandle,
|
|
77
73
|
unregisterSkillRoute,
|
|
78
74
|
} from "../runtime/skill-route-registry.js";
|
|
79
|
-
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
80
75
|
import {
|
|
81
76
|
registerPluginTools,
|
|
82
77
|
unregisterPluginTools,
|
|
@@ -88,25 +83,6 @@ import { registerShutdownHook } from "./shutdown-registry.js";
|
|
|
88
83
|
|
|
89
84
|
const log = getLogger("plugins-bootstrap");
|
|
90
85
|
|
|
91
|
-
/**
|
|
92
|
-
* Resolve one credential value. Returns the raw secret string or throws a
|
|
93
|
-
* {@link PluginExecutionError} tagged with the plugin name so the caller can
|
|
94
|
-
* fail startup with clear attribution.
|
|
95
|
-
*/
|
|
96
|
-
async function resolveCredentialOrThrow(
|
|
97
|
-
pluginName: string,
|
|
98
|
-
credentialKey: string,
|
|
99
|
-
): Promise<string> {
|
|
100
|
-
const value = await getSecureKeyAsync(credentialKey);
|
|
101
|
-
if (value === undefined || value === "") {
|
|
102
|
-
throw new PluginExecutionError(
|
|
103
|
-
`plugin ${pluginName} requires credential "${credentialKey}" but the credential store returned no value`,
|
|
104
|
-
pluginName,
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
return value;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
86
|
/**
|
|
111
87
|
* Validate a plugin config block. If the manifest supplies a parser-like
|
|
112
88
|
* validator (Zod schemas expose `.parse()`), use it. Otherwise pass the
|
|
@@ -246,8 +222,8 @@ export async function bootstrapPlugins(): Promise<void> {
|
|
|
246
222
|
// Shutdown context is identical for every plugin in this boot — construct
|
|
247
223
|
// once and reuse across the per-plugin teardown and the normal shutdown
|
|
248
224
|
// hook below. Only `assistantVersion` is exposed today; future additions
|
|
249
|
-
// live on {@link
|
|
250
|
-
const shutdownContext:
|
|
225
|
+
// live on {@link ShutdownContext}.
|
|
226
|
+
const shutdownContext: ShutdownContext = {
|
|
251
227
|
assistantVersion: APP_VERSION,
|
|
252
228
|
};
|
|
253
229
|
|
|
@@ -271,7 +247,14 @@ export async function bootstrapPlugins(): Promise<void> {
|
|
|
271
247
|
// out-of-band kill switch — the operator creates a directory named
|
|
272
248
|
// after the plugin's manifest name (e.g. `plugins/default-advisor/`)
|
|
273
249
|
// and drops a `.disabled` file inside it. Runs before init so no
|
|
274
|
-
//
|
|
250
|
+
// tools or routes from the disabled plugin are ever wired.
|
|
251
|
+
//
|
|
252
|
+
// Unlike the feature-flag path above, we do NOT call
|
|
253
|
+
// `unregisterPlugin(name)` here. The plugin's hooks stay in the hook
|
|
254
|
+
// registry and are filtered at read time by `isPluginDisabled` in
|
|
255
|
+
// `getHooksFor`. This means `assistant plugins enable <name>` takes
|
|
256
|
+
// effect on the next turn without a restart — the hooks are already
|
|
257
|
+
// registered, they just need the sentinel removed to be included.
|
|
275
258
|
const disabledSentinelPath = join(
|
|
276
259
|
getWorkspacePluginsDir(),
|
|
277
260
|
name,
|
|
@@ -282,7 +265,6 @@ export async function bootstrapPlugins(): Promise<void> {
|
|
|
282
265
|
{ plugin: name, sentinel: disabledSentinelPath },
|
|
283
266
|
`skipping plugin ${name}: disabled via .disabled sentinel`,
|
|
284
267
|
);
|
|
285
|
-
unregisterPlugin(name);
|
|
286
268
|
continue;
|
|
287
269
|
}
|
|
288
270
|
|
|
@@ -346,11 +328,6 @@ async function initializePlugin(
|
|
|
346
328
|
let initCompleted = false;
|
|
347
329
|
|
|
348
330
|
try {
|
|
349
|
-
const credentials: Record<string, string> = {};
|
|
350
|
-
for (const key of plugin.manifest.requiresCredential ?? []) {
|
|
351
|
-
credentials[key] = await resolveCredentialOrThrow(name, key);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
331
|
const config = validatePluginConfig(
|
|
355
332
|
name,
|
|
356
333
|
plugin.manifest.config,
|
|
@@ -359,7 +336,6 @@ async function initializePlugin(
|
|
|
359
336
|
|
|
360
337
|
const initContext = {
|
|
361
338
|
config,
|
|
362
|
-
credentials,
|
|
363
339
|
logger: log.child({ plugin: name }),
|
|
364
340
|
pluginStorageDir: ensurePluginStorageDir(name),
|
|
365
341
|
assistantVersion: APP_VERSION,
|
|
@@ -437,7 +413,7 @@ async function initializePlugin(
|
|
|
437
413
|
async function teardownPlugin(
|
|
438
414
|
active: ActivePlugin,
|
|
439
415
|
reason: string,
|
|
440
|
-
shutdownContext:
|
|
416
|
+
shutdownContext: ShutdownContext,
|
|
441
417
|
): Promise<void> {
|
|
442
418
|
const { plugin, routeHandles } = active;
|
|
443
419
|
const name = plugin.manifest.name;
|
|
@@ -174,7 +174,6 @@ describe("acceptA2AInvite", () => {
|
|
|
174
174
|
expect(contact).not.toBeNull();
|
|
175
175
|
expect(contact!.channels).toHaveLength(1);
|
|
176
176
|
expect(contact!.channels[0]!.type).toBe("a2a");
|
|
177
|
-
expect(contact!.channels[0]!.status).toBe("active");
|
|
178
177
|
|
|
179
178
|
// Verify assistant metadata
|
|
180
179
|
const metadata = getAssistantContactMetadata(result.contactId!);
|
|
@@ -100,8 +100,6 @@ describe("completeA2AInvite", () => {
|
|
|
100
100
|
expect(contact!.displayName).toBe("Acceptor Bot");
|
|
101
101
|
expect(contact!.channels).toHaveLength(1);
|
|
102
102
|
expect(contact!.channels[0]!.type).toBe("a2a");
|
|
103
|
-
expect(contact!.channels[0]!.status).toBe("active");
|
|
104
|
-
expect(contact!.channels[0]!.policy).toBe("allow");
|
|
105
103
|
});
|
|
106
104
|
|
|
107
105
|
test("contact channel address is acceptor.assistantId.toLowerCase()", () => {
|
|
@@ -71,8 +71,6 @@ describe("redeemA2AInvite", () => {
|
|
|
71
71
|
expect(contact!.displayName).toBe("Sender Bot");
|
|
72
72
|
expect(contact!.channels).toHaveLength(1);
|
|
73
73
|
expect(contact!.channels[0]!.type).toBe("a2a");
|
|
74
|
-
expect(contact!.channels[0]!.status).toBe("active");
|
|
75
|
-
expect(contact!.channels[0]!.policy).toBe("allow");
|
|
76
74
|
});
|
|
77
75
|
|
|
78
76
|
test("idempotency: already-connected sender returns alreadyConnected", () => {
|
|
@@ -129,4 +127,29 @@ describe("redeemA2AInvite", () => {
|
|
|
129
127
|
const result = redeemA2AInvite({ sender: SENDER });
|
|
130
128
|
expect(result.success).toBe(true);
|
|
131
129
|
});
|
|
130
|
+
|
|
131
|
+
test("activeConnections counts a2a channel existence, not status", () => {
|
|
132
|
+
const result = redeemA2AInvite({ sender: SENDER });
|
|
133
|
+
expect(result.success).toBe(true);
|
|
134
|
+
|
|
135
|
+
expect(getA2AConfig().activeConnections).toBe(1);
|
|
136
|
+
|
|
137
|
+
// Readiness is existence-based: a non-active stored status still counts.
|
|
138
|
+
const sqlite = getSqlite();
|
|
139
|
+
sqlite.run("UPDATE contact_channels SET status = 'unverified'");
|
|
140
|
+
invalidateConfigCache();
|
|
141
|
+
expect(getA2AConfig().activeConnections).toBe(1);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("already-connected guard fires regardless of stored channel status", () => {
|
|
145
|
+
const first = redeemA2AInvite({ sender: SENDER });
|
|
146
|
+
expect(first.success).toBe(true);
|
|
147
|
+
|
|
148
|
+
const sqlite = getSqlite();
|
|
149
|
+
sqlite.run("UPDATE contact_channels SET status = 'revoked'");
|
|
150
|
+
|
|
151
|
+
const second = redeemA2AInvite({ sender: SENDER });
|
|
152
|
+
expect(second.alreadyConnected).toBe(true);
|
|
153
|
+
expect(second.contactId).toBe(first.contactId);
|
|
154
|
+
});
|
|
132
155
|
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { GuardianDelivery } from "@vellumai/gateway-client";
|
|
4
|
+
|
|
5
|
+
import type { ContactChannel } from "../../../contacts/types.js";
|
|
6
|
+
|
|
7
|
+
let mockGuardians: GuardianDelivery[] | null = null;
|
|
8
|
+
let mockBinding: { guardianExternalUserId: string; guardianDeliveryChatId: string } | null = null;
|
|
9
|
+
let mockContactChannel: { channel: ContactChannel } | null = null;
|
|
10
|
+
let mockChannel: ContactChannel | null = null;
|
|
11
|
+
let mockGwContactChannels: Array<{ id: string; status: string; verifiedAt: number | null }> | null =
|
|
12
|
+
null;
|
|
13
|
+
let ipcCalls: Array<{ method: string; payload: unknown }> = [];
|
|
14
|
+
|
|
15
|
+
mock.module("../../../contacts/guardian-delivery-reader.js", () => ({
|
|
16
|
+
getGuardianDelivery: async (input?: { channelTypes?: string[] }) => {
|
|
17
|
+
if (mockGuardians == null) return null;
|
|
18
|
+
if (!input?.channelTypes) return mockGuardians;
|
|
19
|
+
return mockGuardians.filter((g) =>
|
|
20
|
+
input.channelTypes!.includes(g.channelType),
|
|
21
|
+
);
|
|
22
|
+
},
|
|
23
|
+
guardianForChannel: (
|
|
24
|
+
list: GuardianDelivery[],
|
|
25
|
+
channelType: string,
|
|
26
|
+
) => list.find((g) => g.channelType === channelType && g.status === "active"),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
mock.module("../../../contacts/contact-store.js", () => ({
|
|
30
|
+
findContactChannel: () => mockContactChannel,
|
|
31
|
+
getChannelById: () => mockChannel,
|
|
32
|
+
getContact: () => ({ id: "contact-1", displayName: "Pat" }),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
mock.module("../../../contacts/contact-events.js", () => ({
|
|
36
|
+
emitContactChange: () => {},
|
|
37
|
+
onContactChange: () => {},
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
mock.module("../../../ipc/gateway-client.js", () => ({
|
|
41
|
+
ipcCallPersistent: async (method: string, payload: unknown) => {
|
|
42
|
+
ipcCalls.push({ method, payload });
|
|
43
|
+
if (method === "contacts_get_rich") {
|
|
44
|
+
if (mockGwContactChannels == null) return { ok: true, contact: null };
|
|
45
|
+
return {
|
|
46
|
+
ok: true,
|
|
47
|
+
contact: {
|
|
48
|
+
id: "contact-1",
|
|
49
|
+
displayName: "Pat",
|
|
50
|
+
role: "contact",
|
|
51
|
+
interactionCount: 0,
|
|
52
|
+
createdAt: 0,
|
|
53
|
+
updatedAt: 0,
|
|
54
|
+
channels: mockGwContactChannels.map((c) => ({
|
|
55
|
+
id: c.id,
|
|
56
|
+
contactId: "contact-1",
|
|
57
|
+
type: "telegram",
|
|
58
|
+
address: "user-123",
|
|
59
|
+
isPrimary: true,
|
|
60
|
+
externalUserId: null,
|
|
61
|
+
status: c.status,
|
|
62
|
+
policy: "allow",
|
|
63
|
+
verifiedAt: c.verifiedAt,
|
|
64
|
+
verifiedVia: null,
|
|
65
|
+
lastSeenAt: null,
|
|
66
|
+
interactionCount: 0,
|
|
67
|
+
lastInteraction: null,
|
|
68
|
+
revokedReason: null,
|
|
69
|
+
blockedReason: null,
|
|
70
|
+
})),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
ok: true,
|
|
76
|
+
didWrite: true,
|
|
77
|
+
channel: {
|
|
78
|
+
id: "ch-1",
|
|
79
|
+
contactId: "contact-1",
|
|
80
|
+
type: "telegram",
|
|
81
|
+
address: "user-123",
|
|
82
|
+
status: "revoked",
|
|
83
|
+
revokedReason: "guardian_binding_revoked",
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
ipcCall: async () => null,
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
mock.module("../../../runtime/channel-verification-service.js", () => ({
|
|
91
|
+
getGuardianBinding: () => mockBinding,
|
|
92
|
+
revokeBinding: () => true,
|
|
93
|
+
revokePendingSessions: () => {},
|
|
94
|
+
createOutboundSession: () => ({
|
|
95
|
+
sessionId: "sess",
|
|
96
|
+
secret: "code",
|
|
97
|
+
expiresAt: Date.now() + 1000,
|
|
98
|
+
}),
|
|
99
|
+
countRecentSendsToDestination: () => 0,
|
|
100
|
+
isGuardianBoundForChannel: async () => false,
|
|
101
|
+
updateSessionDelivery: () => {},
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
mock.module("../../../runtime/verification-outbound-actions.js", () => ({
|
|
105
|
+
cancelOutbound: () => {},
|
|
106
|
+
deliverVerificationEmail: () => {},
|
|
107
|
+
deliverVerificationSlack: () => {},
|
|
108
|
+
deliverVerificationTelegram: () => {},
|
|
109
|
+
DESTINATION_RATE_WINDOW_MS: 1000,
|
|
110
|
+
MAX_SENDS_PER_DESTINATION_WINDOW: 5,
|
|
111
|
+
normalizeTelegramDestination: (d: string) => d,
|
|
112
|
+
resendOutbound: () => ({}),
|
|
113
|
+
startOutbound: async () => ({}),
|
|
114
|
+
}));
|
|
115
|
+
|
|
116
|
+
import {
|
|
117
|
+
revokeVerificationForChannel,
|
|
118
|
+
verifyTrustedContact,
|
|
119
|
+
} from "../config-channels.js";
|
|
120
|
+
|
|
121
|
+
function channel(overrides: Partial<ContactChannel> = {}): ContactChannel {
|
|
122
|
+
return {
|
|
123
|
+
id: "ch-1",
|
|
124
|
+
contactId: "contact-1",
|
|
125
|
+
type: "telegram",
|
|
126
|
+
address: "user-123",
|
|
127
|
+
isPrimary: true,
|
|
128
|
+
externalChatId: "chat-123",
|
|
129
|
+
inviteId: null,
|
|
130
|
+
lastSeenAt: null,
|
|
131
|
+
interactionCount: 0,
|
|
132
|
+
lastInteraction: null,
|
|
133
|
+
updatedAt: null,
|
|
134
|
+
createdAt: 0,
|
|
135
|
+
...overrides,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function delivery(overrides: Partial<GuardianDelivery> = {}): GuardianDelivery {
|
|
140
|
+
return {
|
|
141
|
+
channelType: "telegram",
|
|
142
|
+
contactId: "contact-1",
|
|
143
|
+
address: "user-123",
|
|
144
|
+
externalChatId: "chat-123",
|
|
145
|
+
status: "active",
|
|
146
|
+
verifiedAt: 1700000000,
|
|
147
|
+
...overrides,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
describe("revokeVerificationForChannel", () => {
|
|
152
|
+
beforeEach(() => {
|
|
153
|
+
mockGuardians = null;
|
|
154
|
+
mockBinding = { guardianExternalUserId: "user-123", guardianDeliveryChatId: "chat-123" };
|
|
155
|
+
mockContactChannel = { channel: channel() };
|
|
156
|
+
ipcCalls = [];
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("relays mark_channel_revoked when the gateway delivery is live", async () => {
|
|
160
|
+
mockGuardians = [delivery({ status: "active" })];
|
|
161
|
+
await revokeVerificationForChannel("telegram");
|
|
162
|
+
expect(ipcCalls.map((c) => c.method)).toContain("mark_channel_revoked");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("skips a redundant revoke when the gateway delivery is already revoked", async () => {
|
|
166
|
+
// The gateway (SoT) says revoked — the gate must follow the gateway and
|
|
167
|
+
// not relay regardless of local state.
|
|
168
|
+
mockContactChannel = { channel: channel() };
|
|
169
|
+
mockGuardians = [delivery({ status: "revoked" })];
|
|
170
|
+
await revokeVerificationForChannel("telegram");
|
|
171
|
+
expect(ipcCalls.map((c) => c.method)).not.toContain("mark_channel_revoked");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("skips the relay when the gateway has no delivery for the channel", async () => {
|
|
175
|
+
mockGuardians = [];
|
|
176
|
+
await revokeVerificationForChannel("telegram");
|
|
177
|
+
expect(ipcCalls.map((c) => c.method)).not.toContain("mark_channel_revoked");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("skips the relay when the gateway is unreachable", async () => {
|
|
181
|
+
mockGuardians = null;
|
|
182
|
+
await revokeVerificationForChannel("telegram");
|
|
183
|
+
expect(ipcCalls.map((c) => c.method)).not.toContain("mark_channel_revoked");
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("verifyTrustedContact already-verified gate", () => {
|
|
188
|
+
beforeEach(() => {
|
|
189
|
+
mockGuardians = null;
|
|
190
|
+
mockGwContactChannels = null;
|
|
191
|
+
mockChannel = channel();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("short-circuits when the gateway contact channel is active and verified", async () => {
|
|
195
|
+
// Arbitrary trusted contact (non-guardian) — read from the contact-channel
|
|
196
|
+
// gateway read, which covers all contacts.
|
|
197
|
+
mockGwContactChannels = [
|
|
198
|
+
{ id: "ch-1", status: "active", verifiedAt: 1700000000 },
|
|
199
|
+
];
|
|
200
|
+
const result = await verifyTrustedContact("ch-1", "assistant-1");
|
|
201
|
+
expect(result.success).toBe(false);
|
|
202
|
+
expect(result.error).toBe("already_verified");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("does not short-circuit when the gateway channel has no verifiedAt", async () => {
|
|
206
|
+
// The gateway channel is unverified — proceed regardless of local state.
|
|
207
|
+
mockChannel = channel();
|
|
208
|
+
mockGwContactChannels = [
|
|
209
|
+
{ id: "ch-1", status: "pending", verifiedAt: null },
|
|
210
|
+
];
|
|
211
|
+
const result = await verifyTrustedContact("ch-1", "assistant-1");
|
|
212
|
+
expect(result.error).not.toBe("already_verified");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("does not short-circuit when the gateway has no matching channel", async () => {
|
|
216
|
+
mockGwContactChannels = [];
|
|
217
|
+
const result = await verifyTrustedContact("ch-1", "assistant-1");
|
|
218
|
+
expect(result.error).not.toBe("already_verified");
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -96,13 +96,11 @@ export function getA2AConfig(): A2AConfigResult {
|
|
|
96
96
|
const config = getConfig();
|
|
97
97
|
const enabled = config.a2a?.enabled ?? false;
|
|
98
98
|
|
|
99
|
+
// a2a is peer binding outside the human-trust ACL model — the gateway has no
|
|
100
|
+
// canonical a2a channel status, so channel existence is the readiness signal.
|
|
99
101
|
const contacts = searchContacts({ channelType: "a2a" });
|
|
100
102
|
const activeConnections = contacts.reduce((count, c) => {
|
|
101
|
-
return (
|
|
102
|
-
count +
|
|
103
|
-
c.channels.filter((ch) => ch.type === "a2a" && ch.status === "active")
|
|
104
|
-
.length
|
|
105
|
-
);
|
|
103
|
+
return count + c.channels.filter((ch) => ch.type === "a2a").length;
|
|
106
104
|
}, 0);
|
|
107
105
|
|
|
108
106
|
return { success: true, enabled, activeConnections };
|
|
@@ -270,12 +268,9 @@ export function redeemA2AInvite(params: {
|
|
|
270
268
|
setA2AConfig();
|
|
271
269
|
}
|
|
272
270
|
|
|
273
|
-
// 2. Check for existing
|
|
271
|
+
// 2. Check for existing contact with this sender (a2a binding existence)
|
|
274
272
|
const existing = findContactByAddress("a2a", params.sender.assistantId);
|
|
275
|
-
if (
|
|
276
|
-
existing &&
|
|
277
|
-
existing.channels.some((ch) => ch.type === "a2a" && ch.status === "active")
|
|
278
|
-
) {
|
|
273
|
+
if (existing && existing.channels.some((ch) => ch.type === "a2a")) {
|
|
279
274
|
return { success: true, alreadyConnected: true, contactId: existing.id };
|
|
280
275
|
}
|
|
281
276
|
|
|
@@ -373,10 +368,7 @@ export async function acceptA2AInvite(params: {
|
|
|
373
368
|
// 2. Short-circuit if already connected — avoids a network round-trip
|
|
374
369
|
// and consuming a token on the sender side.
|
|
375
370
|
const existing = findContactByAddress("a2a", params.senderAssistantId);
|
|
376
|
-
if (
|
|
377
|
-
existing &&
|
|
378
|
-
existing.channels.some((ch) => ch.type === "a2a" && ch.status === "active")
|
|
379
|
-
) {
|
|
371
|
+
if (existing && existing.channels.some((ch) => ch.type === "a2a")) {
|
|
380
372
|
return { success: true, alreadyConnected: true, contactId: existing.id };
|
|
381
373
|
}
|
|
382
374
|
|