@vellumai/assistant 0.4.52 → 0.4.54
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/ARCHITECTURE.md +2 -2
- package/bun.lock +62 -349
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/keychain-broker.md +91 -40
- package/docs/architecture/memory.md +3 -3
- package/docs/architecture/security.md +2 -2
- package/knip.json +7 -29
- package/package.json +2 -9
- package/src/__tests__/agent-loop.test.ts +1 -1
- package/src/__tests__/app-git-history.test.ts +0 -2
- package/src/__tests__/app-git-service.test.ts +1 -6
- package/src/__tests__/approval-cascade.test.ts +3 -2
- package/src/__tests__/approval-routes-http.test.ts +0 -1
- package/src/__tests__/asset-materialize-tool.test.ts +0 -1
- package/src/__tests__/asset-search-tool.test.ts +0 -1
- package/src/__tests__/assistant-events-sse-hardening.test.ts +0 -1
- package/src/__tests__/attachments-store.test.ts +0 -1
- package/src/__tests__/avatar-e2e.test.ts +5 -1
- package/src/__tests__/browser-fill-credential.test.ts +4 -6
- package/src/__tests__/btw-routes.test.ts +39 -0
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-domain.test.ts +1 -1
- package/src/__tests__/call-routes-http.test.ts +1 -3
- package/src/__tests__/canonical-guardian-store.test.ts +33 -2
- package/src/__tests__/channel-guardian.test.ts +4 -4
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +1 -1
- package/src/__tests__/checker.test.ts +13 -11
- package/src/__tests__/claude-code-skill-regression.test.ts +5 -2
- package/src/__tests__/claude-code-tool-profiles.test.ts +7 -3
- package/src/__tests__/config-loader-backfill.test.ts +1 -5
- package/src/__tests__/config-schema.test.ts +9 -46
- package/src/__tests__/config-watcher.test.ts +11 -3
- package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -1
- package/src/__tests__/credential-broker-browser-fill.test.ts +27 -24
- package/src/__tests__/credential-broker-server-use.test.ts +76 -40
- package/src/__tests__/credential-security-e2e.test.ts +1 -6
- package/src/__tests__/credential-security-invariants.test.ts +27 -8
- package/src/__tests__/credential-vault-unit.test.ts +32 -16
- package/src/__tests__/credential-vault.test.ts +40 -28
- package/src/__tests__/credentials-cli.test.ts +1 -21
- package/src/__tests__/email-invite-adapter.test.ts +0 -1
- package/src/__tests__/error-handler-friendly-messages.test.ts +4 -5
- package/src/__tests__/fixtures/credential-security-fixtures.ts +3 -3
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -79
- package/src/__tests__/gateway-only-enforcement.test.ts +1 -23
- package/src/__tests__/guardian-action-conversation-turn.test.ts +8 -8
- package/src/__tests__/guardian-action-late-reply.test.ts +13 -14
- package/src/__tests__/guardian-action-store.test.ts +0 -57
- package/src/__tests__/guardian-outbound-http.test.ts +1 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -3
- package/src/__tests__/hooks-blocking.test.ts +1 -1
- package/src/__tests__/hooks-config.test.ts +5 -29
- package/src/__tests__/hooks-discovery.test.ts +1 -1
- package/src/__tests__/hooks-integration.test.ts +1 -1
- package/src/__tests__/hooks-manager.test.ts +1 -1
- package/src/__tests__/hooks-runner.test.ts +1 -23
- package/src/__tests__/hooks-settings.test.ts +1 -1
- package/src/__tests__/hooks-templates.test.ts +1 -1
- package/src/__tests__/host-shell-tool.test.ts +0 -1
- package/src/__tests__/http-user-message-parity.test.ts +19 -0
- package/src/__tests__/integration-status.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +0 -3
- package/src/__tests__/list-messages-attachments.test.ts +0 -1
- package/src/__tests__/llm-usage-store.test.ts +50 -0
- package/src/__tests__/log-export-workspace.test.ts +233 -0
- package/src/__tests__/managed-proxy-context.test.ts +41 -41
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/media-generate-image.test.ts +9 -4
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -7
- package/src/__tests__/memory-regressions.experimental.test.ts +4 -4
- package/src/__tests__/memory-regressions.test.ts +27 -28
- package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -4
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1
- package/src/__tests__/migration-export-http.test.ts +0 -1
- package/src/__tests__/migration-import-commit-http.test.ts +0 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +0 -1
- package/src/__tests__/migration-validate-http.test.ts +0 -1
- package/src/__tests__/notification-decision-fallback.test.ts +1 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +237 -0
- package/src/__tests__/oauth-cli.test.ts +2 -14
- package/src/__tests__/oauth-store.test.ts +3 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +5 -4
- package/src/__tests__/onboarding-starter-tasks.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -2
- package/src/__tests__/openai-provider.test.ts +7 -7
- package/src/__tests__/platform.test.ts +14 -4
- package/src/__tests__/pricing.test.ts +0 -234
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -15
- package/src/__tests__/provider-fail-open-selection.test.ts +67 -62
- package/src/__tests__/provider-managed-proxy-integration.test.ts +88 -85
- package/src/__tests__/provider-registry-ollama.test.ts +10 -4
- package/src/__tests__/public-ingress-urls.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/registry.test.ts +3 -103
- package/src/__tests__/relay-server.test.ts +0 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +0 -1
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +2 -7
- package/src/__tests__/secret-onetime-send.test.ts +1 -6
- package/src/__tests__/secret-routes-managed-proxy.test.ts +6 -14
- package/src/__tests__/secret-scanner-executor.test.ts +0 -1
- package/src/__tests__/secure-keys.test.ts +241 -229
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/session-abort-tool-results.test.ts +3 -2
- package/src/__tests__/session-agent-loop-overflow.test.ts +1012 -838
- package/src/__tests__/session-agent-loop.test.ts +2 -2
- package/src/__tests__/session-confirmation-signals.test.ts +3 -2
- package/src/__tests__/session-error.test.ts +5 -4
- package/src/__tests__/session-history-web-search.test.ts +34 -9
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -7
- package/src/__tests__/session-pre-run-repair.test.ts +3 -2
- package/src/__tests__/session-provider-retry-repair.test.ts +31 -27
- package/src/__tests__/session-queue.test.ts +5 -5
- package/src/__tests__/session-runtime-assembly.test.ts +118 -0
- package/src/__tests__/session-slash-known.test.ts +31 -14
- package/src/__tests__/session-slash-queue.test.ts +3 -2
- package/src/__tests__/session-slash-unknown.test.ts +3 -2
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -1
- package/src/__tests__/session-workspace-injection.test.ts +3 -2
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -2
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
- package/src/__tests__/skillssh-registry.test.ts +21 -0
- package/src/__tests__/slack-channel-config.test.ts +1 -7
- package/src/__tests__/slack-share-routes.test.ts +1 -1
- package/src/__tests__/swarm-recursion.test.ts +4 -1
- package/src/__tests__/swarm-session-integration.test.ts +24 -14
- package/src/__tests__/swarm-tool.test.ts +4 -2
- package/src/__tests__/task-compiler.test.ts +1 -1
- package/src/__tests__/telegram-bot-username-resolution.test.ts +2 -4
- package/src/__tests__/test-support/browser-skill-harness.ts +0 -18
- package/src/__tests__/test-support/computer-use-skill-harness.ts +0 -23
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1521 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +1 -2
- package/src/__tests__/trust-store.test.ts +8 -83
- package/src/__tests__/twilio-config.test.ts +0 -1
- package/src/__tests__/twilio-provider.test.ts +0 -5
- package/src/__tests__/twilio-routes.test.ts +2 -3
- package/src/__tests__/usage-cache-backfill-migration.test.ts +10 -10
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -1
- package/src/__tests__/voice-quality.test.ts +2 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/__tests__/web-search.test.ts +1 -1
- package/src/agent/loop.ts +17 -1
- package/src/bundler/app-bundler.ts +40 -24
- package/src/calls/call-controller.ts +16 -0
- package/src/calls/guardian-question-copy.ts +1 -1
- package/src/calls/relay-server.ts +29 -13
- package/src/calls/voice-control-protocol.ts +1 -0
- package/src/calls/voice-quality.ts +1 -1
- package/src/calls/voice-session-bridge.ts +9 -3
- package/src/channels/types.ts +16 -0
- package/src/cli/commands/bash.ts +173 -0
- package/src/cli/commands/doctor.ts +15 -57
- package/src/cli/commands/memory.ts +3 -5
- package/src/cli/commands/oauth/connections.ts +4 -2
- package/src/cli/commands/oauth/providers.ts +1 -13
- package/src/cli/commands/sessions.ts +1 -1
- package/src/cli/commands/usage.ts +359 -0
- package/src/cli/http-client.ts +22 -12
- package/src/cli/program.ts +4 -0
- package/src/cli/reference.ts +2 -0
- package/src/cli.ts +251 -181
- package/src/config/assistant-feature-flags.ts +0 -7
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +1 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +0 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +4 -3
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +3 -5
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -3
- package/src/config/bundled-skills/messaging/SKILL.md +0 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +0 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -6
- package/src/config/env.ts +13 -0
- package/src/config/feature-flag-registry.json +15 -39
- package/src/config/loader.ts +7 -135
- package/src/config/schema.ts +0 -6
- package/src/config/schemas/channels.ts +1 -0
- package/src/config/schemas/elevenlabs.ts +2 -2
- package/src/config/schemas/security.ts +1 -2
- package/src/config/skills.ts +1 -1
- package/src/contacts/contact-store.ts +21 -75
- package/src/contacts/contacts-write.ts +6 -6
- package/src/contacts/types.ts +2 -0
- package/src/context/token-estimator.ts +35 -2
- package/src/context/window-manager.ts +16 -2
- package/src/daemon/approved-devices-store.ts +0 -44
- package/src/daemon/classifier.ts +1 -1
- package/src/daemon/config-watcher.ts +35 -11
- package/src/daemon/context-overflow-reducer.ts +13 -2
- package/src/daemon/handlers/config-ingress.ts +25 -8
- package/src/daemon/handlers/config-model.ts +22 -16
- package/src/daemon/handlers/config-telegram.ts +18 -6
- package/src/daemon/handlers/dictation.ts +0 -429
- package/src/daemon/handlers/sessions.ts +4 -116
- package/src/daemon/handlers/skills.ts +2 -201
- package/src/daemon/lifecycle.ts +21 -20
- package/src/daemon/message-types/contacts.ts +2 -0
- package/src/daemon/message-types/integrations.ts +1 -0
- package/src/daemon/message-types/sessions.ts +2 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +75 -0
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +42 -5
- package/src/daemon/session-agent-loop-handlers.ts +1 -1
- package/src/daemon/session-agent-loop.ts +27 -79
- package/src/daemon/session-error.ts +5 -4
- package/src/daemon/session-process.ts +17 -10
- package/src/daemon/session-runtime-assembly.ts +50 -0
- package/src/daemon/session-slash.ts +34 -22
- package/src/daemon/session.ts +1 -0
- package/src/daemon/shutdown-handlers.ts +15 -0
- package/src/daemon/watch-handler.ts +2 -2
- package/src/email/guardrails.ts +1 -1
- package/src/email/service.ts +0 -5
- package/src/events/domain-events.ts +1 -0
- package/src/hooks/templates.ts +1 -1
- package/src/media/app-icon-generator.ts +4 -3
- package/src/media/avatar-router.ts +5 -4
- package/src/media/gemini-image-service.ts +5 -5
- package/src/memory/admin.ts +2 -2
- package/src/memory/app-git-service.ts +0 -7
- package/src/memory/canonical-guardian-store.ts +25 -3
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-title-service.ts +2 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.ts +46 -33
- package/src/memory/external-conversation-store.ts +0 -30
- package/src/memory/guardian-action-store.ts +0 -31
- package/src/memory/guardian-approvals.ts +1 -56
- package/src/memory/indexer.ts +4 -3
- package/src/memory/items-extractor.ts +1 -1
- package/src/memory/job-handlers/backfill.ts +5 -2
- package/src/memory/job-handlers/index-maintenance.ts +2 -2
- package/src/memory/job-handlers/media-processing.ts +2 -2
- package/src/memory/job-handlers/summarization.ts +1 -1
- package/src/memory/job-utils.ts +1 -2
- package/src/memory/jobs-worker.ts +2 -2
- package/src/memory/llm-usage-store.ts +57 -11
- package/src/memory/media-store.ts +4 -535
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +2 -2
- package/src/memory/migrations/110-channel-guardian.ts +0 -1
- package/src/memory/migrations/158-channel-interaction-columns.ts +18 -0
- package/src/memory/migrations/159-drop-contact-interaction-columns.ts +16 -0
- package/src/memory/migrations/160-drop-loopback-port-column.ts +13 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/published-pages-store.ts +0 -83
- package/src/memory/qdrant-circuit-breaker.ts +0 -8
- package/src/memory/retriever.test.ts +19 -12
- package/src/memory/retriever.ts +1 -1
- package/src/memory/schema/contacts.ts +2 -2
- package/src/memory/schema/oauth.ts +0 -1
- package/src/memory/search/semantic.ts +1 -8
- package/src/memory/shared-app-links-store.ts +0 -15
- package/src/messaging/registry.ts +0 -5
- package/src/messaging/style-analyzer.ts +1 -1
- package/src/notifications/copy-composer.ts +5 -13
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/deliveries-store.ts +0 -39
- package/src/notifications/guardian-question-mode.ts +6 -10
- package/src/notifications/preference-extractor.ts +1 -1
- package/src/oauth/byo-connection.test.ts +29 -20
- package/src/oauth/connect-orchestrator.ts +5 -3
- package/src/oauth/connect-types.ts +9 -2
- package/src/oauth/manual-token-connection.ts +9 -7
- package/src/oauth/oauth-store.ts +2 -8
- package/src/oauth/provider-behaviors.ts +11 -1
- package/src/oauth/seed-providers.ts +13 -5
- package/src/permissions/checker.ts +21 -2
- package/src/permissions/shell-identity.ts +0 -5
- package/src/permissions/trust-store.ts +0 -37
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +1 -1
- package/src/prompts/system-prompt.ts +5 -14
- package/src/prompts/templates/BOOTSTRAP.md +1 -3
- package/src/providers/anthropic/client.ts +16 -8
- package/src/providers/managed-proxy/constants.ts +9 -11
- package/src/providers/managed-proxy/context.ts +14 -9
- package/src/providers/provider-send-message.ts +4 -52
- package/src/providers/registry.ts +29 -57
- package/src/providers/types.ts +1 -1
- package/src/runtime/actor-token-store.ts +0 -23
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/channel-invite-transports/telegram.ts +12 -6
- package/src/runtime/channel-retry-sweep.ts +6 -0
- package/src/runtime/http-router.ts +5 -1
- package/src/runtime/http-server.ts +101 -4
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/invite-instruction-generator.ts +25 -51
- package/src/runtime/invite-service.ts +0 -20
- package/src/runtime/middleware/error-handler.ts +1 -2
- package/src/runtime/routes/app-management-routes.ts +1 -0
- package/src/runtime/routes/attachment-routes.ts +1 -1
- package/src/runtime/routes/brain-graph-routes.ts +1 -1
- package/src/runtime/routes/btw-routes.ts +20 -1
- package/src/runtime/routes/call-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +64 -24
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +2 -2
- package/src/runtime/routes/documents-routes.ts +3 -3
- package/src/runtime/routes/global-search-routes.ts +1 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -20
- package/src/runtime/routes/guardian-refresh-routes.ts +0 -20
- package/src/runtime/routes/inbound-message-handler.ts +10 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +5 -5
- package/src/runtime/routes/integrations/slack/share.ts +5 -5
- package/src/runtime/routes/log-export-routes.ts +122 -10
- package/src/runtime/routes/secret-routes.ts +4 -4
- package/src/runtime/routes/session-query-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +53 -0
- package/src/runtime/routes/trust-rules-routes.ts +1 -1
- package/src/runtime/routes/workspace-routes.ts +3 -0
- package/src/runtime/verification-templates.ts +1 -1
- package/src/security/credential-backend.ts +148 -0
- package/src/security/oauth2.ts +5 -5
- package/src/security/secret-allowlist.ts +1 -1
- package/src/security/secure-keys.ts +98 -160
- package/src/security/token-manager.ts +0 -7
- package/src/sequence/guardrails.ts +0 -4
- package/src/sequence/store.ts +1 -20
- package/src/sequence/types.ts +1 -36
- package/src/signals/bash.ts +157 -0
- package/src/signals/cancel.ts +69 -0
- package/src/signals/conversation-undo.ts +127 -0
- package/src/signals/trust-rule.ts +174 -0
- package/src/skills/clawhub.ts +5 -5
- package/src/skills/managed-store.ts +4 -4
- package/src/skills/skillssh-registry.ts +6 -1
- package/src/swarm/backend-claude-code.ts +6 -6
- package/src/swarm/worker-backend.ts +1 -1
- package/src/swarm/worker-runner.ts +1 -1
- package/src/telegram/bot-username.ts +11 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +366 -0
- package/src/telemetry/usage-telemetry-reporter.ts +181 -0
- package/src/tools/claude-code/claude-code.ts +6 -6
- package/src/tools/credentials/broker.ts +7 -5
- package/src/tools/credentials/vault.ts +11 -6
- package/src/tools/memory/handlers.test.ts +24 -26
- package/src/tools/memory/handlers.ts +1 -13
- package/src/tools/network/__tests__/web-search.test.ts +18 -86
- package/src/tools/network/web-search.ts +9 -15
- package/src/tools/registry.ts +5 -100
- package/src/tools/terminal/parser.ts +34 -4
- package/src/tools/tool-manifest.ts +0 -10
- package/src/usage/actors.ts +0 -12
- package/src/util/canonicalize-identity.ts +0 -9
- package/src/util/errors.ts +0 -3
- package/src/util/platform.ts +31 -8
- package/src/util/pricing.ts +0 -39
- package/src/watcher/constants.ts +0 -7
- package/src/watcher/providers/linear.ts +1 -1
- package/src/work-items/work-item-store.ts +4 -4
- package/src/workspace/commit-message-provider.ts +1 -1
- package/src/workspace/git-service.ts +44 -1
- package/src/workspace/provider-commit-message-generator.ts +11 -7
- package/src/__tests__/fixtures/proxy-fixtures.ts +0 -147
- package/src/browser-extension-relay/client.ts +0 -155
- package/src/contacts/index.ts +0 -18
- package/src/daemon/tls-certs.ts +0 -270
- package/src/errors.ts +0 -41
- package/src/events/index.ts +0 -18
- package/src/followups/index.ts +0 -10
- package/src/playbooks/index.ts +0 -10
- package/src/runtime/auth/index.ts +0 -44
- package/src/tasks/candidate-store.ts +0 -95
- package/src/tools/browser/api-map.ts +0 -313
- package/src/tools/browser/auto-navigate.ts +0 -469
- package/src/tools/browser/headless-browser.ts +0 -590
- package/src/tools/browser/recording-store.ts +0 -75
- package/src/tools/computer-use/registry.ts +0 -21
- package/src/tools/tasks/index.ts +0 -27
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { dirname, join } from "node:path";
|
|
2
2
|
|
|
3
|
-
import { getConfig } from "../../../../config/loader.js";
|
|
4
3
|
import {
|
|
5
4
|
getKeyframesForAsset,
|
|
6
5
|
getMediaAssetById,
|
|
7
6
|
} from "../../../../memory/media-store.js";
|
|
7
|
+
import { getSecureKeyAsync } from "../../../../security/secure-keys.js";
|
|
8
8
|
import type {
|
|
9
9
|
ToolContext,
|
|
10
10
|
ToolExecutionResult,
|
|
@@ -31,8 +31,7 @@ export async function run(
|
|
|
31
31
|
|
|
32
32
|
let openaiApiKey: string | undefined;
|
|
33
33
|
if (includeAudio && (!transcriptionMode || transcriptionMode === "api")) {
|
|
34
|
-
|
|
35
|
-
openaiApiKey = config.apiKeys.openai;
|
|
34
|
+
openaiApiKey = (await getSecureKeyAsync("openai")) ?? undefined;
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
const options: PreprocessOptions = {
|
|
@@ -7,7 +7,6 @@ metadata:
|
|
|
7
7
|
vellum:
|
|
8
8
|
display-name: "Messaging"
|
|
9
9
|
user-invocable: true
|
|
10
|
-
feature-flag: "messaging"
|
|
11
10
|
---
|
|
12
11
|
|
|
13
12
|
You are a unified messaging assistant with access to multiple platforms (Slack, Gmail, Telegram, and more). Use the messaging tools to help users read, search, organize, draft, and send messages across all connected platforms.
|
|
@@ -15,7 +15,7 @@ All call-related settings can be managed via `assistant config`:
|
|
|
15
15
|
| `calls.callerIdentity.userNumber` | E.164 phone number for user-number mode | _(empty)_ |
|
|
16
16
|
| `calls.voice.language` | Language code for TTS and transcription | `en-US` |
|
|
17
17
|
| `calls.voice.transcriptionProvider` | Speech-to-text provider (`Deepgram`, `Google`) | `Deepgram` |
|
|
18
|
-
| `elevenlabs.voiceId` | ElevenLabs voice ID used by both in-app TTS and phone calls. Set during setup from the curated voice list. Defaults to
|
|
18
|
+
| `elevenlabs.voiceId` | ElevenLabs voice ID used by both in-app TTS and phone calls. Set during setup from the curated voice list. Defaults to Amelia | `ZF6FPAbjXT4488VcRRnw` |
|
|
19
19
|
| `elevenlabs.voiceModelId` | Optional Twilio ConversationRelay model suffix. Leave empty to send bare `voiceId` | _(empty)_ |
|
|
20
20
|
| `elevenlabs.speed` | Playback speed (`0.7` – `1.2`) | `1.0` |
|
|
21
21
|
| `elevenlabs.stability` | Voice stability (`0.0` – `1.0`) | `0.5` |
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
11
|
import { extname, join } from "node:path";
|
|
12
12
|
|
|
13
|
-
import { getConfig } from "../../../../config/loader.js";
|
|
14
13
|
import { getAttachmentsByIds } from "../../../../memory/attachments-store.js";
|
|
14
|
+
import { getSecureKeyAsync } from "../../../../security/secure-keys.js";
|
|
15
15
|
import type {
|
|
16
16
|
ToolContext,
|
|
17
17
|
ToolExecutionResult,
|
|
@@ -417,10 +417,10 @@ export async function run(
|
|
|
417
417
|
}
|
|
418
418
|
|
|
419
419
|
// Validate API key for api mode
|
|
420
|
+
let openaiKey: string | undefined;
|
|
420
421
|
if (mode === "api") {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (!apiKey) {
|
|
422
|
+
openaiKey = await getSecureKeyAsync("openai");
|
|
423
|
+
if (!openaiKey) {
|
|
424
424
|
return {
|
|
425
425
|
content:
|
|
426
426
|
'No OpenAI API key configured. Set your OpenAI API key to use cloud transcription, or use mode "local" for on-device transcription with whisper.cpp.',
|
|
@@ -441,8 +441,7 @@ export async function run(
|
|
|
441
441
|
|
|
442
442
|
let text: string;
|
|
443
443
|
if (mode === "api") {
|
|
444
|
-
|
|
445
|
-
text = await transcribeViaApi(wavPath, config.apiKeys.openai!, context);
|
|
444
|
+
text = await transcribeViaApi(wavPath, openaiKey!, context);
|
|
446
445
|
} else {
|
|
447
446
|
text = await transcribeViaLocal(wavPath, context);
|
|
448
447
|
}
|
package/src/config/env.ts
CHANGED
|
@@ -154,6 +154,19 @@ export function getPlatformInternalApiKey(): string {
|
|
|
154
154
|
return str("PLATFORM_INTERNAL_API_KEY") ?? "";
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
// ── Telemetry ──────────────────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
export function getTelemetryPlatformUrl(): string {
|
|
160
|
+
return str("TELEMETRY_PLATFORM_URL") ?? "https://platform.vellum.ai";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function getTelemetryAppToken(): string {
|
|
164
|
+
return (
|
|
165
|
+
str("TELEMETRY_APP_TOKEN") ??
|
|
166
|
+
"e01cf85768cc3617e986f0a7f1966b72e25316526c5db54c8b94a9c3c5c9eaed"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
157
170
|
// ── Startup validation ──────────────────────────────────────────────────────
|
|
158
171
|
|
|
159
172
|
/**
|
|
@@ -25,20 +25,12 @@
|
|
|
25
25
|
"description": "Enable browser skill prerequisites section in the system prompt",
|
|
26
26
|
"defaultEnabled": true
|
|
27
27
|
},
|
|
28
|
-
{
|
|
29
|
-
"id": "messaging",
|
|
30
|
-
"scope": "assistant",
|
|
31
|
-
"key": "feature_flags.messaging.enabled",
|
|
32
|
-
"label": "Messaging",
|
|
33
|
-
"description": "Enable messaging skill section in the system prompt",
|
|
34
|
-
"defaultEnabled": true
|
|
35
|
-
},
|
|
36
28
|
{
|
|
37
29
|
"id": "collect-usage-data",
|
|
38
30
|
"scope": "assistant",
|
|
39
31
|
"key": "feature_flags.collect-usage-data.enabled",
|
|
40
32
|
"label": "Collect usage data",
|
|
41
|
-
"description": "Send crash reports
|
|
33
|
+
"description": "Send crash reports, error diagnostics, and anonymized usage telemetry to help improve the app",
|
|
42
34
|
"defaultEnabled": true
|
|
43
35
|
},
|
|
44
36
|
{
|
|
@@ -49,14 +41,6 @@
|
|
|
49
41
|
"description": "Enable user-hosted onboarding flow",
|
|
50
42
|
"defaultEnabled": false
|
|
51
43
|
},
|
|
52
|
-
{
|
|
53
|
-
"id": "command-palette",
|
|
54
|
-
"scope": "assistant",
|
|
55
|
-
"key": "feature_flags.command-palette.enabled",
|
|
56
|
-
"label": "Command Palette",
|
|
57
|
-
"description": "Enable the CMD+K command palette for unified search across conversations, memories, schedules, and contacts",
|
|
58
|
-
"defaultEnabled": false
|
|
59
|
-
},
|
|
60
44
|
{
|
|
61
45
|
"id": "contacts",
|
|
62
46
|
"scope": "assistant",
|
|
@@ -73,14 +57,6 @@
|
|
|
73
57
|
"description": "Show the Email channel card on the Contacts page and enable the email-setup skill",
|
|
74
58
|
"defaultEnabled": false
|
|
75
59
|
},
|
|
76
|
-
{
|
|
77
|
-
"id": "outbound-proxy-sidecar",
|
|
78
|
-
"scope": "assistant",
|
|
79
|
-
"key": "feature_flags.outbound-proxy-sidecar.enabled",
|
|
80
|
-
"label": "Outbound Proxy Sidecar",
|
|
81
|
-
"description": "Route proxy session management through the sidecar process instead of running in-process",
|
|
82
|
-
"defaultEnabled": false
|
|
83
|
-
},
|
|
84
60
|
{
|
|
85
61
|
"id": "app-builder-multifile",
|
|
86
62
|
"scope": "assistant",
|
|
@@ -98,20 +74,12 @@
|
|
|
98
74
|
"defaultEnabled": false
|
|
99
75
|
},
|
|
100
76
|
{
|
|
101
|
-
"id": "
|
|
102
|
-
"scope": "
|
|
103
|
-
"key": "
|
|
104
|
-
"label": "
|
|
105
|
-
"description": "
|
|
106
|
-
"defaultEnabled":
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"id": "sequences",
|
|
110
|
-
"scope": "assistant",
|
|
111
|
-
"key": "feature_flags.sequences.enabled",
|
|
112
|
-
"label": "Email Sequences",
|
|
113
|
-
"description": "Enable email sequence management skill",
|
|
114
|
-
"defaultEnabled": true
|
|
77
|
+
"id": "mobile-pairing",
|
|
78
|
+
"scope": "macos",
|
|
79
|
+
"key": "mobile_pairing_enabled",
|
|
80
|
+
"label": "Mobile Pairing",
|
|
81
|
+
"description": "Show the Mobile (iOS) pairing card in Settings > Account",
|
|
82
|
+
"defaultEnabled": false
|
|
115
83
|
},
|
|
116
84
|
{
|
|
117
85
|
"id": "settings-developer-nav",
|
|
@@ -121,6 +89,14 @@
|
|
|
121
89
|
"description": "Control Developer nav visibility in macOS settings",
|
|
122
90
|
"defaultEnabled": true
|
|
123
91
|
},
|
|
92
|
+
{
|
|
93
|
+
"id": "developer-menu-items",
|
|
94
|
+
"scope": "macos",
|
|
95
|
+
"key": "developer_menu_items_enabled",
|
|
96
|
+
"label": "Developer Menu Items",
|
|
97
|
+
"description": "Show Component Gallery and Replay Onboarding in the menu bar",
|
|
98
|
+
"defaultEnabled": false
|
|
99
|
+
},
|
|
124
100
|
{
|
|
125
101
|
"id": "logfire",
|
|
126
102
|
"scope": "assistant",
|
package/src/config/loader.ts
CHANGED
|
@@ -7,11 +7,6 @@ import {
|
|
|
7
7
|
} from "node:fs";
|
|
8
8
|
import { dirname } from "node:path";
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
deleteSecureKey,
|
|
12
|
-
getSecureKey,
|
|
13
|
-
setSecureKey,
|
|
14
|
-
} from "../security/secure-keys.js";
|
|
15
10
|
import { ConfigError } from "../util/errors.js";
|
|
16
11
|
import { getLogger } from "../util/logger.js";
|
|
17
12
|
import {
|
|
@@ -162,7 +157,6 @@ export function deepMergeMissing(
|
|
|
162
157
|
/**
|
|
163
158
|
* Read the existing config.json from disk, merge any missing schema-default
|
|
164
159
|
* keys, and rewrite only when there is an effective change.
|
|
165
|
-
* Preserves exclusions: apiKeys and dataDir are never written.
|
|
166
160
|
*/
|
|
167
161
|
function backfillConfigDefaults(
|
|
168
162
|
configPath: string,
|
|
@@ -197,9 +191,9 @@ function backfillConfigDefaults(
|
|
|
197
191
|
export function loadConfig(): AssistantConfig {
|
|
198
192
|
if (cached) return cached;
|
|
199
193
|
|
|
200
|
-
// Re-entrancy guard: log calls during loading (e.g. file-mode warning
|
|
201
|
-
//
|
|
202
|
-
//
|
|
194
|
+
// Re-entrancy guard: log calls during loading (e.g. file-mode warning)
|
|
195
|
+
// can trigger loadConfig again. Return defaults to break the cycle
|
|
196
|
+
// instead of recursing to stack overflow.
|
|
203
197
|
if (loading) return cloneDefaultConfig();
|
|
204
198
|
loading = true;
|
|
205
199
|
|
|
@@ -230,67 +224,6 @@ export function loadConfig(): AssistantConfig {
|
|
|
230
224
|
configFileExisted = false;
|
|
231
225
|
}
|
|
232
226
|
|
|
233
|
-
// Pre-validate apiKeys shape before migration (must be a plain object)
|
|
234
|
-
if (
|
|
235
|
-
fileConfig.apiKeys !== undefined &&
|
|
236
|
-
(typeof fileConfig.apiKeys !== "object" ||
|
|
237
|
-
fileConfig.apiKeys == null ||
|
|
238
|
-
Array.isArray(fileConfig.apiKeys))
|
|
239
|
-
) {
|
|
240
|
-
log.warn(
|
|
241
|
-
"Invalid apiKeys in config file: must be an object with string values. Ignoring.",
|
|
242
|
-
);
|
|
243
|
-
delete fileConfig.apiKeys;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Auto-migrate plaintext apiKeys from config.json to secure storage
|
|
247
|
-
if (fileConfig.apiKeys && typeof fileConfig.apiKeys === "object") {
|
|
248
|
-
const apiKeysObj = fileConfig.apiKeys as Record<string, unknown>;
|
|
249
|
-
const plaintextKeys = Object.entries(apiKeysObj).filter(
|
|
250
|
-
([, v]) => typeof v === "string" && (v as string).length > 0,
|
|
251
|
-
);
|
|
252
|
-
if (plaintextKeys.length > 0) {
|
|
253
|
-
const migratedProviders: string[] = [];
|
|
254
|
-
for (const [provider, value] of plaintextKeys) {
|
|
255
|
-
if (setSecureKey(provider, value as string)) {
|
|
256
|
-
migratedProviders.push(provider);
|
|
257
|
-
} else {
|
|
258
|
-
log.warn(
|
|
259
|
-
`Failed to migrate API key for "${provider}" to secure storage`,
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
if (migratedProviders.length > 0) {
|
|
264
|
-
// Rewrite config.json without successfully migrated apiKeys
|
|
265
|
-
try {
|
|
266
|
-
const rawJson = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
267
|
-
for (const p of migratedProviders) {
|
|
268
|
-
delete rawJson.apiKeys[p];
|
|
269
|
-
}
|
|
270
|
-
if (Object.keys(rawJson.apiKeys).length === 0) {
|
|
271
|
-
delete rawJson.apiKeys;
|
|
272
|
-
}
|
|
273
|
-
writeFileSync(configPath, JSON.stringify(rawJson, null, 2) + "\n");
|
|
274
|
-
log.info(
|
|
275
|
-
`Migrated ${migratedProviders.length} API key(s) from config.json to secure storage`,
|
|
276
|
-
);
|
|
277
|
-
} catch (err) {
|
|
278
|
-
log.warn(
|
|
279
|
-
{ err },
|
|
280
|
-
"Failed to remove migrated keys from config.json",
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
// Clear only migrated keys from fileConfig so failed keys still flow into config
|
|
285
|
-
for (const p of migratedProviders) {
|
|
286
|
-
delete apiKeysObj[p];
|
|
287
|
-
}
|
|
288
|
-
if (Object.keys(apiKeysObj).length === 0) {
|
|
289
|
-
delete fileConfig.apiKeys;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
227
|
// Validate and apply defaults via Zod schema
|
|
295
228
|
const config = validateWithSchema(fileConfig);
|
|
296
229
|
|
|
@@ -303,8 +236,8 @@ export function loadConfig(): AssistantConfig {
|
|
|
303
236
|
if (!existsSync(dir)) {
|
|
304
237
|
mkdirSync(dir, { recursive: true });
|
|
305
238
|
}
|
|
306
|
-
// Strip
|
|
307
|
-
const {
|
|
239
|
+
// Strip dataDir (runtime-derived) from the persisted config
|
|
240
|
+
const { dataDir: _, ...persistable } = config;
|
|
308
241
|
|
|
309
242
|
if (!configFileExisted) {
|
|
310
243
|
writeFileSync(configPath, JSON.stringify(persistable, null, 2) + "\n");
|
|
@@ -316,48 +249,8 @@ export function loadConfig(): AssistantConfig {
|
|
|
316
249
|
log.warn({ err }, "Failed to write/backfill config file");
|
|
317
250
|
}
|
|
318
251
|
|
|
319
|
-
// Set cached before secure-key/env overrides so re-entrant calls
|
|
320
|
-
// return the in-flight config instead of bare defaults.
|
|
321
252
|
cached = config;
|
|
322
253
|
|
|
323
|
-
// Secure storage keys override plaintext config file
|
|
324
|
-
try {
|
|
325
|
-
for (const provider of API_KEY_PROVIDERS) {
|
|
326
|
-
const secureKey = getSecureKey(provider);
|
|
327
|
-
if (secureKey) {
|
|
328
|
-
config.apiKeys[provider] = secureKey;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
} catch (err) {
|
|
332
|
-
log.debug({ err }, "Failed to load keys from secure storage");
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Environment variables override everything
|
|
336
|
-
if (process.env.ANTHROPIC_API_KEY) {
|
|
337
|
-
config.apiKeys.anthropic = process.env.ANTHROPIC_API_KEY;
|
|
338
|
-
}
|
|
339
|
-
if (process.env.OPENAI_API_KEY) {
|
|
340
|
-
config.apiKeys.openai = process.env.OPENAI_API_KEY;
|
|
341
|
-
}
|
|
342
|
-
if (process.env.GEMINI_API_KEY) {
|
|
343
|
-
config.apiKeys.gemini = process.env.GEMINI_API_KEY;
|
|
344
|
-
}
|
|
345
|
-
if (process.env.OLLAMA_API_KEY) {
|
|
346
|
-
config.apiKeys.ollama = process.env.OLLAMA_API_KEY;
|
|
347
|
-
}
|
|
348
|
-
if (process.env.FIREWORKS_API_KEY) {
|
|
349
|
-
config.apiKeys.fireworks = process.env.FIREWORKS_API_KEY;
|
|
350
|
-
}
|
|
351
|
-
if (process.env.OPENROUTER_API_KEY) {
|
|
352
|
-
config.apiKeys.openrouter = process.env.OPENROUTER_API_KEY;
|
|
353
|
-
}
|
|
354
|
-
if (process.env.BRAVE_API_KEY) {
|
|
355
|
-
config.apiKeys.brave = process.env.BRAVE_API_KEY;
|
|
356
|
-
}
|
|
357
|
-
if (process.env.PERPLEXITY_API_KEY) {
|
|
358
|
-
config.apiKeys.perplexity = process.env.PERPLEXITY_API_KEY;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
254
|
loading = false;
|
|
362
255
|
return config;
|
|
363
256
|
} catch (err) {
|
|
@@ -372,25 +265,7 @@ export function saveConfig(config: AssistantConfig): void {
|
|
|
372
265
|
ensureMigratedDataDir();
|
|
373
266
|
const configPath = getConfigPath();
|
|
374
267
|
|
|
375
|
-
|
|
376
|
-
for (const [provider, value] of Object.entries(config.apiKeys)) {
|
|
377
|
-
if (typeof value === "string" && value.length > 0) {
|
|
378
|
-
if (!setSecureKey(provider, value)) {
|
|
379
|
-
throw new ConfigError(
|
|
380
|
-
`Failed to save API key for "${provider}" to secure storage`,
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
// Delete secure keys for providers no longer in apiKeys or with empty values
|
|
386
|
-
for (const provider of API_KEY_PROVIDERS) {
|
|
387
|
-
const value = config.apiKeys[provider];
|
|
388
|
-
if (!value || (typeof value === "string" && value.length === 0)) {
|
|
389
|
-
deleteSecureKey(provider);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
const { apiKeys: _, ...rest } = config;
|
|
393
|
-
writeFileSync(configPath, JSON.stringify(rest, null, 2) + "\n");
|
|
268
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
394
269
|
|
|
395
270
|
cached = config;
|
|
396
271
|
}
|
|
@@ -428,10 +303,7 @@ export function saveRawConfig(config: Record<string, unknown>): void {
|
|
|
428
303
|
ensureMigratedDataDir();
|
|
429
304
|
const configPath = getConfigPath();
|
|
430
305
|
|
|
431
|
-
|
|
432
|
-
// by saveConfig() and `assistant keys` commands, not here.
|
|
433
|
-
const { apiKeys: _, ...rest } = config;
|
|
434
|
-
writeFileSync(configPath, JSON.stringify(rest, null, 2) + "\n");
|
|
306
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
435
307
|
|
|
436
308
|
cached = null; // invalidate cache
|
|
437
309
|
}
|
package/src/config/schema.ts
CHANGED
|
@@ -232,12 +232,6 @@ export const AssistantConfigSchema = z
|
|
|
232
232
|
imageGenModel: z
|
|
233
233
|
.string({ error: "imageGenModel must be a string" })
|
|
234
234
|
.default("gemini-2.5-flash-image"),
|
|
235
|
-
apiKeys: z
|
|
236
|
-
.record(
|
|
237
|
-
z.string(),
|
|
238
|
-
z.string({ error: "Each apiKeys value must be a string" }),
|
|
239
|
-
)
|
|
240
|
-
.default({} as Record<string, string>),
|
|
241
235
|
webSearchProvider: z
|
|
242
236
|
.enum(VALID_WEB_SEARCH_PROVIDERS, {
|
|
243
237
|
error: `webSearchProvider must be one of: ${VALID_WEB_SEARCH_PROVIDERS.join(
|
|
@@ -34,6 +34,7 @@ export const WhatsAppConfigSchema = z.object({
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
export const TelegramConfigSchema = z.object({
|
|
37
|
+
botId: z.string({ error: "telegram.botId must be a string" }).default(""),
|
|
37
38
|
botUsername: z
|
|
38
39
|
.string({ error: "telegram.botUsername must be a string" })
|
|
39
40
|
.default(""),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
// Default ElevenLabs voice — "
|
|
3
|
+
// Default ElevenLabs voice — "Amelia" (expressive, enthusiastic, British English).
|
|
4
4
|
// Used by both in-app TTS and phone calls (via Twilio ConversationRelay).
|
|
5
5
|
// Mirrored in: clients/macos/.../OpenAIVoiceService.swift (defaultVoiceId)
|
|
6
|
-
export const DEFAULT_ELEVENLABS_VOICE_ID = "
|
|
6
|
+
export const DEFAULT_ELEVENLABS_VOICE_ID = "ZF6FPAbjXT4488VcRRnw";
|
|
7
7
|
|
|
8
8
|
export const ElevenLabsConfigSchema = z.object({
|
|
9
9
|
voiceId: z
|
|
@@ -5,7 +5,7 @@ const VALID_PERMISSIONS_MODES = ["strict", "workspace"] as const;
|
|
|
5
5
|
|
|
6
6
|
export { VALID_PERMISSIONS_MODES, VALID_SECRET_ACTIONS };
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const CustomSecretPatternSchema = z.object({
|
|
9
9
|
label: z.string({
|
|
10
10
|
error: "secretDetection.customPatterns[].label must be a string",
|
|
11
11
|
}),
|
|
@@ -49,6 +49,5 @@ export const PermissionsConfigSchema = z.object({
|
|
|
49
49
|
.default("workspace"),
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
export type CustomSecretPattern = z.infer<typeof CustomSecretPatternSchema>;
|
|
53
52
|
export type SecretDetectionConfig = z.infer<typeof SecretDetectionConfigSchema>;
|
|
54
53
|
export type PermissionsConfig = z.infer<typeof PermissionsConfigSchema>;
|
package/src/config/skills.ts
CHANGED
|
@@ -1294,7 +1294,7 @@ async function generateSkillIcon(
|
|
|
1294
1294
|
name: string,
|
|
1295
1295
|
description: string,
|
|
1296
1296
|
): Promise<string> {
|
|
1297
|
-
const provider = getConfiguredProvider();
|
|
1297
|
+
const provider = await getConfiguredProvider();
|
|
1298
1298
|
if (!provider) {
|
|
1299
1299
|
throw new Error("Configured provider unavailable for icon generation");
|
|
1300
1300
|
}
|
|
@@ -2,7 +2,6 @@ import { and, asc, desc, eq, like, sql } from "drizzle-orm";
|
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
4
|
import { getDb } from "../memory/db.js";
|
|
5
|
-
import { rawChanges } from "../memory/raw-query.js";
|
|
6
5
|
import {
|
|
7
6
|
assistantContactMetadata,
|
|
8
7
|
contactChannels,
|
|
@@ -34,8 +33,8 @@ function parseContact(row: typeof contacts.$inferSelect): Contact {
|
|
|
34
33
|
id: row.id,
|
|
35
34
|
displayName: row.displayName,
|
|
36
35
|
notes: row.notes,
|
|
37
|
-
lastInteraction:
|
|
38
|
-
interactionCount:
|
|
36
|
+
lastInteraction: null,
|
|
37
|
+
interactionCount: 0,
|
|
39
38
|
createdAt: row.createdAt,
|
|
40
39
|
updatedAt: row.updatedAt,
|
|
41
40
|
role: row.role as Contact["role"],
|
|
@@ -63,6 +62,8 @@ function parseChannel(
|
|
|
63
62
|
revokedReason: row.revokedReason,
|
|
64
63
|
blockedReason: row.blockedReason,
|
|
65
64
|
lastSeenAt: row.lastSeenAt,
|
|
65
|
+
interactionCount: row.interactionCount,
|
|
66
|
+
lastInteraction: row.lastInteraction,
|
|
66
67
|
updatedAt: row.updatedAt,
|
|
67
68
|
createdAt: row.createdAt,
|
|
68
69
|
};
|
|
@@ -80,7 +81,15 @@ function getChannelsForContact(contactId: string): ContactChannel[] {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
function withChannels(contact: Contact): ContactWithChannels {
|
|
83
|
-
|
|
84
|
+
const channels = getChannelsForContact(contact.id);
|
|
85
|
+
const interactionCount = channels.reduce(
|
|
86
|
+
(sum, ch) => sum + ch.interactionCount,
|
|
87
|
+
0,
|
|
88
|
+
);
|
|
89
|
+
const lastInteraction =
|
|
90
|
+
channels.reduce((max, ch) => Math.max(max, ch.lastInteraction ?? 0), 0) ||
|
|
91
|
+
null;
|
|
92
|
+
return { ...contact, interactionCount, lastInteraction, channels };
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
// ── Channel data type for syncChannels ───────────────────────────────
|
|
@@ -235,8 +244,6 @@ export function upsertContact(params: {
|
|
|
235
244
|
id: contactId,
|
|
236
245
|
displayName: params.displayName,
|
|
237
246
|
notes: params.notes ?? null,
|
|
238
|
-
lastInteraction: null,
|
|
239
|
-
interactionCount: 0,
|
|
240
247
|
role: params.role ?? "contact",
|
|
241
248
|
contactType: params.contactType ?? "human",
|
|
242
249
|
principalId: params.principalId ?? null,
|
|
@@ -488,7 +495,7 @@ export function searchContacts(params: {
|
|
|
488
495
|
.from(contacts)
|
|
489
496
|
.innerJoin(contactChannels, eq(contacts.id, contactChannels.contactId))
|
|
490
497
|
.where(whereClause)
|
|
491
|
-
.orderBy(desc(contacts.updatedAt)
|
|
498
|
+
.orderBy(desc(contacts.updatedAt))
|
|
492
499
|
.all();
|
|
493
500
|
|
|
494
501
|
const contactIds = [...new Set(rows.map((r) => r.contactId))];
|
|
@@ -509,7 +516,7 @@ export function searchContacts(params: {
|
|
|
509
516
|
.select()
|
|
510
517
|
.from(contacts)
|
|
511
518
|
.where(whereClause)
|
|
512
|
-
.orderBy(desc(contacts.updatedAt)
|
|
519
|
+
.orderBy(desc(contacts.updatedAt))
|
|
513
520
|
.limit(limit)
|
|
514
521
|
.all();
|
|
515
522
|
|
|
@@ -531,11 +538,7 @@ export function listContacts(
|
|
|
531
538
|
.select()
|
|
532
539
|
.from(contacts)
|
|
533
540
|
.where(conditions.length === 1 ? conditions[0] : and(...conditions))
|
|
534
|
-
.orderBy(
|
|
535
|
-
sql`${contacts.role} = 'guardian' DESC`,
|
|
536
|
-
desc(contacts.updatedAt),
|
|
537
|
-
desc(contacts.lastInteraction),
|
|
538
|
-
)
|
|
541
|
+
.orderBy(sql`${contacts.role} = 'guardian' DESC`, desc(contacts.updatedAt))
|
|
539
542
|
.limit(effectiveLimit)
|
|
540
543
|
.all();
|
|
541
544
|
return rows.map((r) => withChannels(parseContact(r)));
|
|
@@ -571,16 +574,8 @@ export function mergeContacts(
|
|
|
571
574
|
.get();
|
|
572
575
|
if (!merge) throw new Error(`Contact "${mergeId}" not found`);
|
|
573
576
|
|
|
574
|
-
// Resolve merged field values — pick the better/more recent value
|
|
575
|
-
const mergedInteractionCount =
|
|
576
|
-
keep.interactionCount + merge.interactionCount;
|
|
577
|
-
const mergedLastInteraction =
|
|
578
|
-
Math.max(keep.lastInteraction ?? 0, merge.lastInteraction ?? 0) || null;
|
|
579
|
-
|
|
580
577
|
tx.update(contacts)
|
|
581
578
|
.set({
|
|
582
|
-
interactionCount: mergedInteractionCount,
|
|
583
|
-
lastInteraction: mergedLastInteraction,
|
|
584
579
|
notes: [keep.notes, merge.notes].filter(Boolean).join("\n") || null,
|
|
585
580
|
updatedAt: now,
|
|
586
581
|
})
|
|
@@ -792,46 +787,6 @@ export function findGuardianForChannel(
|
|
|
792
787
|
};
|
|
793
788
|
}
|
|
794
789
|
|
|
795
|
-
/**
|
|
796
|
-
* Revoke the guardian's active channel of the given type by setting its
|
|
797
|
-
* status to 'revoked'. This ensures findGuardianForChannel() no longer
|
|
798
|
-
* returns stale data after a binding is revoked.
|
|
799
|
-
*
|
|
800
|
-
* Returns true if a channel was found and revoked, false otherwise.
|
|
801
|
-
*/
|
|
802
|
-
export function revokeGuardianChannel(channelType: string): boolean {
|
|
803
|
-
const db = getDb();
|
|
804
|
-
const conditions = [
|
|
805
|
-
eq(contacts.role, "guardian"),
|
|
806
|
-
eq(contactChannels.type, channelType),
|
|
807
|
-
eq(contactChannels.status, "active"),
|
|
808
|
-
];
|
|
809
|
-
const rows = db
|
|
810
|
-
.select({
|
|
811
|
-
channelId: contactChannels.id,
|
|
812
|
-
})
|
|
813
|
-
.from(contacts)
|
|
814
|
-
.innerJoin(contactChannels, eq(contacts.id, contactChannels.contactId))
|
|
815
|
-
.where(and(...conditions))
|
|
816
|
-
.all();
|
|
817
|
-
|
|
818
|
-
if (rows.length === 0) return false;
|
|
819
|
-
|
|
820
|
-
const now = Date.now();
|
|
821
|
-
for (const row of rows) {
|
|
822
|
-
db.update(contactChannels)
|
|
823
|
-
.set({
|
|
824
|
-
status: "revoked",
|
|
825
|
-
revokedReason: "guardian_binding_revoked",
|
|
826
|
-
updatedAt: now,
|
|
827
|
-
})
|
|
828
|
-
.where(eq(contactChannels.id, row.channelId))
|
|
829
|
-
.run();
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
return true;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
790
|
/**
|
|
836
791
|
* List all active channels for guardian contacts.
|
|
837
792
|
* This is the contacts-based equivalent of listActiveBindingsByAssistant(assistantId).
|
|
@@ -934,19 +889,19 @@ export function updateChannelLastSeenById(channelId: string): void {
|
|
|
934
889
|
}
|
|
935
890
|
|
|
936
891
|
/**
|
|
937
|
-
* Atomically increment interactionCount and set lastInteraction on a contact.
|
|
892
|
+
* Atomically increment interactionCount and set lastInteraction on a contact channel.
|
|
938
893
|
* Optimized for the hot path — single UPDATE with no prior SELECT.
|
|
939
894
|
*/
|
|
940
|
-
export function
|
|
895
|
+
export function updateChannelInteraction(channelId: string): void {
|
|
941
896
|
const db = getDb();
|
|
942
897
|
const now = Date.now();
|
|
943
|
-
db.update(
|
|
898
|
+
db.update(contactChannels)
|
|
944
899
|
.set({
|
|
945
900
|
lastInteraction: now,
|
|
946
|
-
interactionCount: sql`${
|
|
901
|
+
interactionCount: sql`${contactChannels.interactionCount} + 1`,
|
|
947
902
|
updatedAt: now,
|
|
948
903
|
})
|
|
949
|
-
.where(eq(
|
|
904
|
+
.where(eq(contactChannels.id, channelId))
|
|
950
905
|
.run();
|
|
951
906
|
}
|
|
952
907
|
|
|
@@ -1037,12 +992,3 @@ export function getAssistantContactMetadata(
|
|
|
1037
992
|
if (!row) return null;
|
|
1038
993
|
return parseAssistantMetadata(row);
|
|
1039
994
|
}
|
|
1040
|
-
|
|
1041
|
-
export function deleteAssistantContactMetadata(contactId: string): boolean {
|
|
1042
|
-
const db = getDb();
|
|
1043
|
-
db.delete(assistantContactMetadata)
|
|
1044
|
-
.where(eq(assistantContactMetadata.contactId, contactId))
|
|
1045
|
-
.run();
|
|
1046
|
-
|
|
1047
|
-
return rawChanges() > 0;
|
|
1048
|
-
}
|
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
findGuardianForChannel,
|
|
17
17
|
getChannelById,
|
|
18
18
|
getContactInternal,
|
|
19
|
+
updateChannelInteraction,
|
|
19
20
|
updateChannelLastSeenById,
|
|
20
21
|
updateChannelStatus,
|
|
21
|
-
updateContactInteraction,
|
|
22
22
|
upsertContact,
|
|
23
23
|
} from "./contact-store.js";
|
|
24
24
|
import type {
|
|
@@ -287,13 +287,13 @@ export function touchChannelLastSeen(channelId: string): void {
|
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
290
|
+
* Track an interaction on the specific channel that received it.
|
|
291
|
+
* Swallows errors to avoid disrupting the inbound message hot path.
|
|
292
292
|
*/
|
|
293
|
-
export function touchContactInteraction(
|
|
293
|
+
export function touchContactInteraction(channelId: string): void {
|
|
294
294
|
try {
|
|
295
|
-
|
|
295
|
+
updateChannelInteraction(channelId);
|
|
296
296
|
} catch (err) {
|
|
297
|
-
log.warn({ err }, "Failed to update
|
|
297
|
+
log.warn({ err }, "Failed to update channel interaction stats");
|
|
298
298
|
}
|
|
299
299
|
}
|