@vellumai/assistant 0.5.13 → 0.5.15
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/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +4 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +64 -88
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +3 -14
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +71 -33
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { generateSparseEmbedding } from "../embedding-backend.js";
|
|
2
|
+
import type { SparseEmbedding } from "../embedding-types.js";
|
|
3
|
+
import type { TieredCandidate } from "./tier-classifier.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compute cosine similarity between two sparse vectors.
|
|
7
|
+
* Returns 0 if either vector has zero magnitude.
|
|
8
|
+
*/
|
|
9
|
+
function sparseCosine(a: SparseEmbedding, b: SparseEmbedding): number {
|
|
10
|
+
// Build index→value map for vector b
|
|
11
|
+
const bMap = new Map<number, number>();
|
|
12
|
+
for (let i = 0; i < b.indices.length; i++) {
|
|
13
|
+
bMap.set(b.indices[i]!, b.values[i]!);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Compute dot product over shared indices
|
|
17
|
+
let dotProduct = 0;
|
|
18
|
+
for (let i = 0; i < a.indices.length; i++) {
|
|
19
|
+
const bVal = bMap.get(a.indices[i]!);
|
|
20
|
+
if (bVal !== undefined) {
|
|
21
|
+
dotProduct += a.values[i]! * bVal;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Compute magnitudes
|
|
26
|
+
let magA = 0;
|
|
27
|
+
for (const v of a.values) magA += v * v;
|
|
28
|
+
magA = Math.sqrt(magA);
|
|
29
|
+
|
|
30
|
+
let magB = 0;
|
|
31
|
+
for (const v of b.values) magB += v * v;
|
|
32
|
+
magB = Math.sqrt(magB);
|
|
33
|
+
|
|
34
|
+
if (magA === 0 || magB === 0) return 0;
|
|
35
|
+
return dotProduct / (magA * magB);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Apply Maximal Marginal Relevance (MMR) diversity ranking to candidates.
|
|
40
|
+
*
|
|
41
|
+
* Items are re-ranked using a greedy selection loop that progressively
|
|
42
|
+
* penalizes candidates whose text is similar to already-selected ones.
|
|
43
|
+
* Non-item candidates (segments, summaries, media) pass through unpenalized
|
|
44
|
+
* since they represent different conversation windows.
|
|
45
|
+
*
|
|
46
|
+
* @param candidates - Scored candidates from upstream ranking
|
|
47
|
+
* @param penalty - Float 0..1. 0 = no diversity pressure, 1 = maximum
|
|
48
|
+
* @returns Re-ranked candidates with adjusted finalScores
|
|
49
|
+
*/
|
|
50
|
+
export function applyMMR(
|
|
51
|
+
candidates: TieredCandidate[],
|
|
52
|
+
penalty: number,
|
|
53
|
+
): TieredCandidate[] {
|
|
54
|
+
// Pre-compute sparse embeddings for all candidates
|
|
55
|
+
const embeddings = candidates.map((c) => generateSparseEmbedding(c.text));
|
|
56
|
+
|
|
57
|
+
// Separate items from non-items
|
|
58
|
+
const items: { index: number; candidate: TieredCandidate }[] = [];
|
|
59
|
+
const nonItems: TieredCandidate[] = [];
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
62
|
+
const c = candidates[i]!;
|
|
63
|
+
if (c.type === "item") {
|
|
64
|
+
items.push({ index: i, candidate: c });
|
|
65
|
+
} else {
|
|
66
|
+
nonItems.push(c);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// If no items or no penalty, pass through in original order
|
|
71
|
+
if (items.length === 0 || penalty === 0) {
|
|
72
|
+
return candidates;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Greedy MMR selection loop
|
|
76
|
+
const selected: number[] = [];
|
|
77
|
+
const remaining = new Set<number>(items.map((_, i) => i));
|
|
78
|
+
const adjustedScores = new Map<number, number>();
|
|
79
|
+
|
|
80
|
+
// Select the item with the highest finalScore first
|
|
81
|
+
let bestIdx = -1;
|
|
82
|
+
let bestScore = -Infinity;
|
|
83
|
+
for (const idx of remaining) {
|
|
84
|
+
const score = items[idx]!.candidate.finalScore;
|
|
85
|
+
if (score > bestScore) {
|
|
86
|
+
bestScore = score;
|
|
87
|
+
bestIdx = idx;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
selected.push(bestIdx);
|
|
91
|
+
remaining.delete(bestIdx);
|
|
92
|
+
adjustedScores.set(bestIdx, items[bestIdx]!.candidate.finalScore);
|
|
93
|
+
|
|
94
|
+
// Iteratively select remaining items
|
|
95
|
+
while (remaining.size > 0) {
|
|
96
|
+
let nextBestIdx = -1;
|
|
97
|
+
let nextBestScore = -Infinity;
|
|
98
|
+
|
|
99
|
+
for (const idx of remaining) {
|
|
100
|
+
const itemEmbIdx = items[idx]!.index;
|
|
101
|
+
|
|
102
|
+
// Compute max similarity to any already-selected item
|
|
103
|
+
let maxSim = 0;
|
|
104
|
+
for (const selIdx of selected) {
|
|
105
|
+
const selEmbIdx = items[selIdx]!.index;
|
|
106
|
+
const sim = sparseCosine(
|
|
107
|
+
embeddings[itemEmbIdx]!,
|
|
108
|
+
embeddings[selEmbIdx]!,
|
|
109
|
+
);
|
|
110
|
+
if (sim > maxSim) maxSim = sim;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const adjustedScore =
|
|
114
|
+
items[idx]!.candidate.finalScore * (1 - maxSim * penalty);
|
|
115
|
+
if (adjustedScore > nextBestScore) {
|
|
116
|
+
nextBestScore = adjustedScore;
|
|
117
|
+
nextBestIdx = idx;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
selected.push(nextBestIdx);
|
|
122
|
+
remaining.delete(nextBestIdx);
|
|
123
|
+
adjustedScores.set(nextBestIdx, nextBestScore);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Rebuild output: non-items first (original order), then items in selected order
|
|
127
|
+
const result: TieredCandidate[] = [...nonItems];
|
|
128
|
+
for (const idx of selected) {
|
|
129
|
+
result.push({
|
|
130
|
+
...items[idx]!.candidate,
|
|
131
|
+
finalScore: adjustedScores.get(idx)!,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { TieredCandidate } from "./tier-classifier.js";
|
|
2
1
|
import type { StalenessLevel } from "./types.js";
|
|
3
2
|
|
|
4
3
|
const BASE_LIFETIME_MS: Record<string, number> = {
|
|
@@ -39,17 +38,3 @@ export function computeStaleness(
|
|
|
39
38
|
if (ratio <= 2) return { level: "stale", ratio };
|
|
40
39
|
return { level: "very_stale", ratio };
|
|
41
40
|
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Demote very_stale tier-1 candidates to tier 2.
|
|
45
|
-
*/
|
|
46
|
-
export function applyStaleDemotion(
|
|
47
|
-
candidates: TieredCandidate[],
|
|
48
|
-
): TieredCandidate[] {
|
|
49
|
-
return candidates.map((c) => {
|
|
50
|
-
if (c.tier === 1 && c.staleness === "very_stale") {
|
|
51
|
-
return { ...c, tier: 2 as const };
|
|
52
|
-
}
|
|
53
|
-
return c;
|
|
54
|
-
});
|
|
55
|
-
}
|
|
@@ -1,29 +1,18 @@
|
|
|
1
1
|
import type { Candidate } from "./types.js";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export interface TieredCandidate extends Candidate {
|
|
6
|
-
tier: Tier;
|
|
3
|
+
/** Backward-compatible alias — downstream files import this type. */
|
|
4
|
+
export type TieredCandidate = Candidate & {
|
|
7
5
|
/** Human-readable label for the source conversation/summary (e.g. conversation title). */
|
|
8
6
|
sourceLabel?: string;
|
|
9
|
-
}
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const MIN_SCORE_THRESHOLD = 0.2;
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* the multiplicative scoring pipeline (semantic × recency × metadata) compresses
|
|
16
|
-
* the effective score range. Lowering the gates lets moderately-relevant items
|
|
17
|
-
* surface rather than being silently dropped.
|
|
12
|
+
* Filter candidates to those exceeding the minimum relevance threshold.
|
|
13
|
+
* Replaces the former tier 1/tier 2 classification — all surviving candidates
|
|
14
|
+
* are treated equally and ranked by score.
|
|
18
15
|
*/
|
|
19
|
-
export function
|
|
20
|
-
|
|
21
|
-
if (score > 0.4) return 2;
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function classifyTiers(candidates: Candidate[]): TieredCandidate[] {
|
|
26
|
-
return candidates
|
|
27
|
-
.map((c) => ({ ...c, tier: classifyTier(c.finalScore) }))
|
|
28
|
-
.filter((c): c is TieredCandidate => c.tier != null);
|
|
16
|
+
export function filterByMinScore(candidates: Candidate[]): TieredCandidate[] {
|
|
17
|
+
return candidates.filter((c) => c.finalScore > MIN_SCORE_THRESHOLD);
|
|
29
18
|
}
|
|
@@ -15,6 +15,10 @@ export interface Candidate {
|
|
|
15
15
|
conversationId?: string;
|
|
16
16
|
/** The source message ID this candidate was extracted from (segments only). */
|
|
17
17
|
messageId?: string;
|
|
18
|
+
/** The ID of the memory item this candidate supersedes (items only). */
|
|
19
|
+
supersedes?: string;
|
|
20
|
+
/** Relative workspace path to the source conversation's messages file. */
|
|
21
|
+
sourcePath?: string;
|
|
18
22
|
confidence: number;
|
|
19
23
|
importance: number;
|
|
20
24
|
createdAt: number;
|
|
@@ -32,6 +36,7 @@ export interface MemoryRecallCandiateDebug {
|
|
|
32
36
|
finalScore: number;
|
|
33
37
|
semantic: number;
|
|
34
38
|
recency: number;
|
|
39
|
+
sourceLabel?: string;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
export type DegradationReason =
|
|
@@ -67,6 +72,12 @@ export interface MemoryRecallResult {
|
|
|
67
72
|
hybridSearchMs?: number;
|
|
68
73
|
/** Whether sparse vectors were used in the hybrid search. */
|
|
69
74
|
sparseVectorUsed?: boolean;
|
|
75
|
+
/** Whether HyDE query expansion was used for this retrieval. */
|
|
76
|
+
hydeExpanded?: boolean;
|
|
77
|
+
/** Number of hypothetical documents generated by HyDE expansion. */
|
|
78
|
+
hydeDocCount?: number;
|
|
79
|
+
/** Whether MMR diversity ranking was applied to candidates. */
|
|
80
|
+
mmrApplied?: boolean;
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
/**
|
|
@@ -94,6 +105,12 @@ export interface MemoryRecallOptions {
|
|
|
94
105
|
*/
|
|
95
106
|
scopePolicyOverride?: ScopePolicyOverride;
|
|
96
107
|
maxInjectTokensOverride?: number;
|
|
108
|
+
/**
|
|
109
|
+
* When true, use HyDE (Hypothetical Document Embeddings) query expansion
|
|
110
|
+
* to generate hypothetical memory documents, embed them alongside the raw
|
|
111
|
+
* query, and run parallel semantic searches for improved recall.
|
|
112
|
+
*/
|
|
113
|
+
hydeEnabled?: boolean;
|
|
97
114
|
}
|
|
98
115
|
|
|
99
116
|
export interface ItemMetadata {
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* implements the MessagingProvider interface.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { findContactChannel } from "../../../contacts/contact-store.js";
|
|
9
|
+
import { upsertContactChannel } from "../../../contacts/contacts-write.js";
|
|
8
10
|
import type { OAuthConnection } from "../../../oauth/connection.js";
|
|
9
11
|
import { resolveOAuthConnection } from "../../../oauth/connection-resolver.js";
|
|
10
12
|
import { isProviderConnected } from "../../../oauth/oauth-store.js";
|
|
@@ -62,6 +64,21 @@ async function resolveUserName(
|
|
|
62
64
|
const cached = userNameCache.get(userId);
|
|
63
65
|
if (cached) return cached;
|
|
64
66
|
|
|
67
|
+
// Check contacts DB for a persistent cache hit
|
|
68
|
+
try {
|
|
69
|
+
const result = findContactChannel({
|
|
70
|
+
channelType: "slack",
|
|
71
|
+
externalUserId: userId,
|
|
72
|
+
});
|
|
73
|
+
if (result) {
|
|
74
|
+
const name = result.contact.displayName;
|
|
75
|
+
userNameCache.set(userId, name);
|
|
76
|
+
return name;
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
// Contact lookup failures are non-fatal — fall through to API
|
|
80
|
+
}
|
|
81
|
+
|
|
65
82
|
try {
|
|
66
83
|
const resp = await slack.userInfo(auth, userId);
|
|
67
84
|
const name =
|
|
@@ -70,6 +87,18 @@ async function resolveUserName(
|
|
|
70
87
|
resp.user.real_name ||
|
|
71
88
|
resp.user.name;
|
|
72
89
|
userNameCache.set(userId, name);
|
|
90
|
+
|
|
91
|
+
// Persist to contacts for future sessions
|
|
92
|
+
try {
|
|
93
|
+
upsertContactChannel({
|
|
94
|
+
sourceChannel: "slack",
|
|
95
|
+
externalUserId: userId,
|
|
96
|
+
displayName: name,
|
|
97
|
+
});
|
|
98
|
+
} catch {
|
|
99
|
+
// Non-fatal — caching failure shouldn't break messaging
|
|
100
|
+
}
|
|
101
|
+
|
|
73
102
|
return name;
|
|
74
103
|
} catch {
|
|
75
104
|
return userId;
|
|
@@ -216,13 +245,30 @@ export const slackProvider: MessagingProvider = {
|
|
|
216
245
|
(!options?.limit || conversations.length < options.limit)
|
|
217
246
|
);
|
|
218
247
|
|
|
219
|
-
// Resolve DM user names
|
|
248
|
+
// Resolve DM user names and cache channel mappings
|
|
220
249
|
for (const conv of conversations) {
|
|
221
250
|
if (conv.type === "dm" && conv.metadata?.dmUserId) {
|
|
222
|
-
conv.
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
251
|
+
const dmUserId = conv.metadata.dmUserId as string;
|
|
252
|
+
conv.name = await resolveUserName(auth, dmUserId);
|
|
253
|
+
|
|
254
|
+
// Persist the DM channel ID so future sends skip conversations.open
|
|
255
|
+
try {
|
|
256
|
+
const existing = findContactChannel({
|
|
257
|
+
channelType: "slack",
|
|
258
|
+
externalUserId: dmUserId,
|
|
259
|
+
});
|
|
260
|
+
if (existing && !existing.channel.externalChatId) {
|
|
261
|
+
upsertContactChannel({
|
|
262
|
+
contactId: existing.contact.id,
|
|
263
|
+
sourceChannel: "slack",
|
|
264
|
+
externalUserId: dmUserId,
|
|
265
|
+
externalChatId: conv.id,
|
|
266
|
+
displayName: conv.name,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
} catch {
|
|
270
|
+
// Non-fatal
|
|
271
|
+
}
|
|
226
272
|
}
|
|
227
273
|
}
|
|
228
274
|
|
|
@@ -147,6 +147,19 @@ export class NotificationBroadcaster {
|
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
// For tool_grant_request signals, prefer the deterministic template seed
|
|
151
|
+
// over LLM-generated prose. The enriched questionText is already concise
|
|
152
|
+
// and informative — LLM rewording just adds noise.
|
|
153
|
+
if (signal.contextPayload?.requestKind === "tool_grant_request") {
|
|
154
|
+
if (!fallbackCopy) {
|
|
155
|
+
fallbackCopy = composeFallbackCopy(signal, decision.selectedChannels);
|
|
156
|
+
}
|
|
157
|
+
const templateSeed = fallbackCopy[channel]?.conversationSeedMessage;
|
|
158
|
+
if (templateSeed) {
|
|
159
|
+
copy = { ...copy, conversationSeedMessage: templateSeed };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
150
163
|
// Resolve the per-channel conversation action from the decision (default: start_new)
|
|
151
164
|
const conversationAction: ConversationAction | undefined =
|
|
152
165
|
decision.conversationActions?.[channel];
|
|
@@ -326,10 +326,17 @@ const TEMPLATES: Partial<Record<NotificationSourceEventName, CopyTemplate>> = {
|
|
|
326
326
|
const requestCode = nonEmpty(
|
|
327
327
|
typeof payload.requestCode === "string" ? payload.requestCode : undefined,
|
|
328
328
|
);
|
|
329
|
+
|
|
330
|
+
// For tool_grant_request, the questionText already includes requester name + input summary.
|
|
331
|
+
// Use it directly as the conversation seed to avoid LLM-generated filler.
|
|
332
|
+
const isToolGrant = payload.requestKind === "tool_grant_request";
|
|
333
|
+
const conversationSeedMessage = isToolGrant ? question : undefined;
|
|
334
|
+
|
|
329
335
|
if (!requestCode) {
|
|
330
336
|
return {
|
|
331
337
|
title: "Guardian Question",
|
|
332
338
|
body: question,
|
|
339
|
+
conversationSeedMessage,
|
|
333
340
|
};
|
|
334
341
|
}
|
|
335
342
|
|
|
@@ -342,6 +349,7 @@ const TEMPLATES: Partial<Record<NotificationSourceEventName, CopyTemplate>> = {
|
|
|
342
349
|
return {
|
|
343
350
|
title: "Guardian Question",
|
|
344
351
|
body: `${question}\n\n${instruction}`,
|
|
352
|
+
conversationSeedMessage,
|
|
345
353
|
};
|
|
346
354
|
},
|
|
347
355
|
|
|
@@ -229,7 +229,7 @@ export async function orchestrateOAuthConnect(
|
|
|
229
229
|
return {
|
|
230
230
|
success: false,
|
|
231
231
|
error:
|
|
232
|
-
"
|
|
232
|
+
"OAuth connect from a non-interactive session requires a public ingress URL. Configure ingress.publicBaseUrl first.",
|
|
233
233
|
safeError: true,
|
|
234
234
|
};
|
|
235
235
|
}
|
|
@@ -88,7 +88,7 @@ export async function resolveOAuthConnection(
|
|
|
88
88
|
? ` matching ${filters.join(" and ")}`
|
|
89
89
|
: "";
|
|
90
90
|
throw new Error(
|
|
91
|
-
`No active OAuth connection found for "${providerKey}"${qualifier}. Connect the service first with
|
|
91
|
+
`No active OAuth connection found for "${providerKey}"${qualifier}. Connect the service first with \`assistant oauth connect ${providerKey}\`.`,
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -97,7 +97,7 @@ export async function resolveOAuthConnection(
|
|
|
97
97
|
);
|
|
98
98
|
if (!accessToken) {
|
|
99
99
|
throw new Error(
|
|
100
|
-
`OAuth connection for "${providerKey}" exists but has no access token. Re-authorize with
|
|
100
|
+
`OAuth connection for "${providerKey}" exists but has no access token. Re-authorize with \`assistant oauth connect ${providerKey}\`.`,
|
|
101
101
|
);
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared serialization utilities for OAuth provider rows.
|
|
3
|
+
*
|
|
4
|
+
* Used by both the CLI (providers commands) and runtime API endpoints to
|
|
5
|
+
* produce consistent, parsed representations of provider rows stored in
|
|
6
|
+
* the database.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { OAuthProviderRow } from "./oauth-store.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Full serialized representation of an OAuth provider row.
|
|
13
|
+
*
|
|
14
|
+
* JSON string fields are parsed into their native types, boolean/integer
|
|
15
|
+
* fields are normalised to booleans, timestamps are ISO 8601 strings,
|
|
16
|
+
* and a computed `supportsManagedMode` flag is included.
|
|
17
|
+
*/
|
|
18
|
+
export type SerializedProvider = ReturnType<typeof serializeProvider> &
|
|
19
|
+
Record<string, unknown>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Lightweight summary projection of an OAuth provider, suitable for API
|
|
23
|
+
* list responses where full detail is not needed. All keys are snake_case
|
|
24
|
+
* to match the HTTP API convention.
|
|
25
|
+
*/
|
|
26
|
+
export interface SerializedProviderSummary {
|
|
27
|
+
provider_key: string;
|
|
28
|
+
display_name: string | null;
|
|
29
|
+
description: string | null;
|
|
30
|
+
dashboard_url: string | null;
|
|
31
|
+
client_id_placeholder: string | null;
|
|
32
|
+
requires_client_secret: boolean;
|
|
33
|
+
supports_managed_mode: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Serialize a full provider row from the database into a parsed object.
|
|
38
|
+
*
|
|
39
|
+
* JSON string columns are parsed, boolean/integer fields are normalised to
|
|
40
|
+
* booleans, and timestamps are converted to ISO 8601 strings. An optional
|
|
41
|
+
* `redirectUri` override can be supplied by the caller (e.g. the CLI,
|
|
42
|
+
* which resolves the redirect URI from config).
|
|
43
|
+
*
|
|
44
|
+
* Returns `undefined` when `row` is `undefined`, and `null` when `row` is
|
|
45
|
+
* `null`, preserving the caller's nullable semantics.
|
|
46
|
+
*/
|
|
47
|
+
export function serializeProvider(
|
|
48
|
+
row: OAuthProviderRow | null | undefined,
|
|
49
|
+
options?: { redirectUri?: string | null },
|
|
50
|
+
): ReturnType<typeof _serializeProvider> | null | undefined {
|
|
51
|
+
if (row === undefined) return undefined;
|
|
52
|
+
if (row === null) return null;
|
|
53
|
+
return _serializeProvider(row, options);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function _serializeProvider(
|
|
57
|
+
row: OAuthProviderRow,
|
|
58
|
+
options?: { redirectUri?: string | null },
|
|
59
|
+
) {
|
|
60
|
+
return {
|
|
61
|
+
...row,
|
|
62
|
+
displayName: row.displayName ?? null,
|
|
63
|
+
description: row.description ?? null,
|
|
64
|
+
dashboardUrl: row.dashboardUrl ?? null,
|
|
65
|
+
clientIdPlaceholder: row.clientIdPlaceholder ?? null,
|
|
66
|
+
requiresClientSecret: !!(row.requiresClientSecret ?? 1),
|
|
67
|
+
supportsManagedMode: !!row.managedServiceConfigKey,
|
|
68
|
+
defaultScopes: row.defaultScopes ? JSON.parse(row.defaultScopes) : [],
|
|
69
|
+
scopePolicy: row.scopePolicy ? JSON.parse(row.scopePolicy) : {},
|
|
70
|
+
extraParams: row.extraParams ? JSON.parse(row.extraParams) : null,
|
|
71
|
+
pingHeaders: row.pingHeaders ? JSON.parse(row.pingHeaders) : null,
|
|
72
|
+
pingBody: row.pingBody ? JSON.parse(row.pingBody) : null,
|
|
73
|
+
loopbackPort: row.loopbackPort ?? null,
|
|
74
|
+
injectionTemplates: row.injectionTemplates
|
|
75
|
+
? JSON.parse(row.injectionTemplates)
|
|
76
|
+
: null,
|
|
77
|
+
appType: row.appType ?? null,
|
|
78
|
+
setupNotes: row.setupNotes ? JSON.parse(row.setupNotes) : null,
|
|
79
|
+
identityUrl: row.identityUrl ?? null,
|
|
80
|
+
identityMethod: row.identityMethod ?? null,
|
|
81
|
+
identityHeaders: row.identityHeaders
|
|
82
|
+
? JSON.parse(row.identityHeaders)
|
|
83
|
+
: null,
|
|
84
|
+
identityBody: row.identityBody ? JSON.parse(row.identityBody) : null,
|
|
85
|
+
identityResponsePaths: row.identityResponsePaths
|
|
86
|
+
? JSON.parse(row.identityResponsePaths)
|
|
87
|
+
: null,
|
|
88
|
+
identityFormat: row.identityFormat ?? null,
|
|
89
|
+
identityOkField: row.identityOkField ?? null,
|
|
90
|
+
redirectUri:
|
|
91
|
+
options?.redirectUri !== undefined ? options.redirectUri : null,
|
|
92
|
+
createdAt: new Date(row.createdAt).toISOString(),
|
|
93
|
+
updatedAt: new Date(row.updatedAt).toISOString(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Return a lightweight snake_case summary of a provider row, suitable for
|
|
99
|
+
* embedding in API list responses.
|
|
100
|
+
*
|
|
101
|
+
* Returns `null` when `row` is nullish.
|
|
102
|
+
*/
|
|
103
|
+
export function serializeProviderSummary(
|
|
104
|
+
row: OAuthProviderRow | null | undefined,
|
|
105
|
+
): SerializedProviderSummary | null {
|
|
106
|
+
if (!row) return null;
|
|
107
|
+
return {
|
|
108
|
+
provider_key: row.providerKey,
|
|
109
|
+
display_name: row.displayName ?? null,
|
|
110
|
+
description: row.description ?? null,
|
|
111
|
+
dashboard_url: row.dashboardUrl ?? null,
|
|
112
|
+
client_id_placeholder: row.clientIdPlaceholder ?? null,
|
|
113
|
+
requires_client_secret: !!(row.requiresClientSecret ?? 1),
|
|
114
|
+
supports_managed_mode: !!row.managedServiceConfigKey,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
renameSync,
|
|
7
7
|
writeFileSync,
|
|
8
8
|
} from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
9
10
|
import { dirname, join } from "node:path";
|
|
10
11
|
|
|
11
12
|
import { Minimatch } from "minimatch";
|
|
@@ -13,7 +14,6 @@ import { v4 as uuid } from "uuid";
|
|
|
13
14
|
|
|
14
15
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
15
16
|
import { getLogger } from "../util/logger.js";
|
|
16
|
-
import { getRootDir } from "../util/platform.js";
|
|
17
17
|
import { getDefaultRuleTemplates } from "./defaults.js";
|
|
18
18
|
import * as trustClient from "./trust-client.js";
|
|
19
19
|
import type {
|
|
@@ -128,7 +128,19 @@ function rebuildPatternCache(rules: TrustRule[]): void {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
function getTrustPath(): string {
|
|
131
|
-
return join(
|
|
131
|
+
return join(getGatewaySecurityDir(), "trust.json");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Resolve the gateway security directory.
|
|
136
|
+
*
|
|
137
|
+
* Docker: `GATEWAY_SECURITY_DIR` env var.
|
|
138
|
+
* Local: falls back to `~/.vellum/` + `protected/`.
|
|
139
|
+
*/
|
|
140
|
+
function getGatewaySecurityDir(): string {
|
|
141
|
+
const securityDir = process.env.GATEWAY_SECURITY_DIR;
|
|
142
|
+
if (securityDir) return securityDir;
|
|
143
|
+
return join(homedir(), ".vellum", "protected");
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
/**
|
|
@@ -1103,11 +1115,9 @@ function getGatewayTrustStore(): GatewayTrustStoreAdapter {
|
|
|
1103
1115
|
* Returns the active trust store backend.
|
|
1104
1116
|
*
|
|
1105
1117
|
* When `IS_CONTAINERIZED=true`, returns a gateway-backed adapter that
|
|
1106
|
-
* proxies all trust operations through the gateway HTTP API.
|
|
1107
|
-
* never reads or writes `protected/trust.json` directly in Docker.
|
|
1118
|
+
* proxies all trust operations through the gateway HTTP API.
|
|
1108
1119
|
*
|
|
1109
|
-
* When `IS_CONTAINERIZED=false`, returns the file-based implementation
|
|
1110
|
-
* (no change from previous behavior).
|
|
1120
|
+
* When `IS_CONTAINERIZED=false`, returns the file-based implementation.
|
|
1111
1121
|
*/
|
|
1112
1122
|
export function getTrustStore(): TrustStoreBackend {
|
|
1113
1123
|
if (getIsContainerized()) {
|
|
@@ -1133,7 +1143,14 @@ export function addRule(
|
|
|
1133
1143
|
executionTarget?: string;
|
|
1134
1144
|
},
|
|
1135
1145
|
): TrustRule {
|
|
1136
|
-
return getTrustStore().addRule(
|
|
1146
|
+
return getTrustStore().addRule(
|
|
1147
|
+
tool,
|
|
1148
|
+
pattern,
|
|
1149
|
+
scope,
|
|
1150
|
+
decision,
|
|
1151
|
+
priority,
|
|
1152
|
+
options,
|
|
1153
|
+
);
|
|
1137
1154
|
}
|
|
1138
1155
|
|
|
1139
1156
|
export function updateRule(
|
|
@@ -23,6 +23,11 @@ describe("buildCliReferenceSection", () => {
|
|
|
23
23
|
expect(result).toContain("assistant --help");
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
test("mentions assistant platform for querying platform state", () => {
|
|
27
|
+
const result = buildCliReferenceSection();
|
|
28
|
+
expect(result).toContain("assistant platform status");
|
|
29
|
+
});
|
|
30
|
+
|
|
26
31
|
test("does not embed the full CLI help output", () => {
|
|
27
32
|
const result = buildCliReferenceSection();
|
|
28
33
|
// The full help text used to be embedded; now the model should
|