@vellumai/assistant 0.5.12 → 0.5.14
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/Dockerfile +41 -9
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/node_modules/@vellumai/ces-contracts/src/index.ts +7 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +5 -0
- 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 +11 -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__/navigate-settings-tab.test.ts +6 -2
- 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 +62 -251
- 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-feature-flags.test.ts +8 -0
- 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__/skill-secret-handling-guard.test.ts +212 -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__/token-estimator-accuracy.benchmark.test.ts +1 -1
- 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 +250 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +290 -0
- package/src/cli/commands/platform/__tests__/status.test.ts +282 -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/settings/TOOLS.json +5 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +4 -2
- 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 +17 -1
- 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/client.ts +14 -2
- 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/first-greeting.ts +6 -1
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +18 -15
- 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/index.ts +0 -12
- 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/conversation-queries.ts +6 -6
- 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/journal-memory.ts +8 -2
- 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 +54 -36
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +38 -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 +10 -29
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/rebind-secrets-screen.ts +2 -2
- 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/secret-routes.ts +9 -2
- 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/browser-manager.ts +2 -2
- 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 +72 -124
- 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
|
@@ -3,25 +3,16 @@ import {
|
|
|
3
3
|
setSlackChannelConfig,
|
|
4
4
|
type SlackChannelConfigResult,
|
|
5
5
|
} from "../../daemon/handlers/config-slack-channel.js";
|
|
6
|
-
import { orchestrateOAuthConnect } from "../../oauth/connect-orchestrator.js";
|
|
7
6
|
import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
|
|
8
7
|
import {
|
|
9
8
|
disconnectOAuthProvider,
|
|
10
9
|
getActiveConnection,
|
|
11
|
-
getAppByProviderAndClientId,
|
|
12
|
-
getMostRecentAppByProvider,
|
|
13
|
-
getProvider,
|
|
14
|
-
listProviders,
|
|
15
10
|
} from "../../oauth/oauth-store.js";
|
|
16
11
|
import { RiskLevel } from "../../permissions/types.js";
|
|
17
12
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
18
|
-
import { buildAssistantEvent } from "../../runtime/assistant-event.js";
|
|
19
|
-
import { assistantEventHub } from "../../runtime/assistant-event-hub.js";
|
|
20
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../runtime/assistant-scope.js";
|
|
21
13
|
import { credentialKey } from "../../security/credential-key.js";
|
|
22
14
|
import {
|
|
23
15
|
deleteSecureKeyAsync,
|
|
24
|
-
getSecureKeyAsync,
|
|
25
16
|
listSecureKeysAsync,
|
|
26
17
|
setSecureKeyAsync,
|
|
27
18
|
} from "../../security/secure-keys.js";
|
|
@@ -90,16 +81,9 @@ class CredentialStoreTool implements Tool {
|
|
|
90
81
|
properties: {
|
|
91
82
|
action: {
|
|
92
83
|
type: "string",
|
|
93
|
-
enum: [
|
|
94
|
-
"store",
|
|
95
|
-
"list",
|
|
96
|
-
"delete",
|
|
97
|
-
"prompt",
|
|
98
|
-
"oauth2_connect",
|
|
99
|
-
"describe",
|
|
100
|
-
],
|
|
84
|
+
enum: ["store", "list", "delete", "prompt"],
|
|
101
85
|
description:
|
|
102
|
-
'The operation to perform. Use "prompt" to ask the user for a secret via secure UI - the value never enters the conversation.
|
|
86
|
+
'The operation to perform. Use "prompt" to ask the user for a secret via secure UI - the value never enters the conversation.',
|
|
103
87
|
},
|
|
104
88
|
service: {
|
|
105
89
|
type: "string",
|
|
@@ -150,22 +134,6 @@ class CredentialStoreTool implements Tool {
|
|
|
150
134
|
description:
|
|
151
135
|
'Human-readable description of intended usage (for store/prompt actions), e.g. "GitHub login for pushing changes"',
|
|
152
136
|
},
|
|
153
|
-
scopes: {
|
|
154
|
-
type: "array",
|
|
155
|
-
items: { type: "string" },
|
|
156
|
-
description:
|
|
157
|
-
"OAuth2 scopes to request (only for oauth2_connect action). Auto-filled for well-known services (google, slack).",
|
|
158
|
-
},
|
|
159
|
-
client_id: {
|
|
160
|
-
type: "string",
|
|
161
|
-
description:
|
|
162
|
-
"OAuth2 client ID (only for oauth2_connect action). If omitted, looked up from previously stored credentials.",
|
|
163
|
-
},
|
|
164
|
-
client_secret: {
|
|
165
|
-
type: "string",
|
|
166
|
-
description:
|
|
167
|
-
"OAuth2 client secret for providers that require it (e.g. Google, Slack). If omitted, looked up from previously stored credentials; if still absent, PKCE-only is used (only for oauth2_connect action)",
|
|
168
|
-
},
|
|
169
137
|
alias: {
|
|
170
138
|
type: "string",
|
|
171
139
|
description:
|
|
@@ -817,221 +785,6 @@ class CredentialStoreTool implements Tool {
|
|
|
817
785
|
};
|
|
818
786
|
}
|
|
819
787
|
|
|
820
|
-
case "oauth2_connect": {
|
|
821
|
-
const service = input.service as string | undefined;
|
|
822
|
-
if (!service)
|
|
823
|
-
return {
|
|
824
|
-
content: "Error: service is required for oauth2_connect action",
|
|
825
|
-
isError: true,
|
|
826
|
-
};
|
|
827
|
-
|
|
828
|
-
// Protocol-level config from the DB (authUrl, tokenUrl, scopes, etc.)
|
|
829
|
-
const providerRow = getProvider(service);
|
|
830
|
-
|
|
831
|
-
// Resolve client_id and client_secret.
|
|
832
|
-
// Priority:
|
|
833
|
-
// 1. Explicit input from the caller
|
|
834
|
-
// 2. oauth-store DB - when clientId is already known, look up the
|
|
835
|
-
// matching app so the secret comes from the same app. Only fall
|
|
836
|
-
// back to the most-recent-app heuristic when clientId is unknown.
|
|
837
|
-
let clientId = input.client_id as string | undefined;
|
|
838
|
-
let clientSecret = input.client_secret as string | undefined;
|
|
839
|
-
|
|
840
|
-
if (!clientId || !clientSecret) {
|
|
841
|
-
const dbApp = clientId
|
|
842
|
-
? getAppByProviderAndClientId(service, clientId)
|
|
843
|
-
: getMostRecentAppByProvider(service);
|
|
844
|
-
if (dbApp) {
|
|
845
|
-
if (!clientId) clientId = dbApp.clientId;
|
|
846
|
-
if (!clientSecret) {
|
|
847
|
-
clientSecret = await getSecureKeyAsync(
|
|
848
|
-
dbApp.clientSecretCredentialPath,
|
|
849
|
-
);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
// Early guardrails that stay in vault.ts (credential resolution is vault-specific)
|
|
855
|
-
const inputScopes = input.scopes as string[] | undefined;
|
|
856
|
-
|
|
857
|
-
if (!providerRow) {
|
|
858
|
-
return {
|
|
859
|
-
content: `Error: no OAuth provider registered for "${service}". Ensure the provider is seeded in the database.`,
|
|
860
|
-
isError: true,
|
|
861
|
-
};
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
if (!clientId)
|
|
865
|
-
return {
|
|
866
|
-
content:
|
|
867
|
-
"Error: client_id is required for oauth2_connect action. Provide it directly or store it first with credential_store.",
|
|
868
|
-
isError: true,
|
|
869
|
-
};
|
|
870
|
-
|
|
871
|
-
// Fail early when client_secret is required but missing - guide the
|
|
872
|
-
// agent to collect it from the user rather than letting it improvise
|
|
873
|
-
// browser-automation workarounds that inevitably fail.
|
|
874
|
-
const requiresSecret = !!providerRow.requiresClientSecret;
|
|
875
|
-
if (requiresSecret && !clientSecret) {
|
|
876
|
-
return {
|
|
877
|
-
content: `Error: client_secret is required for ${service} but not found in the vault.\n\nUse credential_store with action "prompt" to securely collect the client_secret from the user before calling oauth2_connect again.`,
|
|
878
|
-
isError: true,
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// Delegate to the shared orchestrator - it resolves authUrl, tokenUrl,
|
|
883
|
-
// extraParams, userinfoUrl, and tokenEndpointAuthMethod from the DB.
|
|
884
|
-
const result = await orchestrateOAuthConnect({
|
|
885
|
-
service,
|
|
886
|
-
clientId,
|
|
887
|
-
clientSecret,
|
|
888
|
-
isInteractive: !!context.isInteractive,
|
|
889
|
-
sendToClient: context.sendToClient,
|
|
890
|
-
...(inputScopes ? { requestedScopes: inputScopes } : {}),
|
|
891
|
-
onDeferredComplete: (deferredResult) => {
|
|
892
|
-
// Emit oauth_connect_result to all connected SSE clients so the
|
|
893
|
-
// UI can update immediately when the deferred browser flow completes.
|
|
894
|
-
assistantEventHub
|
|
895
|
-
.publish(
|
|
896
|
-
buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
|
|
897
|
-
type: "oauth_connect_result",
|
|
898
|
-
success: deferredResult.success,
|
|
899
|
-
service: deferredResult.service,
|
|
900
|
-
accountInfo: deferredResult.accountInfo,
|
|
901
|
-
error: deferredResult.error,
|
|
902
|
-
}),
|
|
903
|
-
)
|
|
904
|
-
.catch((err) => {
|
|
905
|
-
log.warn(
|
|
906
|
-
{ err, service: deferredResult.service },
|
|
907
|
-
"Failed to publish oauth_connect_result event",
|
|
908
|
-
);
|
|
909
|
-
});
|
|
910
|
-
|
|
911
|
-
if (deferredResult.success) {
|
|
912
|
-
log.info(
|
|
913
|
-
{
|
|
914
|
-
service: deferredResult.service,
|
|
915
|
-
accountInfo: deferredResult.accountInfo,
|
|
916
|
-
},
|
|
917
|
-
"Deferred OAuth connect completed successfully",
|
|
918
|
-
);
|
|
919
|
-
} else {
|
|
920
|
-
log.warn(
|
|
921
|
-
{
|
|
922
|
-
service: deferredResult.service,
|
|
923
|
-
err: deferredResult.error,
|
|
924
|
-
},
|
|
925
|
-
"Deferred OAuth connect failed",
|
|
926
|
-
);
|
|
927
|
-
}
|
|
928
|
-
},
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
if (!result.success) {
|
|
932
|
-
return { content: `Error: ${result.error}`, isError: true };
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
if (result.deferred) {
|
|
936
|
-
return {
|
|
937
|
-
content: `To connect ${service}, open this link and authorize access:\n\n${result.authUrl}\n\nOnce you authorize, the connection will be set up automatically. You can verify by asking me to check your inbox.`,
|
|
938
|
-
isError: false,
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
return {
|
|
943
|
-
content: `Successfully connected "${service}"${
|
|
944
|
-
result.accountInfo ? ` as ${result.accountInfo}` : ""
|
|
945
|
-
}. The service is now ready to use.`,
|
|
946
|
-
isError: false,
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
case "describe": {
|
|
951
|
-
const descService = (input.service as string | undefined) ?? "";
|
|
952
|
-
if (!descService) {
|
|
953
|
-
return {
|
|
954
|
-
content: "Error: service is required for describe action",
|
|
955
|
-
isError: true,
|
|
956
|
-
};
|
|
957
|
-
}
|
|
958
|
-
const descProviderRow = getProvider(descService);
|
|
959
|
-
if (!descProviderRow) {
|
|
960
|
-
const availableServices = listProviders().map((p) => p.providerKey);
|
|
961
|
-
return {
|
|
962
|
-
content: `No well-known OAuth config found for "${descService}". Available services: ${availableServices.join(", ")}`,
|
|
963
|
-
isError: false,
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// Compute the redirect URI based on callback transport
|
|
968
|
-
let redirectUri: string;
|
|
969
|
-
const transport =
|
|
970
|
-
(descProviderRow.callbackTransport as
|
|
971
|
-
| "loopback"
|
|
972
|
-
| "gateway"
|
|
973
|
-
| null) ?? "loopback";
|
|
974
|
-
const loopbackPort = descProviderRow.loopbackPort;
|
|
975
|
-
if (transport === "loopback" && loopbackPort) {
|
|
976
|
-
redirectUri = `http://localhost:${loopbackPort}/oauth/callback`;
|
|
977
|
-
} else if (transport === "loopback") {
|
|
978
|
-
redirectUri =
|
|
979
|
-
"(automatic - no redirect URI needed, uses random localhost port)";
|
|
980
|
-
} else {
|
|
981
|
-
// Try to compute the actual URL from config/env
|
|
982
|
-
try {
|
|
983
|
-
const { loadConfig } = await import("../../config/loader.js");
|
|
984
|
-
const { getPublicBaseUrl } =
|
|
985
|
-
await import("../../inbound/public-ingress-urls.js");
|
|
986
|
-
const baseUrl = getPublicBaseUrl(loadConfig());
|
|
987
|
-
redirectUri = `${baseUrl}/webhooks/oauth/callback`;
|
|
988
|
-
} catch {
|
|
989
|
-
redirectUri =
|
|
990
|
-
"(requires ingress.publicBaseUrl - not currently configured)";
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
const requiresClientSecret = !!descProviderRow.requiresClientSecret;
|
|
995
|
-
|
|
996
|
-
const descDefaultScopes: string[] = descProviderRow.defaultScopes
|
|
997
|
-
? JSON.parse(descProviderRow.defaultScopes)
|
|
998
|
-
: [];
|
|
999
|
-
|
|
1000
|
-
const info: Record<string, unknown> = {
|
|
1001
|
-
service: descService,
|
|
1002
|
-
authUrl: descProviderRow.authUrl,
|
|
1003
|
-
tokenUrl: descProviderRow.tokenUrl,
|
|
1004
|
-
scopes: descDefaultScopes,
|
|
1005
|
-
callbackTransport: transport,
|
|
1006
|
-
redirectUri,
|
|
1007
|
-
requiresClientSecret,
|
|
1008
|
-
};
|
|
1009
|
-
if (
|
|
1010
|
-
descProviderRow.displayName &&
|
|
1011
|
-
descProviderRow.dashboardUrl &&
|
|
1012
|
-
descProviderRow.appType
|
|
1013
|
-
) {
|
|
1014
|
-
info.setup = {
|
|
1015
|
-
displayName: descProviderRow.displayName,
|
|
1016
|
-
dashboardUrl: descProviderRow.dashboardUrl,
|
|
1017
|
-
appType: descProviderRow.appType,
|
|
1018
|
-
requiresClientSecret: !!descProviderRow.requiresClientSecret,
|
|
1019
|
-
...(descProviderRow.setupNotes
|
|
1020
|
-
? { notes: JSON.parse(descProviderRow.setupNotes) }
|
|
1021
|
-
: {}),
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
1024
|
-
if (descProviderRow.extraParams) {
|
|
1025
|
-
try {
|
|
1026
|
-
info.extraParams = JSON.parse(descProviderRow.extraParams);
|
|
1027
|
-
} catch {
|
|
1028
|
-
// Non-fatal
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
return { content: JSON.stringify(info, null, 2), isError: false };
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
788
|
default:
|
|
1036
789
|
return { content: `Error: unknown action "${action}"`, isError: true };
|
|
1037
790
|
}
|
|
@@ -3,7 +3,7 @@ import type { ToolDefinition } from "../../providers/types.js";
|
|
|
3
3
|
export const memoryRecallDefinition: ToolDefinition = {
|
|
4
4
|
name: "memory_recall",
|
|
5
5
|
description:
|
|
6
|
-
"Semantic search across memory for specific information. Relevant memories are auto-injected each turn, so only call this when the auto-injected context doesn't contain what you need - e.g. the user references a past session, or you need deeper recall. Be specific in your query for best results. Returns formatted memory context with item IDs for use with memory_manage.",
|
|
6
|
+
"Semantic search across memory for specific information. Relevant memories are auto-injected each turn, so only call this when the auto-injected context doesn't contain what you need - e.g. the user references a past session, or you need deeper recall. Be specific in your query for best results. Returns formatted memory context with item IDs for use with memory_manage. Results include source conversation titles so you can identify which conversation a memory originated from.",
|
|
7
7
|
input_schema: {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
@@ -375,9 +375,9 @@ describe("handleMemoryRecall", () => {
|
|
|
375
375
|
// Not degraded because embeddings are optional
|
|
376
376
|
expect(parsed.degraded).toBe(false);
|
|
377
377
|
expect(parsed.sources.semantic).toBe(0);
|
|
378
|
-
// Qdrant is mocked empty
|
|
379
|
-
//
|
|
380
|
-
expect(parsed.resultCount).toBe(
|
|
378
|
+
// Qdrant is mocked empty so hybrid search returns no candidates, but
|
|
379
|
+
// serendipity sampling may pick up seeded items from the DB directly.
|
|
380
|
+
expect(typeof parsed.resultCount).toBe("number");
|
|
381
381
|
});
|
|
382
382
|
|
|
383
383
|
test("gracefully returns empty in degraded mode without embeddings", async () => {
|
|
@@ -452,11 +452,11 @@ describe("handleMemoryRecall", () => {
|
|
|
452
452
|
|
|
453
453
|
expect(result.isError).toBe(false);
|
|
454
454
|
const parsed = parseResult(result.content);
|
|
455
|
-
// With
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
expect(parsed.
|
|
459
|
-
expect(parsed.
|
|
455
|
+
// With Qdrant mocked empty, segments inserted directly into the DB are
|
|
456
|
+
// not found via hybrid search. Verify the handler returns a valid result
|
|
457
|
+
// shape without erroring — the scope policy is still passed through.
|
|
458
|
+
expect(typeof parsed.resultCount).toBe("number");
|
|
459
|
+
expect(typeof parsed.sources.recency).toBe("number");
|
|
460
460
|
});
|
|
461
461
|
|
|
462
462
|
test("default scope handler invocation does not error", async () => {
|
|
@@ -523,6 +523,48 @@ describe("handleMemoryRecall", () => {
|
|
|
523
523
|
expect(parsed.sources.recency).toBe(0);
|
|
524
524
|
});
|
|
525
525
|
|
|
526
|
+
// ── Source conversation info ───────────────────────────────────────
|
|
527
|
+
// These tests must come after normal tests and before the error test,
|
|
528
|
+
// because mock.module replaces the retriever for all subsequent imports.
|
|
529
|
+
|
|
530
|
+
test("items include sourceConversationTitle when sourceLabel is present", async () => {
|
|
531
|
+
mock.module("../../memory/retriever.js", () => ({
|
|
532
|
+
buildMemoryRecall: async () => ({
|
|
533
|
+
injectedText: "<memory>test memory</memory>",
|
|
534
|
+
selectedCount: 2,
|
|
535
|
+
semanticHits: 2,
|
|
536
|
+
degraded: false,
|
|
537
|
+
topCandidates: [
|
|
538
|
+
{
|
|
539
|
+
key: "item:abc-1",
|
|
540
|
+
type: "item",
|
|
541
|
+
kind: "preference",
|
|
542
|
+
id: "abc-1",
|
|
543
|
+
sourceLabel: "API Design Discussion",
|
|
544
|
+
},
|
|
545
|
+
{ key: "item:abc-2", type: "item", kind: "identity", id: "abc-2" },
|
|
546
|
+
],
|
|
547
|
+
}),
|
|
548
|
+
}));
|
|
549
|
+
|
|
550
|
+
const { handleMemoryRecall: recallWithLabels } =
|
|
551
|
+
await import("./handlers.js");
|
|
552
|
+
|
|
553
|
+
const result = await recallWithLabels({ query: "test" }, TEST_CONFIG);
|
|
554
|
+
expect(result.isError).toBe(false);
|
|
555
|
+
|
|
556
|
+
const parsed = parseResult(result.content);
|
|
557
|
+
expect(parsed.items).toHaveLength(2);
|
|
558
|
+
|
|
559
|
+
// First item has a sourceLabel -> should have sourceConversationTitle
|
|
560
|
+
expect(parsed.items[0].sourceConversationTitle).toBe(
|
|
561
|
+
"API Design Discussion",
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
// Second item has no sourceLabel -> should not have sourceConversationTitle
|
|
565
|
+
expect(parsed.items[1].sourceConversationTitle).toBeUndefined();
|
|
566
|
+
});
|
|
567
|
+
|
|
526
568
|
// ── Error handling ────────────────────────────────────────────────
|
|
527
569
|
// This test must be last: mock.module replaces the retriever for all
|
|
528
570
|
// subsequent imports and cannot be cleanly reverted within the same
|
|
@@ -252,7 +252,7 @@ export interface MemoryRecallToolResult {
|
|
|
252
252
|
text: string;
|
|
253
253
|
resultCount: number;
|
|
254
254
|
degraded: boolean;
|
|
255
|
-
items: Array<{ id: string; type: string; kind: string }>;
|
|
255
|
+
items: Array<{ id: string; type: string; kind: string; sourceConversationTitle?: string }>;
|
|
256
256
|
sources: {
|
|
257
257
|
semantic: number;
|
|
258
258
|
recency: number;
|
|
@@ -298,6 +298,7 @@ export async function handleMemoryRecall(
|
|
|
298
298
|
{
|
|
299
299
|
scopeId,
|
|
300
300
|
scopePolicyOverride,
|
|
301
|
+
hydeEnabled: true,
|
|
301
302
|
},
|
|
302
303
|
);
|
|
303
304
|
|
|
@@ -328,6 +329,7 @@ export async function handleMemoryRecall(
|
|
|
328
329
|
id: c.key,
|
|
329
330
|
type: c.type,
|
|
330
331
|
kind: c.kind,
|
|
332
|
+
...(c.sourceLabel ? { sourceConversationTitle: c.sourceLabel } : {}),
|
|
331
333
|
})),
|
|
332
334
|
sources: {
|
|
333
335
|
semantic: recall.semanticHits,
|
|
@@ -47,12 +47,7 @@ export function isSideEffectTool(
|
|
|
47
47
|
// Action-aware checks for mixed-action tools
|
|
48
48
|
if (toolName === "credential_store") {
|
|
49
49
|
const action = input?.action;
|
|
50
|
-
return
|
|
51
|
-
action === "store" ||
|
|
52
|
-
action === "delete" ||
|
|
53
|
-
action === "prompt" ||
|
|
54
|
-
action === "oauth2_connect"
|
|
55
|
-
);
|
|
50
|
+
return action === "store" || action === "delete" || action === "prompt";
|
|
56
51
|
}
|
|
57
52
|
|
|
58
53
|
return false;
|
|
@@ -48,8 +48,9 @@ export function buildSanitizedEnv(): Record<string, string> {
|
|
|
48
48
|
// Always inject an internal gateway base for local control-plane/API calls.
|
|
49
49
|
const internalGatewayBase = getGatewayInternalBaseUrl();
|
|
50
50
|
env.INTERNAL_GATEWAY_BASE_URL = internalGatewayBase;
|
|
51
|
-
//
|
|
52
|
-
//
|
|
51
|
+
// @deprecated — VELLUM_DATA_DIR is equivalent to $VELLUM_WORKSPACE_DIR/data.
|
|
52
|
+
// Removing this requires an LLM-based migration or declarative migration
|
|
53
|
+
// file to update existing user-authored skills to use VELLUM_WORKSPACE_DIR.
|
|
53
54
|
env.VELLUM_DATA_DIR = getDataDir();
|
|
54
55
|
// Expose the workspace directory so skills and child processes can read/write
|
|
55
56
|
// workspace-scoped files (e.g. avatar traits, user data).
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
3
4
|
|
|
4
5
|
import { getConfig } from "../../config/loader.js";
|
|
5
6
|
import { isCesShellLockdownEnabled } from "../../credential-execution/feature-gates.js";
|
|
@@ -8,7 +9,7 @@ import type { ToolDefinition } from "../../providers/types.js";
|
|
|
8
9
|
import { isUntrustedTrustClass } from "../../runtime/actor-trust-resolver.js";
|
|
9
10
|
import { redactSecrets } from "../../security/secret-scanner.js";
|
|
10
11
|
import { getLogger } from "../../util/logger.js";
|
|
11
|
-
import { getDataDir,
|
|
12
|
+
import { getDataDir, getWorkspaceDir } from "../../util/platform.js";
|
|
12
13
|
import { resolveCredentialRef } from "../credentials/resolve.js";
|
|
13
14
|
import {
|
|
14
15
|
getOrStartSession,
|
|
@@ -38,20 +39,16 @@ function buildCredentialRefTrace(
|
|
|
38
39
|
* Build the list of absolute paths that should be blocked from read access
|
|
39
40
|
* inside the sandbox when CES shell lockdown is active.
|
|
40
41
|
*
|
|
41
|
-
*
|
|
42
|
-
* -
|
|
43
|
-
* CES data root at ~/.vellum/protected/credential-executor/)
|
|
42
|
+
* Blocked paths include:
|
|
43
|
+
* - Gateway security directory (credential store secrets, CES data)
|
|
44
44
|
* - ~/.vellum/workspace/data/db/ - database files that may contain credential metadata
|
|
45
|
-
* - CES bootstrap socket directory (/run/ces-bootstrap/ or CES_BOOTSTRAP_SOCKET_DIR)
|
|
46
|
-
*
|
|
47
|
-
* - CES managed-mode data root (CES_DATA_DIR, or /ces-data when
|
|
48
|
-
* CES_MANAGED_MODE is set) - prevents access to CES-private state in
|
|
49
|
-
* managed deployments (local-mode is already covered by the protected/
|
|
50
|
-
* entry)
|
|
45
|
+
* - CES bootstrap socket directory (/run/ces-bootstrap/ or CES_BOOTSTRAP_SOCKET_DIR)
|
|
46
|
+
* - CES managed-mode data root (CES_DATA_DIR, or /ces-data when CES_MANAGED_MODE is set)
|
|
51
47
|
*/
|
|
52
48
|
function buildCesProtectedPaths(): string[] {
|
|
53
|
-
const
|
|
54
|
-
|
|
49
|
+
const securityDir =
|
|
50
|
+
process.env.GATEWAY_SECURITY_DIR || join(homedir(), ".vellum", "protected");
|
|
51
|
+
const paths = [securityDir, join(getWorkspaceDir(), "data", "db")];
|
|
55
52
|
|
|
56
53
|
// CES bootstrap socket directory - block access to the Unix socket that
|
|
57
54
|
// accepts RPC commands from the assistant process.
|
|
@@ -70,7 +67,7 @@ function buildCesProtectedPaths(): string[] {
|
|
|
70
67
|
|
|
71
68
|
// CES managed-mode private data root - in managed deployments the CES
|
|
72
69
|
// data lives outside the Vellum root, so it isn't covered by the
|
|
73
|
-
//
|
|
70
|
+
// gateway security directory entry above.
|
|
74
71
|
const cesDataDir = process.env["CES_DATA_DIR"];
|
|
75
72
|
if (cesDataDir) {
|
|
76
73
|
paths.push(cesDataDir);
|
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
} from "../memory/canonical-guardian-store.js";
|
|
7
7
|
import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
|
|
8
8
|
import { createOrReuseToolGrantRequest } from "../runtime/tool-grant-request-helper.js";
|
|
9
|
+
import { redactSecrets } from "../security/secret-scanner.js";
|
|
9
10
|
import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
|
|
10
11
|
import { getTaskRunRules } from "../tasks/ephemeral-permissions.js";
|
|
11
12
|
import { getLogger } from "../util/logger.js";
|
|
12
13
|
import { getAllTools, getTool } from "./registry.js";
|
|
13
14
|
import { isSideEffectTool } from "./side-effects.js";
|
|
15
|
+
import { summarizeToolInput } from "./tool-input-summary.js";
|
|
14
16
|
import type {
|
|
15
17
|
ExecutionTarget,
|
|
16
18
|
Tool,
|
|
@@ -22,6 +24,22 @@ import { enforceVerificationControlPlanePolicy } from "./verification-control-pl
|
|
|
22
24
|
|
|
23
25
|
const log = getLogger("tool-approval-handler");
|
|
24
26
|
|
|
27
|
+
function buildToolGrantQuestionText(
|
|
28
|
+
toolName: string,
|
|
29
|
+
input: Record<string, unknown>,
|
|
30
|
+
context: ToolContext,
|
|
31
|
+
): string {
|
|
32
|
+
const senderLabel =
|
|
33
|
+
context.requesterDisplayName ||
|
|
34
|
+
context.requesterIdentifier ||
|
|
35
|
+
context.requesterExternalUserId ||
|
|
36
|
+
"A trusted contact";
|
|
37
|
+
const inputSummary = redactSecrets(summarizeToolInput(toolName, input));
|
|
38
|
+
return inputSummary
|
|
39
|
+
? `${senderLabel} wants to use "${toolName}": ${inputSummary}`
|
|
40
|
+
: `${senderLabel} is requesting permission to use "${toolName}"`;
|
|
41
|
+
}
|
|
42
|
+
|
|
25
43
|
/** Default polling interval for inline grant wait (ms). */
|
|
26
44
|
export const TC_GRANT_WAIT_INTERVAL_MS = 500;
|
|
27
45
|
/** Default maximum wait time for inline grant wait (ms). */
|
|
@@ -494,7 +512,8 @@ export class ToolApprovalHandler {
|
|
|
494
512
|
requesterChatId: context.requesterChatId,
|
|
495
513
|
toolName: name,
|
|
496
514
|
inputDigest,
|
|
497
|
-
questionText:
|
|
515
|
+
questionText: buildToolGrantQuestionText(name, input, context),
|
|
516
|
+
requesterIdentifier: context.requesterDisplayName || context.requesterIdentifier,
|
|
498
517
|
});
|
|
499
518
|
|
|
500
519
|
// Only wait inline if the escalation succeeded (created or deduped).
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summarizes tool input into a concise string for guardian approval display.
|
|
3
|
+
*
|
|
4
|
+
* Returns unredacted text — callers that persist the result (e.g. to the
|
|
5
|
+
* canonical_guardian_requests table) must apply redactSecrets() before writing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function truncate(value: string, maxLen: number): string {
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
if (trimmed.length <= maxLen) return trimmed;
|
|
11
|
+
return `${trimmed.slice(0, maxLen)}…`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function extractString(
|
|
15
|
+
input: Record<string, unknown>,
|
|
16
|
+
...keys: string[]
|
|
17
|
+
): string | undefined {
|
|
18
|
+
for (const key of keys) {
|
|
19
|
+
const val = input[key];
|
|
20
|
+
if (typeof val === "string" && val.trim().length > 0) {
|
|
21
|
+
return val;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function firstStringValue(input: Record<string, unknown>): string | undefined {
|
|
28
|
+
for (const val of Object.values(input)) {
|
|
29
|
+
if (typeof val === "string" && val.trim().length > 0) {
|
|
30
|
+
return val;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function summarizeToolInput(
|
|
37
|
+
toolName: string,
|
|
38
|
+
input: Record<string, unknown>,
|
|
39
|
+
): string {
|
|
40
|
+
switch (toolName) {
|
|
41
|
+
case "bash":
|
|
42
|
+
case "terminal":
|
|
43
|
+
case "host_bash": {
|
|
44
|
+
const cmd = extractString(input, "command");
|
|
45
|
+
return cmd ? truncate(cmd, 120) : "";
|
|
46
|
+
}
|
|
47
|
+
case "file_read":
|
|
48
|
+
case "file_write":
|
|
49
|
+
case "file_edit":
|
|
50
|
+
case "host_file_read":
|
|
51
|
+
case "host_file_write":
|
|
52
|
+
case "host_file_edit": {
|
|
53
|
+
const path = extractString(input, "file_path", "path");
|
|
54
|
+
return path ? path.trim() : "";
|
|
55
|
+
}
|
|
56
|
+
case "web_fetch":
|
|
57
|
+
case "network_request": {
|
|
58
|
+
const url = extractString(input, "url");
|
|
59
|
+
return url ? truncate(url, 100) : "";
|
|
60
|
+
}
|
|
61
|
+
default: {
|
|
62
|
+
const fallback = firstStringValue(input);
|
|
63
|
+
return fallback ? truncate(fallback, 80) : "";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/tools/types.ts
CHANGED
|
@@ -169,6 +169,10 @@ export interface ToolContext {
|
|
|
169
169
|
requesterExternalUserId?: string;
|
|
170
170
|
/** Chat ID of the requester (non-guardian actor). Used for tool grant request escalation notifications. */
|
|
171
171
|
requesterChatId?: string;
|
|
172
|
+
/** Human-readable identifier for the requester (e.g., @username). */
|
|
173
|
+
requesterIdentifier?: string;
|
|
174
|
+
/** Preferred display name for the requester. */
|
|
175
|
+
requesterDisplayName?: string;
|
|
172
176
|
/** Slack channel ID for channel-scoped permission enforcement. When set, tools are checked against the channel's permission profile. */
|
|
173
177
|
channelPermissionChannelId?: string;
|
|
174
178
|
/** The tool_use block ID from the LLM response, used to correlate confirmation prompts with specific tool invocations. */
|
package/src/usage/types.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface PricingUsage {
|
|
|
19
19
|
cacheCreationInputTokens: number;
|
|
20
20
|
cacheReadInputTokens: number;
|
|
21
21
|
anthropicCacheCreation: AnthropicCacheCreationTokenDetails | null;
|
|
22
|
+
/** Anthropic fast mode speed indicator from the API response. */
|
|
23
|
+
speed?: "fast" | "standard" | null;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -36,6 +38,8 @@ export interface UsageEventInput {
|
|
|
36
38
|
conversationId: string | null;
|
|
37
39
|
runId: string | null;
|
|
38
40
|
requestId: string | null;
|
|
41
|
+
/** Number of actual LLM API calls represented by this event (defaults to 1). */
|
|
42
|
+
llmCallCount?: number;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
/**
|