@vellumai/assistant 0.4.56 → 0.4.57
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 +185 -173
- 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 +237 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- 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-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- 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 +1 -1
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- 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 +547 -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 +139 -274
- 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 +25 -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 +1 -0
- 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 +2 -2
- package/src/notifications/emit-signal.ts +4 -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 +166 -33
- package/src/prompts/templates/IDENTITY.md +8 -23
- 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 +46 -2
- 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 +1 -1
- 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 +63 -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 +9 -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/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 +8 -8
- 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 +263 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +220 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
- package/src/telemetry/usage-telemetry-reporter.ts +51 -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 +1 -1
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -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/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,75 @@ 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 ??
|
|
108
|
+
join(dirname(getStorePath()), STORE_KEY_FILENAME)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Read the store.key file. Returns the raw 32-byte key buffer, or null
|
|
114
|
+
* if the file is missing, wrong size, or unreadable.
|
|
115
|
+
*/
|
|
116
|
+
function readStoreKey(): Buffer | null {
|
|
117
|
+
const keyPath = getStoreKeyPath();
|
|
118
|
+
if (!pathExists(keyPath)) return null;
|
|
119
|
+
try {
|
|
120
|
+
const buf = readFileSync(keyPath);
|
|
121
|
+
if (buf.length !== STORE_KEY_LENGTH) return null;
|
|
122
|
+
return buf;
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Generate a cryptographically random store key and write it atomically
|
|
130
|
+
* to `<dir>/store.key` with 0o600 permissions. Returns the key buffer.
|
|
131
|
+
*/
|
|
132
|
+
function generateAndWriteStoreKey(dir: string): Buffer {
|
|
133
|
+
ensureDir(dir);
|
|
134
|
+
const key = randomBytes(STORE_KEY_LENGTH);
|
|
135
|
+
const keyPath = join(dir, STORE_KEY_FILENAME);
|
|
136
|
+
const tmpPath = keyPath + `.tmp.${process.pid}`;
|
|
137
|
+
writeFileSync(tmpPath, key, { mode: 0o600 });
|
|
138
|
+
chmodSync(tmpPath, 0o600);
|
|
139
|
+
renameSync(tmpPath, keyPath);
|
|
140
|
+
return key;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Read the existing store key, or generate and write a new one if missing.
|
|
145
|
+
*/
|
|
146
|
+
function getOrReadStoreKey(dir: string): Buffer {
|
|
147
|
+
const existing = readStoreKey();
|
|
148
|
+
if (existing) return existing;
|
|
149
|
+
return generateAndWriteStoreKey(dir);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Machine entropy for key derivation (legacy v1 only)
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @deprecated @internal Kept only for v1->v2 migration path.
|
|
158
|
+
* Derives entropy from publicly-knowable machine properties.
|
|
159
|
+
*/
|
|
160
|
+
function getMachineEntropy(): string {
|
|
81
161
|
const parts: string[] = [];
|
|
82
162
|
try {
|
|
83
163
|
parts.push(hostname());
|
|
@@ -99,11 +179,75 @@ export function getMachineEntropy(): string {
|
|
|
99
179
|
return parts.join(":");
|
|
100
180
|
}
|
|
101
181
|
|
|
182
|
+
/**
|
|
183
|
+
* @deprecated @internal Kept only for v1->v2 migration path.
|
|
184
|
+
* Derives an AES key from machine entropy via PBKDF2.
|
|
185
|
+
*/
|
|
102
186
|
function deriveKey(salt: Buffer): Buffer {
|
|
103
187
|
const entropy = getMachineEntropy();
|
|
104
188
|
return pbkdf2Sync(entropy, salt, PBKDF2_ITERATIONS, KEY_LENGTH, "sha512");
|
|
105
189
|
}
|
|
106
190
|
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Key resolution
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Resolve the AES key for a given store format.
|
|
197
|
+
* - v2: reads store.key file (returns null if missing)
|
|
198
|
+
* - v1: derives key from machine entropy via PBKDF2
|
|
199
|
+
*/
|
|
200
|
+
function getKeyForStore(store: StoreFile): Buffer | null {
|
|
201
|
+
if (store.version === 2) {
|
|
202
|
+
return readStoreKey();
|
|
203
|
+
}
|
|
204
|
+
return deriveKey(Buffer.from(store.salt, "hex"));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// v1 -> v2 migration
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Migrate a v1 store to v2 format:
|
|
213
|
+
* 1. Get or generate a random store.key
|
|
214
|
+
* 2. Decrypt each entry with the legacy PBKDF2-derived key
|
|
215
|
+
* 3. Re-encrypt each entry with the random store key
|
|
216
|
+
*
|
|
217
|
+
* Entries that fail to decrypt (corrupt/tampered) are logged and skipped.
|
|
218
|
+
* Returns null if a fatal error occurs (e.g. can't write store.key).
|
|
219
|
+
*/
|
|
220
|
+
function migrateV1ToV2(store: StoreFileV1): StoreFileV2 | null {
|
|
221
|
+
const protectedDir = dirname(getStorePath());
|
|
222
|
+
|
|
223
|
+
let storeKey: Buffer;
|
|
224
|
+
try {
|
|
225
|
+
storeKey = getOrReadStoreKey(protectedDir);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
log.error({ err }, "Failed to create store.key during v1->v2 migration");
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Derive the legacy key for decryption
|
|
232
|
+
const legacyKey = deriveKey(Buffer.from(store.salt, "hex"));
|
|
233
|
+
|
|
234
|
+
const newEntries: Record<string, EncryptedEntry> = Object.create(null);
|
|
235
|
+
|
|
236
|
+
for (const [account, entry] of Object.entries(store.entries)) {
|
|
237
|
+
try {
|
|
238
|
+
const plaintext = decrypt(entry, legacyKey);
|
|
239
|
+
newEntries[account] = encrypt(plaintext, storeKey);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
log.warn(
|
|
242
|
+
{ err, account },
|
|
243
|
+
"Skipping corrupt entry during v1->v2 migration",
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return { version: 2, entries: newEntries };
|
|
249
|
+
}
|
|
250
|
+
|
|
107
251
|
// ---------------------------------------------------------------------------
|
|
108
252
|
// Store I/O
|
|
109
253
|
// ---------------------------------------------------------------------------
|
|
@@ -124,25 +268,34 @@ function readStore(): StoreFile | null {
|
|
|
124
268
|
|
|
125
269
|
try {
|
|
126
270
|
const parsed = JSON.parse(raw);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
typeof parsed.salt !== "string" ||
|
|
130
|
-
typeof parsed.entries !== "object"
|
|
131
|
-
) {
|
|
271
|
+
|
|
272
|
+
if (typeof parsed.entries !== "object") {
|
|
132
273
|
throw new Error("Encrypted store has invalid format");
|
|
133
274
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
275
|
+
|
|
276
|
+
// Accept v2 (no salt required) or v1 (salt required)
|
|
277
|
+
if (parsed.version === 2) {
|
|
278
|
+
const safeEntries: Record<string, EncryptedEntry> = Object.create(null);
|
|
279
|
+
Object.assign(safeEntries, parsed.entries);
|
|
280
|
+
parsed.entries = safeEntries;
|
|
281
|
+
return parsed as StoreFileV2;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (parsed.version === 1 && typeof parsed.salt === "string") {
|
|
285
|
+
const safeEntries: Record<string, EncryptedEntry> = Object.create(null);
|
|
286
|
+
Object.assign(safeEntries, parsed.entries);
|
|
287
|
+
parsed.entries = safeEntries;
|
|
288
|
+
return parsed as StoreFileV1;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
throw new Error("Encrypted store has invalid format");
|
|
139
292
|
} catch (err) {
|
|
140
|
-
// Corrupted or invalid store file
|
|
293
|
+
// Corrupted or invalid store file -- back it up and start fresh so the
|
|
141
294
|
// daemon doesn't crash on every credential access.
|
|
142
295
|
const backupPath = `${path}.corrupt.${Date.now()}`;
|
|
143
296
|
log.error(
|
|
144
297
|
{ err, backupPath },
|
|
145
|
-
"Encrypted store is corrupt
|
|
298
|
+
"Encrypted store is corrupt -- backing up and resetting",
|
|
146
299
|
);
|
|
147
300
|
try {
|
|
148
301
|
renameSync(path, backupPath);
|
|
@@ -154,48 +307,37 @@ function readStore(): StoreFile | null {
|
|
|
154
307
|
}
|
|
155
308
|
|
|
156
309
|
/**
|
|
157
|
-
* Well-known filename for the persisted machine entropy.
|
|
158
|
-
* Written alongside `keys.enc` so the CES sidecar
|
|
159
|
-
*
|
|
310
|
+
* Well-known filename for the persisted machine entropy (legacy).
|
|
311
|
+
* Written alongside `keys.enc` so the CES sidecar could derive the same AES key.
|
|
312
|
+
* Superseded by `store.key` in v2 format.
|
|
160
313
|
*/
|
|
161
314
|
const ENTROPY_FILENAME = "entropy.key";
|
|
162
315
|
|
|
163
316
|
/**
|
|
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.
|
|
317
|
+
* Ensure the `store.key` file exists and is accessible on the shared mount
|
|
318
|
+
* (containerized/managed mode). Best-effort delete the old `entropy.key` file
|
|
319
|
+
* since v2 stores no longer need it.
|
|
170
320
|
*/
|
|
171
|
-
function
|
|
321
|
+
function persistStoreKey(protectedDir: string): void {
|
|
172
322
|
if (!getIsContainerized()) return;
|
|
173
323
|
try {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
324
|
+
const storeKeyPath = join(protectedDir, STORE_KEY_FILENAME);
|
|
325
|
+
if (!pathExists(storeKeyPath)) {
|
|
326
|
+
// store.key should already exist from normal creation, but ensure it
|
|
327
|
+
getOrReadStoreKey(protectedDir);
|
|
328
|
+
}
|
|
179
329
|
} catch {
|
|
180
|
-
// Best-effort
|
|
330
|
+
// Best-effort
|
|
181
331
|
}
|
|
182
|
-
}
|
|
183
332
|
|
|
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;
|
|
333
|
+
// Best-effort cleanup of legacy entropy.key
|
|
191
334
|
try {
|
|
192
|
-
const protectedDir = dirname(getStorePath());
|
|
193
335
|
const entropyPath = join(protectedDir, ENTROPY_FILENAME);
|
|
194
|
-
if (
|
|
195
|
-
|
|
336
|
+
if (pathExists(entropyPath)) {
|
|
337
|
+
unlinkSync(entropyPath);
|
|
196
338
|
}
|
|
197
339
|
} catch {
|
|
198
|
-
// Best-effort
|
|
340
|
+
// Best-effort -- don't fail if cleanup of old file doesn't work.
|
|
199
341
|
}
|
|
200
342
|
}
|
|
201
343
|
|
|
@@ -211,23 +353,36 @@ function writeStore(store: StoreFile): void {
|
|
|
211
353
|
chmodSync(tmpPath, 0o600);
|
|
212
354
|
renameSync(tmpPath, path);
|
|
213
355
|
|
|
214
|
-
// Keep
|
|
215
|
-
|
|
356
|
+
// Keep store.key in sync so the managed CES sidecar can decrypt.
|
|
357
|
+
persistStoreKey(protectedDir);
|
|
216
358
|
}
|
|
217
359
|
|
|
218
|
-
function getOrCreateStore():
|
|
360
|
+
function getOrCreateStore(): StoreFileV2 {
|
|
219
361
|
const existing = readStore();
|
|
220
|
-
if (existing) return existing;
|
|
221
362
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
entries
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
363
|
+
if (!existing) {
|
|
364
|
+
// Fresh store: generate store.key and create v2 format
|
|
365
|
+
const protectedDir = dirname(getStorePath());
|
|
366
|
+
getOrReadStoreKey(protectedDir);
|
|
367
|
+
const entries: Record<string, EncryptedEntry> = Object.create(null);
|
|
368
|
+
const store: StoreFileV2 = { version: 2, entries };
|
|
369
|
+
writeStore(store);
|
|
370
|
+
return store;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (existing.version === 1) {
|
|
374
|
+
// Migrate v1 -> v2
|
|
375
|
+
const migrated = migrateV1ToV2(existing);
|
|
376
|
+
if (migrated) {
|
|
377
|
+
writeStore(migrated);
|
|
378
|
+
return migrated;
|
|
379
|
+
}
|
|
380
|
+
// Migration failed fatally -- fall through and use v1 as-is won't work
|
|
381
|
+
// because we can't get the key. Throw so callers handle the error.
|
|
382
|
+
throw new Error("Failed to migrate encrypted store from v1 to v2");
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return existing;
|
|
231
386
|
}
|
|
232
387
|
|
|
233
388
|
// ---------------------------------------------------------------------------
|
|
@@ -264,7 +419,7 @@ function decrypt(entry: EncryptedEntry, key: Buffer): string {
|
|
|
264
419
|
}
|
|
265
420
|
|
|
266
421
|
// ---------------------------------------------------------------------------
|
|
267
|
-
// Public API
|
|
422
|
+
// Public API -- matches keychain.ts interface
|
|
268
423
|
// ---------------------------------------------------------------------------
|
|
269
424
|
|
|
270
425
|
/**
|
|
@@ -276,15 +431,30 @@ export function getKey(account: string): string | undefined {
|
|
|
276
431
|
const store = readStore();
|
|
277
432
|
if (!store) return undefined;
|
|
278
433
|
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
434
|
+
// If v1, trigger migration
|
|
435
|
+
if (store.version === 1) {
|
|
436
|
+
const migrated = migrateV1ToV2(store);
|
|
437
|
+
if (migrated) {
|
|
438
|
+
writeStore(migrated);
|
|
439
|
+
const entry = migrated.entries[account];
|
|
440
|
+
if (!entry) return undefined;
|
|
441
|
+
const key = getKeyForStore(migrated);
|
|
442
|
+
if (!key) return undefined;
|
|
443
|
+
return decrypt(entry, key);
|
|
444
|
+
}
|
|
445
|
+
// Migration failed -- try reading with legacy key
|
|
446
|
+
const entry = store.entries[account];
|
|
447
|
+
if (!entry) return undefined;
|
|
448
|
+
const key = getKeyForStore(store);
|
|
449
|
+
if (!key) return undefined;
|
|
450
|
+
return decrypt(entry, key);
|
|
451
|
+
}
|
|
282
452
|
|
|
283
453
|
const entry = store.entries[account];
|
|
284
454
|
if (!entry) return undefined;
|
|
285
455
|
|
|
286
|
-
const
|
|
287
|
-
|
|
456
|
+
const key = getKeyForStore(store);
|
|
457
|
+
if (!key) return undefined;
|
|
288
458
|
return decrypt(entry, key);
|
|
289
459
|
} catch (err) {
|
|
290
460
|
log.debug({ err, account }, "Failed to read from encrypted store");
|
|
@@ -299,8 +469,8 @@ export function getKey(account: string): string | undefined {
|
|
|
299
469
|
export function setKey(account: string, value: string): boolean {
|
|
300
470
|
try {
|
|
301
471
|
const store = getOrCreateStore();
|
|
302
|
-
const
|
|
303
|
-
|
|
472
|
+
const key = getKeyForStore(store);
|
|
473
|
+
if (!key) return false;
|
|
304
474
|
store.entries[account] = encrypt(value, key);
|
|
305
475
|
writeStore(store);
|
|
306
476
|
return true;
|
|
@@ -310,7 +480,7 @@ export function setKey(account: string, value: string): boolean {
|
|
|
310
480
|
}
|
|
311
481
|
}
|
|
312
482
|
|
|
313
|
-
/** Result of a delete operation
|
|
483
|
+
/** Result of a delete operation -- distinguishes success, not-found, and error. */
|
|
314
484
|
export type DeleteKeyResult = "deleted" | "not-found" | "error";
|
|
315
485
|
|
|
316
486
|
/**
|
|
@@ -320,8 +490,23 @@ export type DeleteKeyResult = "deleted" | "not-found" | "error";
|
|
|
320
490
|
*/
|
|
321
491
|
export function deleteKey(account: string): DeleteKeyResult {
|
|
322
492
|
try {
|
|
323
|
-
const
|
|
324
|
-
if (!
|
|
493
|
+
const existing = readStore();
|
|
494
|
+
if (!existing) return "not-found";
|
|
495
|
+
|
|
496
|
+
// Ensure v1→v2 migration happens when a store exists
|
|
497
|
+
let store: StoreFileV2;
|
|
498
|
+
if (existing.version === 1) {
|
|
499
|
+
const migrated = migrateV1ToV2(existing);
|
|
500
|
+
if (!migrated) {
|
|
501
|
+
throw new Error("Failed to migrate encrypted store from v1 to v2");
|
|
502
|
+
}
|
|
503
|
+
writeStore(migrated);
|
|
504
|
+
store = migrated;
|
|
505
|
+
} else {
|
|
506
|
+
store = existing;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (!Object.prototype.hasOwnProperty.call(store.entries, account))
|
|
325
510
|
return "not-found";
|
|
326
511
|
|
|
327
512
|
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;
|