@vellumai/assistant 0.4.56 → 0.5.0
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 +10 -10
- package/Dockerfile +3 -0
- package/README.md +11 -11
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/memory.md +3 -4
- package/docs/credential-execution-service.md +13 -20
- package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +7 -7
- package/src/__tests__/anthropic-provider.test.ts +172 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
- package/src/__tests__/approval-cascade.test.ts +2 -2
- package/src/__tests__/approval-routes-http.test.ts +3 -4
- package/src/__tests__/asset-materialize-tool.test.ts +5 -5
- package/src/__tests__/asset-search-tool.test.ts +1 -1
- package/src/__tests__/assistant-attachments.test.ts +5 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
- package/src/__tests__/attachments-store.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +5 -3
- package/src/__tests__/browser-skill-endstate.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +2 -2
- package/src/__tests__/callback-handoff-copy.test.ts +1 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +31 -32
- package/src/__tests__/chrome-cdp.test.ts +47 -18
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +9 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop.test.ts +11 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +33 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
- package/src/__tests__/conversation-queue.test.ts +23 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +204 -185
- package/src/__tests__/conversation-seed-composer.test.ts +1 -1
- package/src/__tests__/conversation-slash-queue.test.ts +4 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
- package/src/__tests__/conversation-starter-routes.test.ts +291 -0
- package/src/__tests__/conversation-wipe.test.ts +438 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
- package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
- package/src/__tests__/credential-security-e2e.test.ts +20 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +227 -0
- package/src/__tests__/credentials-cli.test.ts +3 -0
- package/src/__tests__/date-context.test.ts +59 -377
- package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
- package/src/__tests__/encrypted-store.test.ts +249 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/file-read-tool.test.ts +40 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +4 -4
- package/src/__tests__/gemini-provider.test.ts +6 -9
- package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/host-file-read-tool.test.ts +87 -0
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +209 -0
- package/src/__tests__/intent-routing.test.ts +51 -99
- package/src/__tests__/invite-routes-http.test.ts +5 -0
- package/src/__tests__/list-messages-attachments.test.ts +1 -1
- package/src/__tests__/managed-proxy-context.test.ts +2 -5
- package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
- package/src/__tests__/media-generate-image.test.ts +32 -15
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
- package/src/__tests__/memory-recall-quality.test.ts +4 -3
- package/src/__tests__/memory-regressions.test.ts +86 -90
- package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
- package/src/__tests__/migration-export-http.test.ts +26 -27
- package/src/__tests__/migration-import-commit-http.test.ts +165 -37
- package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
- package/src/__tests__/migration-validate-http.test.ts +16 -16
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/non-member-access-request.test.ts +3 -3
- package/src/__tests__/notification-broadcaster.test.ts +1 -1
- package/src/__tests__/notification-decision-fallback.test.ts +2 -2
- package/src/__tests__/notification-decision-identity.test.ts +8 -9
- package/src/__tests__/notification-decision-strategy.test.ts +1 -1
- package/src/__tests__/notification-deep-link.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
- package/src/__tests__/onboarding-template-contract.test.ts +23 -59
- package/src/__tests__/provider-error-scenarios.test.ts +154 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
- package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
- package/src/__tests__/provider-registry-ollama.test.ts +5 -2
- package/src/__tests__/qdrant-manager.test.ts +7 -7
- package/src/__tests__/ratelimit.test.ts +0 -74
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/scheduler-recurrence.test.ts +46 -2
- package/src/__tests__/schema-transforms.test.ts +114 -54
- package/src/__tests__/secret-onetime-send.test.ts +20 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
- package/src/__tests__/secret-scanner-executor.test.ts +1 -2
- package/src/__tests__/send-endpoint-busy.test.ts +63 -4
- package/src/__tests__/send-notification-tool.test.ts +2 -2
- package/src/__tests__/shell-credential-ref.test.ts +0 -1
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
- package/src/__tests__/skill-memory.test.ts +549 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
- package/src/__tests__/slack-channel-config.test.ts +109 -94
- package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
- package/src/__tests__/swarm-recursion.test.ts +2 -2
- package/src/__tests__/swarm-tool.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +19 -66
- package/src/__tests__/telegram-config.test.ts +121 -0
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trace-emitter.test.ts +8 -1
- package/src/__tests__/trust-store.test.ts +7 -8
- package/src/__tests__/twilio-routes.test.ts +1 -18
- package/src/__tests__/user-reference.test.ts +82 -2
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +3 -3
- package/src/avatar/ascii-renderer.ts +2 -2
- package/src/avatar/png-renderer.ts +2 -2
- package/src/avatar/resvg-lazy.ts +21 -0
- package/src/calls/guardian-dispatch.ts +1 -1
- package/src/calls/relay-access-wait.ts +2 -2
- package/src/calls/twilio-rest.ts +0 -248
- package/src/cli/AGENTS.md +5 -8
- package/src/cli/__tests__/notifications.test.ts +5 -5
- package/src/cli/commands/avatar.ts +64 -2
- package/src/cli/commands/conversations.ts +131 -1
- package/src/cli/commands/credentials.ts +2 -0
- package/src/cli/commands/notifications.ts +3 -3
- package/src/cli.ts +10 -0
- package/src/config/bundled-skills/acp/SKILL.md +5 -5
- package/src/config/bundled-skills/acp/TOOLS.json +6 -6
- package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
- package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
- package/src/config/bundled-skills/browser/SKILL.md +15 -15
- package/src/config/bundled-skills/browser/TOOLS.json +14 -14
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
- package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
- package/src/config/bundled-skills/contacts/SKILL.md +3 -3
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
- package/src/config/bundled-skills/document/SKILL.md +4 -4
- package/src/config/bundled-skills/document/TOOLS.json +2 -2
- package/src/config/bundled-skills/followups/TOOLS.json +3 -3
- package/src/config/bundled-skills/gmail/SKILL.md +32 -32
- package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
- package/src/config/bundled-skills/google-calendar/types.ts +1 -1
- package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
- package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
- package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
- package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +29 -25
- package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +3 -3
- package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
- package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
- package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
- package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
- package/src/config/bundled-skills/schedule/SKILL.md +26 -26
- package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
- package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
- package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +2 -2
- package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
- package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
- package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
- package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
- package/src/config/bundled-skills/slack/SKILL.md +2 -2
- package/src/config/bundled-skills/slack/TOOLS.json +8 -8
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
- package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
- package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
- package/src/config/bundled-skills/watcher/SKILL.md +4 -4
- package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
- package/src/config/feature-flag-registry.json +33 -17
- package/src/config/schemas/sandbox.ts +1 -1
- package/src/config/schemas/services.ts +13 -3
- package/src/config/schemas/timeouts.ts +0 -10
- package/src/contacts/contact-store.ts +63 -0
- package/src/contacts/contacts-write.ts +1 -1
- package/src/daemon/assistant-attachments.ts +2 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
- package/src/daemon/conversation-agent-loop.ts +7 -30
- package/src/daemon/conversation-error.ts +24 -0
- package/src/daemon/conversation-memory.ts +8 -7
- package/src/daemon/conversation-runtime-assembly.ts +141 -275
- package/src/daemon/conversation-slash.ts +7 -26
- package/src/daemon/conversation-surfaces.ts +14 -0
- package/src/daemon/conversation-tool-setup.ts +9 -8
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +10 -83
- package/src/daemon/handlers/config-channels.ts +12 -2
- package/src/daemon/handlers/config-slack-channel.ts +7 -1
- package/src/daemon/handlers/config-telegram.ts +6 -1
- package/src/daemon/handlers/conversations.ts +2 -2
- package/src/daemon/handlers/skills.ts +4 -0
- package/src/daemon/lifecycle.ts +28 -4
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +1 -5
- package/src/daemon/shutdown-handlers.ts +9 -3
- package/src/daemon/tool-side-effects.ts +40 -0
- package/src/daemon/trace-emitter.ts +26 -2
- package/src/events/domain-events.ts +1 -1
- package/src/events/tool-permission-telemetry-listener.ts +46 -0
- package/src/inbound/platform-callback-registration.ts +0 -18
- package/src/media/app-icon-generator.ts +15 -8
- package/src/media/avatar-router.ts +15 -8
- package/src/media/gemini-image-service.ts +125 -21
- package/src/memory/attachments-store.ts +3 -3
- package/src/memory/channel-verification-sessions.ts +6 -6
- package/src/memory/conversation-crud.ts +196 -1
- package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
- package/src/memory/conversation-title-service.ts +2 -3
- package/src/memory/db-init.ts +25 -1
- package/src/memory/invite-store.ts +4 -4
- package/src/memory/items-extractor.ts +4 -4
- package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/jobs-worker.ts +7 -5
- package/src/memory/lifecycle-events-store.ts +63 -0
- package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
- package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
- package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
- package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
- package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
- package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +223 -96
- package/src/memory/retriever.ts +115 -138
- package/src/memory/schema/calls.ts +1 -1
- package/src/memory/schema/contacts.ts +1 -1
- package/src/memory/schema/infrastructure.ts +29 -0
- package/src/memory/schema/memory-core.ts +7 -17
- package/src/memory/schema/notifications.ts +1 -1
- package/src/memory/search/formatting.ts +23 -6
- package/src/memory/search/lexical.ts +2 -0
- package/src/memory/search/semantic.ts +2 -0
- package/src/memory/search/staleness.ts +5 -1
- package/src/memory/search/types.ts +4 -0
- package/src/memory/task-memory-cleanup.ts +96 -6
- package/src/memory/trace-event-store.ts +148 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/decision-engine.ts +45 -4
- package/src/notifications/emit-signal.ts +5 -4
- package/src/notifications/events-store.ts +4 -4
- package/src/notifications/signal.ts +1 -1
- package/src/oauth/manual-token-connection.ts +49 -25
- package/src/permissions/checker.ts +6 -5
- package/src/permissions/defaults.ts +4 -4
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
- package/src/prompts/cache-boundary.ts +8 -0
- package/src/prompts/system-prompt.ts +105 -634
- package/src/prompts/templates/BOOTSTRAP.md +172 -33
- package/src/prompts/templates/IDENTITY.md +8 -24
- package/src/prompts/templates/SOUL.md +20 -41
- package/src/prompts/templates/USER.md +3 -19
- package/src/prompts/user-reference.ts +14 -16
- package/src/providers/anthropic/client.ts +51 -19
- package/src/providers/gemini/client.ts +6 -9
- package/src/providers/managed-proxy/constants.ts +1 -7
- package/src/providers/managed-proxy/context.ts +0 -1
- package/src/providers/model-intents.ts +5 -5
- package/src/providers/openai/client.ts +10 -1
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/ratelimit.ts +0 -35
- package/src/providers/registry.ts +3 -5
- package/src/providers/retry.ts +18 -1
- package/src/runtime/access-request-helper.ts +16 -2
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/channel-verification-service.ts +1 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/guardian-vellum-migration.ts +61 -1
- package/src/runtime/http-server.ts +8 -4
- package/src/runtime/migrations/vbundle-builder.ts +212 -32
- package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
- package/src/runtime/migrations/vbundle-importer.ts +66 -1
- package/src/runtime/migrations/vbundle-validator.ts +17 -3
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
- package/src/runtime/routes/attachment-routes.ts +2 -2
- package/src/runtime/routes/btw-routes.ts +93 -0
- package/src/runtime/routes/channel-verification-routes.ts +19 -2
- package/src/runtime/routes/conversation-management-routes.ts +55 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +49 -5
- package/src/runtime/routes/conversation-starter-routes.ts +207 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
- package/src/runtime/routes/identity-intro-cache.ts +105 -0
- package/src/runtime/routes/identity-routes.ts +51 -0
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +25 -13
- package/src/runtime/routes/secret-routes.ts +18 -0
- package/src/runtime/routes/settings-routes.ts +9 -9
- package/src/runtime/routes/telemetry-routes.ts +53 -0
- package/src/runtime/routes/trace-event-routes.ts +62 -0
- package/src/runtime/tool-grant-request-helper.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +47 -31
- package/src/security/encrypted-store.ts +262 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +222 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
- package/src/telemetry/usage-telemetry-reporter.ts +53 -4
- package/src/tools/AGENTS.md +11 -11
- package/src/tools/acp/spawn.ts +1 -1
- package/src/tools/apps/executors.ts +8 -8
- package/src/tools/apps/registry.ts +1 -1
- package/src/tools/assets/materialize.ts +6 -6
- package/src/tools/assets/search.ts +10 -10
- package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
- package/src/tools/browser/auth-detector.ts +6 -6
- package/src/tools/browser/browser-execution.ts +13 -13
- package/src/tools/browser/browser-manager.ts +3 -3
- package/src/tools/browser/chrome-cdp.ts +5 -5
- package/src/tools/browser/jit-auth.ts +2 -2
- package/src/tools/browser/network-recorder.test.ts +2 -2
- package/src/tools/browser/network-recorder.ts +3 -3
- package/src/tools/browser/runtime-check.ts +3 -3
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/computer-use/definitions.ts +18 -18
- package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
- package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
- package/src/tools/credentials/broker-types.ts +5 -5
- package/src/tools/credentials/broker.ts +15 -15
- package/src/tools/credentials/metadata-store.ts +2 -2
- package/src/tools/credentials/resolve.ts +1 -1
- package/src/tools/credentials/selection.ts +1 -1
- package/src/tools/credentials/tool-policy.ts +1 -1
- package/src/tools/credentials/vault.ts +115 -25
- package/src/tools/execution-target.ts +2 -2
- package/src/tools/executor.ts +7 -7
- package/src/tools/filesystem/edit.ts +2 -2
- package/src/tools/filesystem/read.ts +15 -4
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +18 -1
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +9 -8
- package/src/tools/mcp/mcp-tool-factory.ts +7 -6
- package/src/tools/memory/definitions.ts +6 -5
- package/src/tools/memory/handlers.test.ts +1 -1
- package/src/tools/network/__tests__/web-search.test.ts +3 -3
- package/src/tools/network/domain-normalize.ts +2 -2
- package/src/tools/network/script-proxy/session-manager.ts +10 -10
- package/src/tools/network/web-fetch.ts +1 -1
- package/src/tools/network/web-search.ts +3 -3
- package/src/tools/permission-checker.ts +8 -8
- package/src/tools/registry.ts +7 -7
- package/src/tools/schedule/list.ts +2 -2
- package/src/tools/schema-transforms.ts +31 -21
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/sensitive-output-placeholders.ts +1 -1
- package/src/tools/shared/filesystem/edit-engine.ts +1 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
- package/src/tools/shared/filesystem/image-read.ts +25 -5
- package/src/tools/shared/filesystem/path-policy.ts +2 -2
- package/src/tools/shared/shell-output.ts +1 -1
- package/src/tools/side-effects.ts +1 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +3 -3
- package/src/tools/skills/sandbox-runner.ts +3 -3
- package/src/tools/subagent/read.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -2
- package/src/tools/swarm/delegate.ts +3 -3
- package/src/tools/system/request-permission.ts +5 -4
- package/src/tools/terminal/backends/native.ts +4 -4
- package/src/tools/terminal/parser.ts +6 -6
- package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
- package/src/tools/terminal/shell.ts +16 -16
- package/src/tools/tool-approval-handler.ts +21 -12
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +3 -3
- package/src/tools/ui-surface/definitions.ts +9 -37
- package/src/tools/watcher/list.ts +1 -1
- package/src/util/logger.ts +7 -2
- package/src/util/pricing.ts +4 -0
- package/src/util/retry.ts +29 -1
- package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
- package/src/cli/reference.ts +0 -38
- package/src/memory/job-handlers/capability-cards.ts +0 -420
- package/src/runtime/routes/thread-starter-routes.ts +0 -294
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Encrypted-at-rest key storage
|
|
2
|
+
* Encrypted-at-rest key storage -- fallback for systems without OS keychain.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* v2 stores use a cryptographically random 32-byte `store.key` file as the
|
|
5
|
+
* AES-256-GCM key directly (no key derivation). The key file lives alongside
|
|
6
|
+
* `keys.enc` in `~/.vellum/protected/`.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* v1 stores (legacy) derived the AES key from machine-specific entropy via
|
|
9
|
+
* PBKDF2. Existing v1 stores are automatically migrated to v2 on first access.
|
|
10
10
|
*
|
|
11
11
|
* Provides the same get/set/delete interface as `keychain.ts`.
|
|
12
12
|
*/
|
|
@@ -17,7 +17,13 @@ import {
|
|
|
17
17
|
pbkdf2Sync,
|
|
18
18
|
randomBytes,
|
|
19
19
|
} from "node:crypto";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
chmodSync,
|
|
22
|
+
readFileSync,
|
|
23
|
+
renameSync,
|
|
24
|
+
unlinkSync,
|
|
25
|
+
writeFileSync,
|
|
26
|
+
} from "node:fs";
|
|
21
27
|
import { hostname, userInfo } from "node:os";
|
|
22
28
|
import { dirname, join } from "node:path";
|
|
23
29
|
|
|
@@ -36,18 +42,27 @@ const PBKDF2_ITERATIONS =
|
|
|
36
42
|
// In tests, PBKDF2 key derivation dominates runtime (~1-2s per file).
|
|
37
43
|
// 1 iteration is sufficient for correctness; 100k is for brute-force resistance.
|
|
38
44
|
process.env.BUN_TEST === "1" ? 1 : 100_000;
|
|
39
|
-
const SALT_LENGTH = 32; // bytes
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// On-disk formats
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
/** v1 on-disk format (legacy): PBKDF2-derived key from machine entropy. */
|
|
51
|
+
interface StoreFileV1 {
|
|
44
52
|
version: 1;
|
|
45
53
|
/** Hex-encoded salt for PBKDF2 key derivation. */
|
|
46
54
|
salt: string;
|
|
47
|
-
/** Individual encrypted entries keyed by account name. */
|
|
48
55
|
entries: Record<string, EncryptedEntry>;
|
|
49
56
|
}
|
|
50
57
|
|
|
58
|
+
/** v2 on-disk format: random store.key used directly as AES key. */
|
|
59
|
+
interface StoreFileV2 {
|
|
60
|
+
version: 2;
|
|
61
|
+
entries: Record<string, EncryptedEntry>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type StoreFile = StoreFileV1 | StoreFileV2;
|
|
65
|
+
|
|
51
66
|
/** A single encrypted value. */
|
|
52
67
|
interface EncryptedEntry {
|
|
53
68
|
/** Hex-encoded IV. */
|
|
@@ -74,10 +89,74 @@ export function _setStorePath(path: string | null): void {
|
|
|
74
89
|
}
|
|
75
90
|
|
|
76
91
|
// ---------------------------------------------------------------------------
|
|
77
|
-
//
|
|
92
|
+
// Store key file (v2)
|
|
78
93
|
// ---------------------------------------------------------------------------
|
|
79
94
|
|
|
80
|
-
|
|
95
|
+
const STORE_KEY_FILENAME = "store.key";
|
|
96
|
+
const STORE_KEY_LENGTH = 32; // bytes
|
|
97
|
+
|
|
98
|
+
let storeKeyPathOverride: string | null = null;
|
|
99
|
+
|
|
100
|
+
/** @internal Test-only: override the store key file path. Pass `null` to reset. */
|
|
101
|
+
export function _setStoreKeyPath(path: string | null): void {
|
|
102
|
+
storeKeyPathOverride = path;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getStoreKeyPath(): string {
|
|
106
|
+
return (
|
|
107
|
+
storeKeyPathOverride ?? join(dirname(getStorePath()), STORE_KEY_FILENAME)
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Read the store.key file. Returns the raw 32-byte key buffer, or null
|
|
113
|
+
* if the file is missing, wrong size, or unreadable.
|
|
114
|
+
*/
|
|
115
|
+
function readStoreKey(): Buffer | null {
|
|
116
|
+
const keyPath = getStoreKeyPath();
|
|
117
|
+
if (!pathExists(keyPath)) return null;
|
|
118
|
+
try {
|
|
119
|
+
const buf = readFileSync(keyPath);
|
|
120
|
+
if (buf.length !== STORE_KEY_LENGTH) return null;
|
|
121
|
+
return buf;
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Generate a cryptographically random store key and write it atomically
|
|
129
|
+
* to `<dir>/store.key` with 0o600 permissions. Returns the key buffer.
|
|
130
|
+
*/
|
|
131
|
+
function generateAndWriteStoreKey(dir: string): Buffer {
|
|
132
|
+
ensureDir(dir);
|
|
133
|
+
const key = randomBytes(STORE_KEY_LENGTH);
|
|
134
|
+
const keyPath = join(dir, STORE_KEY_FILENAME);
|
|
135
|
+
const tmpPath = keyPath + `.tmp.${process.pid}`;
|
|
136
|
+
writeFileSync(tmpPath, key, { mode: 0o600 });
|
|
137
|
+
chmodSync(tmpPath, 0o600);
|
|
138
|
+
renameSync(tmpPath, keyPath);
|
|
139
|
+
return key;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Read the existing store key, or generate and write a new one if missing.
|
|
144
|
+
*/
|
|
145
|
+
function getOrReadStoreKey(dir: string): Buffer {
|
|
146
|
+
const existing = readStoreKey();
|
|
147
|
+
if (existing) return existing;
|
|
148
|
+
return generateAndWriteStoreKey(dir);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Machine entropy for key derivation (legacy v1 only)
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @deprecated @internal Kept only for v1->v2 migration path.
|
|
157
|
+
* Derives entropy from publicly-knowable machine properties.
|
|
158
|
+
*/
|
|
159
|
+
function getMachineEntropy(): string {
|
|
81
160
|
const parts: string[] = [];
|
|
82
161
|
try {
|
|
83
162
|
parts.push(hostname());
|
|
@@ -99,11 +178,75 @@ export function getMachineEntropy(): string {
|
|
|
99
178
|
return parts.join(":");
|
|
100
179
|
}
|
|
101
180
|
|
|
181
|
+
/**
|
|
182
|
+
* @deprecated @internal Kept only for v1->v2 migration path.
|
|
183
|
+
* Derives an AES key from machine entropy via PBKDF2.
|
|
184
|
+
*/
|
|
102
185
|
function deriveKey(salt: Buffer): Buffer {
|
|
103
186
|
const entropy = getMachineEntropy();
|
|
104
187
|
return pbkdf2Sync(entropy, salt, PBKDF2_ITERATIONS, KEY_LENGTH, "sha512");
|
|
105
188
|
}
|
|
106
189
|
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// Key resolution
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Resolve the AES key for a given store format.
|
|
196
|
+
* - v2: reads store.key file (returns null if missing)
|
|
197
|
+
* - v1: derives key from machine entropy via PBKDF2
|
|
198
|
+
*/
|
|
199
|
+
function getKeyForStore(store: StoreFile): Buffer | null {
|
|
200
|
+
if (store.version === 2) {
|
|
201
|
+
return readStoreKey();
|
|
202
|
+
}
|
|
203
|
+
return deriveKey(Buffer.from(store.salt, "hex"));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// v1 -> v2 migration
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Migrate a v1 store to v2 format:
|
|
212
|
+
* 1. Get or generate a random store.key
|
|
213
|
+
* 2. Decrypt each entry with the legacy PBKDF2-derived key
|
|
214
|
+
* 3. Re-encrypt each entry with the random store key
|
|
215
|
+
*
|
|
216
|
+
* Entries that fail to decrypt (corrupt/tampered) are logged and skipped.
|
|
217
|
+
* Returns null if a fatal error occurs (e.g. can't write store.key).
|
|
218
|
+
*/
|
|
219
|
+
function migrateV1ToV2(store: StoreFileV1): StoreFileV2 | null {
|
|
220
|
+
const protectedDir = dirname(getStorePath());
|
|
221
|
+
|
|
222
|
+
let storeKey: Buffer;
|
|
223
|
+
try {
|
|
224
|
+
storeKey = getOrReadStoreKey(protectedDir);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
log.error({ err }, "Failed to create store.key during v1->v2 migration");
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Derive the legacy key for decryption
|
|
231
|
+
const legacyKey = deriveKey(Buffer.from(store.salt, "hex"));
|
|
232
|
+
|
|
233
|
+
const newEntries: Record<string, EncryptedEntry> = Object.create(null);
|
|
234
|
+
|
|
235
|
+
for (const [account, entry] of Object.entries(store.entries)) {
|
|
236
|
+
try {
|
|
237
|
+
const plaintext = decrypt(entry, legacyKey);
|
|
238
|
+
newEntries[account] = encrypt(plaintext, storeKey);
|
|
239
|
+
} catch (err) {
|
|
240
|
+
log.warn(
|
|
241
|
+
{ err, account },
|
|
242
|
+
"Skipping corrupt entry during v1->v2 migration",
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return { version: 2, entries: newEntries };
|
|
248
|
+
}
|
|
249
|
+
|
|
107
250
|
// ---------------------------------------------------------------------------
|
|
108
251
|
// Store I/O
|
|
109
252
|
// ---------------------------------------------------------------------------
|
|
@@ -124,25 +267,34 @@ function readStore(): StoreFile | null {
|
|
|
124
267
|
|
|
125
268
|
try {
|
|
126
269
|
const parsed = JSON.parse(raw);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
typeof parsed.salt !== "string" ||
|
|
130
|
-
typeof parsed.entries !== "object"
|
|
131
|
-
) {
|
|
270
|
+
|
|
271
|
+
if (typeof parsed.entries !== "object") {
|
|
132
272
|
throw new Error("Encrypted store has invalid format");
|
|
133
273
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
274
|
+
|
|
275
|
+
// Accept v2 (no salt required) or v1 (salt required)
|
|
276
|
+
if (parsed.version === 2) {
|
|
277
|
+
const safeEntries: Record<string, EncryptedEntry> = Object.create(null);
|
|
278
|
+
Object.assign(safeEntries, parsed.entries);
|
|
279
|
+
parsed.entries = safeEntries;
|
|
280
|
+
return parsed as StoreFileV2;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (parsed.version === 1 && typeof parsed.salt === "string") {
|
|
284
|
+
const safeEntries: Record<string, EncryptedEntry> = Object.create(null);
|
|
285
|
+
Object.assign(safeEntries, parsed.entries);
|
|
286
|
+
parsed.entries = safeEntries;
|
|
287
|
+
return parsed as StoreFileV1;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
throw new Error("Encrypted store has invalid format");
|
|
139
291
|
} catch (err) {
|
|
140
|
-
// Corrupted or invalid store file
|
|
292
|
+
// Corrupted or invalid store file -- back it up and start fresh so the
|
|
141
293
|
// daemon doesn't crash on every credential access.
|
|
142
294
|
const backupPath = `${path}.corrupt.${Date.now()}`;
|
|
143
295
|
log.error(
|
|
144
296
|
{ err, backupPath },
|
|
145
|
-
"Encrypted store is corrupt
|
|
297
|
+
"Encrypted store is corrupt -- backing up and resetting",
|
|
146
298
|
);
|
|
147
299
|
try {
|
|
148
300
|
renameSync(path, backupPath);
|
|
@@ -154,48 +306,37 @@ function readStore(): StoreFile | null {
|
|
|
154
306
|
}
|
|
155
307
|
|
|
156
308
|
/**
|
|
157
|
-
* Well-known filename for the persisted machine entropy.
|
|
158
|
-
* Written alongside `keys.enc` so the CES sidecar
|
|
159
|
-
*
|
|
309
|
+
* Well-known filename for the persisted machine entropy (legacy).
|
|
310
|
+
* Written alongside `keys.enc` so the CES sidecar could derive the same AES key.
|
|
311
|
+
* Superseded by `store.key` in v2 format.
|
|
160
312
|
*/
|
|
161
313
|
const ENTROPY_FILENAME = "entropy.key";
|
|
162
314
|
|
|
163
315
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* Only writes in containerized (managed) mode — in local mode, CES runs on
|
|
168
|
-
* the same machine and derives the same entropy natively, so the file is
|
|
169
|
-
* unnecessary and would weaken offline security by persisting entropy to disk.
|
|
316
|
+
* Ensure the `store.key` file exists and is accessible on the shared mount
|
|
317
|
+
* (containerized/managed mode). Best-effort delete the old `entropy.key` file
|
|
318
|
+
* since v2 stores no longer need it.
|
|
170
319
|
*/
|
|
171
|
-
function
|
|
320
|
+
function persistStoreKey(protectedDir: string): void {
|
|
172
321
|
if (!getIsContainerized()) return;
|
|
173
322
|
try {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
323
|
+
const storeKeyPath = join(protectedDir, STORE_KEY_FILENAME);
|
|
324
|
+
if (!pathExists(storeKeyPath)) {
|
|
325
|
+
// store.key should already exist from normal creation, but ensure it
|
|
326
|
+
getOrReadStoreKey(protectedDir);
|
|
327
|
+
}
|
|
179
328
|
} catch {
|
|
180
|
-
// Best-effort
|
|
329
|
+
// Best-effort
|
|
181
330
|
}
|
|
182
|
-
}
|
|
183
331
|
|
|
184
|
-
|
|
185
|
-
* Backfill `entropy.key` for existing encrypted stores that predate the
|
|
186
|
-
* entropy persistence feature. If the store file exists but `entropy.key`
|
|
187
|
-
* does not, write it now so the managed CES sidecar can derive the key.
|
|
188
|
-
*/
|
|
189
|
-
function backfillEntropyIfMissing(): void {
|
|
190
|
-
if (!getIsContainerized()) return;
|
|
332
|
+
// Best-effort cleanup of legacy entropy.key
|
|
191
333
|
try {
|
|
192
|
-
const protectedDir = dirname(getStorePath());
|
|
193
334
|
const entropyPath = join(protectedDir, ENTROPY_FILENAME);
|
|
194
|
-
if (
|
|
195
|
-
|
|
335
|
+
if (pathExists(entropyPath)) {
|
|
336
|
+
unlinkSync(entropyPath);
|
|
196
337
|
}
|
|
197
338
|
} catch {
|
|
198
|
-
// Best-effort
|
|
339
|
+
// Best-effort -- don't fail if cleanup of old file doesn't work.
|
|
199
340
|
}
|
|
200
341
|
}
|
|
201
342
|
|
|
@@ -211,23 +352,36 @@ function writeStore(store: StoreFile): void {
|
|
|
211
352
|
chmodSync(tmpPath, 0o600);
|
|
212
353
|
renameSync(tmpPath, path);
|
|
213
354
|
|
|
214
|
-
// Keep
|
|
215
|
-
|
|
355
|
+
// Keep store.key in sync so the managed CES sidecar can decrypt.
|
|
356
|
+
persistStoreKey(protectedDir);
|
|
216
357
|
}
|
|
217
358
|
|
|
218
|
-
function getOrCreateStore():
|
|
359
|
+
function getOrCreateStore(): StoreFileV2 {
|
|
219
360
|
const existing = readStore();
|
|
220
|
-
if (existing) return existing;
|
|
221
361
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
entries
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
362
|
+
if (!existing) {
|
|
363
|
+
// Fresh store: generate store.key and create v2 format
|
|
364
|
+
const protectedDir = dirname(getStorePath());
|
|
365
|
+
getOrReadStoreKey(protectedDir);
|
|
366
|
+
const entries: Record<string, EncryptedEntry> = Object.create(null);
|
|
367
|
+
const store: StoreFileV2 = { version: 2, entries };
|
|
368
|
+
writeStore(store);
|
|
369
|
+
return store;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (existing.version === 1) {
|
|
373
|
+
// Migrate v1 -> v2
|
|
374
|
+
const migrated = migrateV1ToV2(existing);
|
|
375
|
+
if (migrated) {
|
|
376
|
+
writeStore(migrated);
|
|
377
|
+
return migrated;
|
|
378
|
+
}
|
|
379
|
+
// Migration failed fatally -- fall through and use v1 as-is won't work
|
|
380
|
+
// because we can't get the key. Throw so callers handle the error.
|
|
381
|
+
throw new Error("Failed to migrate encrypted store from v1 to v2");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return existing;
|
|
231
385
|
}
|
|
232
386
|
|
|
233
387
|
// ---------------------------------------------------------------------------
|
|
@@ -264,7 +418,7 @@ function decrypt(entry: EncryptedEntry, key: Buffer): string {
|
|
|
264
418
|
}
|
|
265
419
|
|
|
266
420
|
// ---------------------------------------------------------------------------
|
|
267
|
-
// Public API
|
|
421
|
+
// Public API -- matches keychain.ts interface
|
|
268
422
|
// ---------------------------------------------------------------------------
|
|
269
423
|
|
|
270
424
|
/**
|
|
@@ -276,15 +430,30 @@ export function getKey(account: string): string | undefined {
|
|
|
276
430
|
const store = readStore();
|
|
277
431
|
if (!store) return undefined;
|
|
278
432
|
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
433
|
+
// If v1, trigger migration
|
|
434
|
+
if (store.version === 1) {
|
|
435
|
+
const migrated = migrateV1ToV2(store);
|
|
436
|
+
if (migrated) {
|
|
437
|
+
writeStore(migrated);
|
|
438
|
+
const entry = migrated.entries[account];
|
|
439
|
+
if (!entry) return undefined;
|
|
440
|
+
const key = getKeyForStore(migrated);
|
|
441
|
+
if (!key) return undefined;
|
|
442
|
+
return decrypt(entry, key);
|
|
443
|
+
}
|
|
444
|
+
// Migration failed -- try reading with legacy key
|
|
445
|
+
const entry = store.entries[account];
|
|
446
|
+
if (!entry) return undefined;
|
|
447
|
+
const key = getKeyForStore(store);
|
|
448
|
+
if (!key) return undefined;
|
|
449
|
+
return decrypt(entry, key);
|
|
450
|
+
}
|
|
282
451
|
|
|
283
452
|
const entry = store.entries[account];
|
|
284
453
|
if (!entry) return undefined;
|
|
285
454
|
|
|
286
|
-
const
|
|
287
|
-
|
|
455
|
+
const key = getKeyForStore(store);
|
|
456
|
+
if (!key) return undefined;
|
|
288
457
|
return decrypt(entry, key);
|
|
289
458
|
} catch (err) {
|
|
290
459
|
log.debug({ err, account }, "Failed to read from encrypted store");
|
|
@@ -299,8 +468,8 @@ export function getKey(account: string): string | undefined {
|
|
|
299
468
|
export function setKey(account: string, value: string): boolean {
|
|
300
469
|
try {
|
|
301
470
|
const store = getOrCreateStore();
|
|
302
|
-
const
|
|
303
|
-
|
|
471
|
+
const key = getKeyForStore(store);
|
|
472
|
+
if (!key) return false;
|
|
304
473
|
store.entries[account] = encrypt(value, key);
|
|
305
474
|
writeStore(store);
|
|
306
475
|
return true;
|
|
@@ -310,7 +479,7 @@ export function setKey(account: string, value: string): boolean {
|
|
|
310
479
|
}
|
|
311
480
|
}
|
|
312
481
|
|
|
313
|
-
/** Result of a delete operation
|
|
482
|
+
/** Result of a delete operation -- distinguishes success, not-found, and error. */
|
|
314
483
|
export type DeleteKeyResult = "deleted" | "not-found" | "error";
|
|
315
484
|
|
|
316
485
|
/**
|
|
@@ -320,8 +489,23 @@ export type DeleteKeyResult = "deleted" | "not-found" | "error";
|
|
|
320
489
|
*/
|
|
321
490
|
export function deleteKey(account: string): DeleteKeyResult {
|
|
322
491
|
try {
|
|
323
|
-
const
|
|
324
|
-
if (!
|
|
492
|
+
const existing = readStore();
|
|
493
|
+
if (!existing) return "not-found";
|
|
494
|
+
|
|
495
|
+
// Ensure v1→v2 migration happens when a store exists
|
|
496
|
+
let store: StoreFileV2;
|
|
497
|
+
if (existing.version === 1) {
|
|
498
|
+
const migrated = migrateV1ToV2(existing);
|
|
499
|
+
if (!migrated) {
|
|
500
|
+
throw new Error("Failed to migrate encrypted store from v1 to v2");
|
|
501
|
+
}
|
|
502
|
+
writeStore(migrated);
|
|
503
|
+
store = migrated;
|
|
504
|
+
} else {
|
|
505
|
+
store = existing;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (!Object.prototype.hasOwnProperty.call(store.entries, account))
|
|
325
509
|
return "not-found";
|
|
326
510
|
|
|
327
511
|
delete store.entries[account];
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getWorkspaceSkillsDir,
|
|
20
20
|
readPlatformToken,
|
|
21
21
|
} from "../util/platform.js";
|
|
22
|
+
import { deleteSkillCapabilityMemory } from "./skill-memory.js";
|
|
22
23
|
|
|
23
24
|
const log = getLogger("catalog-install");
|
|
24
25
|
|
|
@@ -31,6 +32,14 @@ export interface CatalogSkill {
|
|
|
31
32
|
emoji?: string;
|
|
32
33
|
includes?: string[];
|
|
33
34
|
version?: string;
|
|
35
|
+
metadata?: {
|
|
36
|
+
vellum?: {
|
|
37
|
+
"display-name"?: string;
|
|
38
|
+
"activation-hints"?: string[];
|
|
39
|
+
"avoid-when"?: string[];
|
|
40
|
+
"feature-flag"?: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
export interface CatalogManifest {
|
|
@@ -280,6 +289,7 @@ export function uninstallSkillLocally(skillId: string): void {
|
|
|
280
289
|
|
|
281
290
|
rmSync(skillDir, { recursive: true, force: true });
|
|
282
291
|
removeSkillsIndexEntry(skillId);
|
|
292
|
+
deleteSkillCapabilityMemory(skillId);
|
|
283
293
|
}
|
|
284
294
|
|
|
285
295
|
export async function installSkillLocally(
|
|
@@ -13,6 +13,7 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
13
13
|
|
|
14
14
|
import { getLogger } from "../util/logger.js";
|
|
15
15
|
import { getWorkspaceSkillsDir } from "../util/platform.js";
|
|
16
|
+
import { deleteSkillCapabilityMemory } from "./skill-memory.js";
|
|
16
17
|
|
|
17
18
|
const log = getLogger("managed-store");
|
|
18
19
|
|
|
@@ -307,6 +308,7 @@ export function deleteManagedSkill(
|
|
|
307
308
|
}
|
|
308
309
|
|
|
309
310
|
rmSync(skillDir, { recursive: true });
|
|
311
|
+
deleteSkillCapabilityMemory(id);
|
|
310
312
|
log.info({ id, path: skillDir }, "Deleted managed skill");
|
|
311
313
|
|
|
312
314
|
let indexUpdated = false;
|