@vellumai/assistant 0.5.13 → 0.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +4 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +64 -88
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +3 -14
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +71 -33
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
package/src/cli.ts
CHANGED
|
@@ -153,7 +153,6 @@ export async function startCli(): Promise<void> {
|
|
|
153
153
|
model: string;
|
|
154
154
|
} | null = null;
|
|
155
155
|
let pendingSessionPick = false;
|
|
156
|
-
let pendingConfirmation = false;
|
|
157
156
|
let pendingCopySession = false;
|
|
158
157
|
let toolStreaming = false;
|
|
159
158
|
let lastDisplayedError: string | null = null;
|
|
@@ -232,91 +231,6 @@ export async function startCli(): Promise<void> {
|
|
|
232
231
|
rl.prompt();
|
|
233
232
|
}
|
|
234
233
|
|
|
235
|
-
/** Send a confirmation decision via signal file (read by the daemon). */
|
|
236
|
-
function sendConfirmation(requestId: string, decision: string): void {
|
|
237
|
-
try {
|
|
238
|
-
const signalsDir = getSignalsDir();
|
|
239
|
-
mkdirSync(signalsDir, { recursive: true });
|
|
240
|
-
writeFileSync(
|
|
241
|
-
join(signalsDir, "confirm"),
|
|
242
|
-
JSON.stringify({ requestId, decision }),
|
|
243
|
-
);
|
|
244
|
-
} catch {
|
|
245
|
-
process.stdout.write("[Failed to send confirmation]\n");
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/** Add a trust rule via signal file, then confirm once the daemon acknowledges. */
|
|
250
|
-
function sendTrustRuleAndConfirm(
|
|
251
|
-
requestId: string,
|
|
252
|
-
pattern: string,
|
|
253
|
-
scope: string,
|
|
254
|
-
decision: "allow" | "deny",
|
|
255
|
-
confirmDecision: string,
|
|
256
|
-
options?: { allowHighRisk?: boolean },
|
|
257
|
-
): void {
|
|
258
|
-
try {
|
|
259
|
-
const signalsDir = getSignalsDir();
|
|
260
|
-
mkdirSync(signalsDir, { recursive: true });
|
|
261
|
-
const resultPath = join(signalsDir, "trust-rule.result");
|
|
262
|
-
writeFileSync(
|
|
263
|
-
join(signalsDir, "trust-rule"),
|
|
264
|
-
JSON.stringify({
|
|
265
|
-
requestId,
|
|
266
|
-
pattern,
|
|
267
|
-
scope,
|
|
268
|
-
decision,
|
|
269
|
-
...(options?.allowHighRisk ? { allowHighRisk: true } : {}),
|
|
270
|
-
}),
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
let settled = false;
|
|
274
|
-
|
|
275
|
-
const onResult = (): void => {
|
|
276
|
-
try {
|
|
277
|
-
const raw = readFileSync(resultPath, "utf-8");
|
|
278
|
-
const result = JSON.parse(raw) as {
|
|
279
|
-
ok?: boolean;
|
|
280
|
-
requestId?: string;
|
|
281
|
-
error?: string;
|
|
282
|
-
};
|
|
283
|
-
if (result.requestId !== requestId) return;
|
|
284
|
-
settled = true;
|
|
285
|
-
watcher.close();
|
|
286
|
-
clearTimeout(timeoutId);
|
|
287
|
-
if (result.ok) {
|
|
288
|
-
sendConfirmation(requestId, confirmDecision);
|
|
289
|
-
} else {
|
|
290
|
-
process.stdout.write(
|
|
291
|
-
`[Failed to add trust rule: ${result.error ?? "unknown error"}]\n`,
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
} catch {
|
|
295
|
-
// Result file not yet readable; ignore.
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
const watcher = watch(signalsDir, (_event, filename) => {
|
|
300
|
-
if (filename === "trust-rule.result") {
|
|
301
|
-
onResult();
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
const timeoutId = setTimeout(() => {
|
|
306
|
-
if (!settled) {
|
|
307
|
-
watcher.close();
|
|
308
|
-
process.stdout.write("[Trust rule timed out]\n");
|
|
309
|
-
}
|
|
310
|
-
}, 5_000);
|
|
311
|
-
|
|
312
|
-
if (existsSync(resultPath)) {
|
|
313
|
-
onResult();
|
|
314
|
-
}
|
|
315
|
-
} catch {
|
|
316
|
-
process.stdout.write("[Failed to send trust rule]\n");
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
234
|
/** Send a user message via signal file to the daemon. */
|
|
321
235
|
async function sendUserMessage(
|
|
322
236
|
content: string,
|
|
@@ -399,211 +313,6 @@ export async function startCli(): Promise<void> {
|
|
|
399
313
|
}
|
|
400
314
|
}
|
|
401
315
|
|
|
402
|
-
function renderConfirmationPrompt(req: ConfirmationRequest): void {
|
|
403
|
-
const preview = formatConfirmationCommandPreview(req);
|
|
404
|
-
const inputLines = formatConfirmationInputLines(req.input);
|
|
405
|
-
process.stdout.write("\n");
|
|
406
|
-
process.stdout.write(`\u250C ${req.toolName}: ${preview}\n`);
|
|
407
|
-
process.stdout.write(
|
|
408
|
-
`\u2502 Risk: ${req.riskLevel}${req.sandboxed ? " [sandboxed]" : ""}\n`,
|
|
409
|
-
);
|
|
410
|
-
if (req.executionTarget) {
|
|
411
|
-
process.stdout.write(`\u2502 Target: ${req.executionTarget}\n`);
|
|
412
|
-
}
|
|
413
|
-
if (inputLines.length > 0) {
|
|
414
|
-
process.stdout.write(`\u2502\n`);
|
|
415
|
-
for (const line of inputLines) {
|
|
416
|
-
process.stdout.write(`\u2502 ${line}\n`);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
if (req.diff) {
|
|
420
|
-
const diffOutput = req.diff.isNewFile
|
|
421
|
-
? formatNewFileDiff(req.diff.newContent, req.diff.filePath, null)
|
|
422
|
-
: formatDiff(
|
|
423
|
-
req.diff.oldContent,
|
|
424
|
-
req.diff.newContent,
|
|
425
|
-
req.diff.filePath,
|
|
426
|
-
);
|
|
427
|
-
if (diffOutput) {
|
|
428
|
-
process.stdout.write(`\u2502\n`);
|
|
429
|
-
for (const line of diffOutput.split("\n")) {
|
|
430
|
-
if (line) process.stdout.write(`\u2502 ${line}\n`);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
process.stdout.write(`\u2502\n`);
|
|
435
|
-
process.stdout.write(`\u2502 [a] Allow once\n`);
|
|
436
|
-
if (req.temporaryOptionsAvailable?.includes("allow_10m")) {
|
|
437
|
-
process.stdout.write(`\u2502 [t] Allow 10m\n`);
|
|
438
|
-
}
|
|
439
|
-
if (req.temporaryOptionsAvailable?.includes("allow_conversation")) {
|
|
440
|
-
process.stdout.write(`\u2502 [T] Allow Conversation\n`);
|
|
441
|
-
}
|
|
442
|
-
process.stdout.write(`\u2502 [d] Deny once\n`);
|
|
443
|
-
if (req.allowlistOptions.length > 0 && req.scopeOptions.length > 0) {
|
|
444
|
-
process.stdout.write(`\u2502 [A] Allowlist...\n`);
|
|
445
|
-
process.stdout.write(`\u2502 [H] Allowlist (high-risk)...\n`);
|
|
446
|
-
process.stdout.write(`\u2502 [D] Denylist...\n`);
|
|
447
|
-
}
|
|
448
|
-
process.stdout.write(`\u2514 > `);
|
|
449
|
-
|
|
450
|
-
pendingConfirmation = true;
|
|
451
|
-
rl.once("line", (answer) => {
|
|
452
|
-
const trimmed = answer.trim();
|
|
453
|
-
const choice = trimmed.toLowerCase();
|
|
454
|
-
|
|
455
|
-
// Uppercase 'A' → allowlist pattern selection (check before lowercase 'a')
|
|
456
|
-
// Only process when scope options exist, matching the display guard above
|
|
457
|
-
if (
|
|
458
|
-
(trimmed === "A" || choice === "allowlist") &&
|
|
459
|
-
req.allowlistOptions.length > 0 &&
|
|
460
|
-
req.scopeOptions.length > 0
|
|
461
|
-
) {
|
|
462
|
-
// pendingConfirmation stays true through sub-prompts
|
|
463
|
-
renderPatternSelection(req, "always_allow");
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Uppercase 'H' → high-risk allowlist pattern selection
|
|
468
|
-
if (
|
|
469
|
-
trimmed === "H" &&
|
|
470
|
-
req.allowlistOptions.length > 0 &&
|
|
471
|
-
req.scopeOptions.length > 0
|
|
472
|
-
) {
|
|
473
|
-
// pendingConfirmation stays true through sub-prompts
|
|
474
|
-
renderPatternSelection(req, "always_allow_high_risk");
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// Uppercase 'D' → denylist pattern selection (check before lowercase 'd')
|
|
479
|
-
if (
|
|
480
|
-
(trimmed === "D" || choice === "denylist") &&
|
|
481
|
-
req.allowlistOptions.length > 0 &&
|
|
482
|
-
req.scopeOptions.length > 0
|
|
483
|
-
) {
|
|
484
|
-
// pendingConfirmation stays true through sub-prompts
|
|
485
|
-
renderPatternSelection(req, "always_deny");
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
pendingConfirmation = false;
|
|
490
|
-
if (choice === "a") {
|
|
491
|
-
sendConfirmation(req.requestId, "allow");
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
if (
|
|
496
|
-
choice === "t" &&
|
|
497
|
-
trimmed === "t" &&
|
|
498
|
-
req.temporaryOptionsAvailable?.includes("allow_10m")
|
|
499
|
-
) {
|
|
500
|
-
sendConfirmation(req.requestId, "allow_10m");
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (
|
|
505
|
-
trimmed === "T" &&
|
|
506
|
-
req.temporaryOptionsAvailable?.includes("allow_conversation")
|
|
507
|
-
) {
|
|
508
|
-
sendConfirmation(req.requestId, "allow_conversation");
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (choice === "d") {
|
|
513
|
-
sendConfirmation(req.requestId, "deny");
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Default to deny for unrecognized input
|
|
518
|
-
sendConfirmation(req.requestId, "deny");
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function renderPatternSelection(
|
|
523
|
-
req: ConfirmationRequest,
|
|
524
|
-
decision: "always_allow" | "always_allow_high_risk" | "always_deny",
|
|
525
|
-
): void {
|
|
526
|
-
const label =
|
|
527
|
-
decision === "always_deny"
|
|
528
|
-
? "Denylist"
|
|
529
|
-
: decision === "always_allow_high_risk"
|
|
530
|
-
? "Allowlist (high-risk)"
|
|
531
|
-
: "Allowlist";
|
|
532
|
-
process.stdout.write("\n");
|
|
533
|
-
process.stdout.write(`\u250C ${label}: choose command pattern\n`);
|
|
534
|
-
for (let i = 0; i < req.allowlistOptions.length; i++) {
|
|
535
|
-
process.stdout.write(
|
|
536
|
-
`\u2502 [${i + 1}] ${req.allowlistOptions[i].label}\n`,
|
|
537
|
-
);
|
|
538
|
-
}
|
|
539
|
-
process.stdout.write(`\u2514 > `);
|
|
540
|
-
|
|
541
|
-
rl.once("line", (answer) => {
|
|
542
|
-
const parsed = parseInt(answer.trim(), 10);
|
|
543
|
-
if (Number.isNaN(parsed)) {
|
|
544
|
-
process.stdout.write(" Invalid input — enter a number.\n");
|
|
545
|
-
renderPatternSelection(req, decision);
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
const idx = parsed - 1;
|
|
549
|
-
if (idx >= 0 && idx < req.allowlistOptions.length) {
|
|
550
|
-
const selectedPattern = req.allowlistOptions[idx].pattern;
|
|
551
|
-
// pendingConfirmation stays true through scope selection
|
|
552
|
-
renderScopeSelection(req, selectedPattern, decision);
|
|
553
|
-
} else {
|
|
554
|
-
// Invalid selection → deny
|
|
555
|
-
pendingConfirmation = false;
|
|
556
|
-
sendConfirmation(req.requestId, "deny");
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
function renderScopeSelection(
|
|
562
|
-
req: ConfirmationRequest,
|
|
563
|
-
selectedPattern: string,
|
|
564
|
-
decision: "always_allow" | "always_allow_high_risk" | "always_deny",
|
|
565
|
-
): void {
|
|
566
|
-
const label =
|
|
567
|
-
decision === "always_deny"
|
|
568
|
-
? "Denylist"
|
|
569
|
-
: decision === "always_allow_high_risk"
|
|
570
|
-
? "Allowlist (high-risk)"
|
|
571
|
-
: "Allowlist";
|
|
572
|
-
process.stdout.write("\n");
|
|
573
|
-
process.stdout.write(`\u250C ${label}: choose scope\n`);
|
|
574
|
-
for (let i = 0; i < req.scopeOptions.length; i++) {
|
|
575
|
-
process.stdout.write(`\u2502 [${i + 1}] ${req.scopeOptions[i].label}\n`);
|
|
576
|
-
}
|
|
577
|
-
process.stdout.write(`\u2514 > `);
|
|
578
|
-
|
|
579
|
-
rl.once("line", (answer) => {
|
|
580
|
-
const parsed = parseInt(answer.trim(), 10);
|
|
581
|
-
if (Number.isNaN(parsed)) {
|
|
582
|
-
process.stdout.write(" Invalid input — enter a number.\n");
|
|
583
|
-
renderScopeSelection(req, selectedPattern, decision);
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
pendingConfirmation = false;
|
|
587
|
-
const idx = parsed - 1;
|
|
588
|
-
if (idx >= 0 && idx < req.scopeOptions.length) {
|
|
589
|
-
const trustDecision = decision === "always_deny" ? "deny" : "allow";
|
|
590
|
-
sendTrustRuleAndConfirm(
|
|
591
|
-
req.requestId,
|
|
592
|
-
selectedPattern,
|
|
593
|
-
req.scopeOptions[idx].scope,
|
|
594
|
-
trustDecision,
|
|
595
|
-
trustDecision,
|
|
596
|
-
decision === "always_allow_high_risk"
|
|
597
|
-
? { allowHighRisk: true }
|
|
598
|
-
: undefined,
|
|
599
|
-
);
|
|
600
|
-
} else {
|
|
601
|
-
// Invalid selection → deny
|
|
602
|
-
sendConfirmation(req.requestId, "deny");
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
|
|
607
316
|
function renderConversationPicker(
|
|
608
317
|
conversations: Array<{ id: string; title: string; updatedAt: number }>,
|
|
609
318
|
): void {
|
|
@@ -875,11 +584,6 @@ export async function startCli(): Promise<void> {
|
|
|
875
584
|
spinner.start("Thinking...");
|
|
876
585
|
break;
|
|
877
586
|
|
|
878
|
-
case "confirmation_request":
|
|
879
|
-
spinner.stop();
|
|
880
|
-
renderConfirmationPrompt(msg);
|
|
881
|
-
break;
|
|
882
|
-
|
|
883
587
|
case "conversation_error":
|
|
884
588
|
spinner.stop();
|
|
885
589
|
if (lastDisplayedError !== msg.userMessage) {
|
|
@@ -891,8 +595,7 @@ export async function startCli(): Promise<void> {
|
|
|
891
595
|
case "error":
|
|
892
596
|
spinner.stop();
|
|
893
597
|
generating = false;
|
|
894
|
-
if (
|
|
895
|
-
pendingConfirmation = false;
|
|
598
|
+
if (pendingSessionPick || pendingCopySession) {
|
|
896
599
|
pendingSessionPick = false;
|
|
897
600
|
pendingCopySession = false;
|
|
898
601
|
rl.removeAllListeners("line");
|
|
@@ -1042,7 +745,6 @@ export async function startCli(): Promise<void> {
|
|
|
1042
745
|
const content = line.trim();
|
|
1043
746
|
if (!content) return;
|
|
1044
747
|
if (pendingSessionPick) return;
|
|
1045
|
-
if (pendingConfirmation) return;
|
|
1046
748
|
|
|
1047
749
|
// Persist to history file (ensure parent directory exists)
|
|
1048
750
|
try {
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* `meta/feature-flags/feature-flag-registry.json` and resolves the effective
|
|
6
6
|
* enabled/disabled state for each declared assistant-scope flag by consulting
|
|
7
7
|
* (in priority order):
|
|
8
|
-
* 1. Override values from
|
|
9
|
-
* or via the gateway HTTP API (Docker/containerized)
|
|
8
|
+
* 1. Override values from the gateway HTTP API (or local file fallback)
|
|
10
9
|
* 2. Remote values from `feature-flags-remote.json` (platform-pushed,
|
|
11
10
|
* cached locally; only used in local mode — containerized mode gets
|
|
12
11
|
* remote values via the gateway)
|
|
@@ -18,9 +17,9 @@
|
|
|
18
17
|
*/
|
|
19
18
|
|
|
20
19
|
import { existsSync, readFileSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
21
|
import { dirname, join } from "node:path";
|
|
22
22
|
|
|
23
|
-
import { getRootDir } from "../util/platform.js";
|
|
24
23
|
import { getIsContainerized } from "./env-registry.js";
|
|
25
24
|
import type { AssistantConfig } from "./schema.js";
|
|
26
25
|
|
|
@@ -112,7 +111,7 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
|
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
// ---------------------------------------------------------------------------
|
|
115
|
-
// Override loading — reads from
|
|
114
|
+
// Override loading — reads from gateway HTTP API or local file
|
|
116
115
|
// ---------------------------------------------------------------------------
|
|
117
116
|
|
|
118
117
|
/**
|
|
@@ -122,7 +121,7 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
|
|
|
122
121
|
let cachedOverrides: Record<string, boolean> | null = null;
|
|
123
122
|
|
|
124
123
|
/**
|
|
125
|
-
* File format for
|
|
124
|
+
* File format for the local feature-flags.json override file, matching the
|
|
126
125
|
* gateway's feature-flag-store.ts schema.
|
|
127
126
|
*/
|
|
128
127
|
interface FeatureFlagFileData {
|
|
@@ -134,18 +133,18 @@ interface FeatureFlagFileData {
|
|
|
134
133
|
* Resolve the path to the feature flag overrides file.
|
|
135
134
|
*
|
|
136
135
|
* Docker: `GATEWAY_SECURITY_DIR/feature-flags.json`
|
|
137
|
-
* Local: `~/.vellum
|
|
136
|
+
* Local: `~/.vellum/` + gateway security subdir + `feature-flags.json`
|
|
138
137
|
*/
|
|
139
138
|
function getFeatureFlagOverridesPath(): string {
|
|
140
139
|
const securityDir = process.env.GATEWAY_SECURITY_DIR;
|
|
141
140
|
if (securityDir) {
|
|
142
141
|
return join(securityDir, "feature-flags.json");
|
|
143
142
|
}
|
|
144
|
-
return join(
|
|
143
|
+
return join(homedir(), ".vellum", "protected", "feature-flags.json");
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
/**
|
|
148
|
-
* Load override values from the
|
|
147
|
+
* Load override values from the local feature-flags.json file.
|
|
149
148
|
* Returns an empty record if the file doesn't exist or is malformed.
|
|
150
149
|
*/
|
|
151
150
|
function loadOverridesFromFile(): Record<string, boolean> {
|
|
@@ -246,16 +245,26 @@ function loadOverridesFromGateway(): Record<string, boolean> {
|
|
|
246
245
|
}
|
|
247
246
|
|
|
248
247
|
/**
|
|
249
|
-
* Load overrides
|
|
248
|
+
* Load overrides, preferring the gateway HTTP API.
|
|
249
|
+
*
|
|
250
|
+
* In containerized mode, always uses the gateway. In local mode, tries
|
|
251
|
+
* the gateway first and falls back to `loadOverridesFromFile()` when
|
|
252
|
+
* the gateway is not yet available (startup race).
|
|
253
|
+
*
|
|
250
254
|
* Results are cached at module level.
|
|
251
255
|
*/
|
|
252
256
|
function loadOverrides(): Record<string, boolean> {
|
|
253
257
|
if (cachedOverrides != null) return cachedOverrides;
|
|
254
258
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
259
|
+
const gatewayOverrides = loadOverridesFromGateway();
|
|
260
|
+
if (Object.keys(gatewayOverrides).length > 0 || getIsContainerized()) {
|
|
261
|
+
cachedOverrides = gatewayOverrides;
|
|
262
|
+
return cachedOverrides;
|
|
263
|
+
}
|
|
258
264
|
|
|
265
|
+
// Graceful fallback: in local mode, if the gateway hasn't started yet
|
|
266
|
+
// (empty response), read overrides from file as a temporary measure.
|
|
267
|
+
cachedOverrides = loadOverridesFromFile();
|
|
259
268
|
return cachedOverrides;
|
|
260
269
|
}
|
|
261
270
|
|
|
@@ -346,8 +355,7 @@ export function _setOverridesForTesting(
|
|
|
346
355
|
* Resolve whether an assistant feature flag is enabled.
|
|
347
356
|
*
|
|
348
357
|
* Resolution order:
|
|
349
|
-
* 1. Override from
|
|
350
|
-
* gateway HTTP (Docker/containerized)
|
|
358
|
+
* 1. Override from gateway HTTP API (or local file fallback)
|
|
351
359
|
* 2. Remote value from `feature-flags-remote.json` (platform-pushed,
|
|
352
360
|
* cached locally)
|
|
353
361
|
* 3. defaults registry `defaultEnabled` (for declared assistant-scope keys)
|
|
@@ -361,7 +369,7 @@ export function isAssistantFeatureFlagEnabled(
|
|
|
361
369
|
const declared = defaults[key];
|
|
362
370
|
const overrides = loadOverrides();
|
|
363
371
|
|
|
364
|
-
// 1. Check overrides from
|
|
372
|
+
// 1. Check overrides from gateway / local file
|
|
365
373
|
const explicit = overrides[key];
|
|
366
374
|
if (typeof explicit === "boolean") return explicit;
|
|
367
375
|
|
|
@@ -75,6 +75,10 @@
|
|
|
75
75
|
},
|
|
76
76
|
"required": ["title"]
|
|
77
77
|
},
|
|
78
|
+
"change_summary": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: add coin flip app', 'fix: correct button alignment'). Used as the version history entry."
|
|
81
|
+
},
|
|
78
82
|
"activity": {
|
|
79
83
|
"type": "string",
|
|
80
84
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -97,6 +101,10 @@
|
|
|
97
101
|
"type": "string",
|
|
98
102
|
"description": "The ID of the app to delete"
|
|
99
103
|
},
|
|
104
|
+
"change_summary": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"description": "Short summary of what changed, using git conventional commit format (e.g. 'chore: delete unused app'). Used as the version history entry."
|
|
107
|
+
},
|
|
100
108
|
"activity": {
|
|
101
109
|
"type": "string",
|
|
102
110
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -119,6 +127,10 @@
|
|
|
119
127
|
"type": "string",
|
|
120
128
|
"description": "The ID of the app to refresh"
|
|
121
129
|
},
|
|
130
|
+
"change_summary": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: add dark mode toggle', 'fix: form validation'). Used as the version history entry."
|
|
133
|
+
},
|
|
122
134
|
"activity": {
|
|
123
135
|
"type": "string",
|
|
124
136
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -145,6 +157,10 @@
|
|
|
145
157
|
"type": "string",
|
|
146
158
|
"description": "Optional description to guide icon generation (e.g. 'a blue calendar with a checkmark'). If omitted, the app name and description are used."
|
|
147
159
|
},
|
|
160
|
+
"change_summary": {
|
|
161
|
+
"type": "string",
|
|
162
|
+
"description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: generate app icon'). Used as the version history entry."
|
|
163
|
+
},
|
|
148
164
|
"activity": {
|
|
149
165
|
"type": "string",
|
|
150
166
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isAssistantFeatureFlagEnabled } from "../../../../config/assistant-feature-flags.js";
|
|
2
2
|
import { getConfig } from "../../../../config/loader.js";
|
|
3
|
+
import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
|
|
3
4
|
import * as appStore from "../../../../memory/app-store.js";
|
|
4
5
|
import type { AppCreateInput } from "../../../../tools/apps/executors.js";
|
|
5
6
|
import { executeAppCreate } from "../../../../tools/apps/executors.js";
|
|
@@ -12,6 +13,9 @@ export async function run(
|
|
|
12
13
|
input: Record<string, unknown>,
|
|
13
14
|
context: ToolContext,
|
|
14
15
|
): Promise<ToolExecutionResult> {
|
|
16
|
+
if (typeof input.change_summary === "string" && input.change_summary.trim()) {
|
|
17
|
+
setAppCommitMessage(context.conversationId, input.change_summary.trim());
|
|
18
|
+
}
|
|
15
19
|
const multifileEnabled = isAssistantFeatureFlagEnabled(
|
|
16
20
|
"app-builder-multifile",
|
|
17
21
|
getConfig(),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
|
|
1
2
|
import * as appStore from "../../../../memory/app-store.js";
|
|
2
3
|
import { executeAppDelete } from "../../../../tools/apps/executors.js";
|
|
3
4
|
import type {
|
|
@@ -7,7 +8,10 @@ import type {
|
|
|
7
8
|
|
|
8
9
|
export async function run(
|
|
9
10
|
input: Record<string, unknown>,
|
|
10
|
-
|
|
11
|
+
context: ToolContext,
|
|
11
12
|
): Promise<ToolExecutionResult> {
|
|
13
|
+
if (typeof input.change_summary === "string" && input.change_summary.trim()) {
|
|
14
|
+
setAppCommitMessage(context.conversationId, input.change_summary.trim());
|
|
15
|
+
}
|
|
12
16
|
return executeAppDelete({ app_id: input.app_id as string }, appStore);
|
|
13
17
|
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
|
|
1
2
|
import * as appStore from "../../../../memory/app-store.js";
|
|
2
3
|
import type { AppGenerateIconInput } from "../../../../tools/apps/executors.js";
|
|
3
4
|
import { executeAppGenerateIcon } from "../../../../tools/apps/executors.js";
|
|
4
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
ToolContext,
|
|
7
|
+
ToolExecutionResult,
|
|
8
|
+
} from "../../../../tools/types.js";
|
|
5
9
|
|
|
6
10
|
export async function run(
|
|
7
11
|
input: Record<string, unknown>,
|
|
12
|
+
context: ToolContext,
|
|
8
13
|
): Promise<ToolExecutionResult> {
|
|
14
|
+
if (typeof input.change_summary === "string" && input.change_summary.trim()) {
|
|
15
|
+
setAppCommitMessage(context.conversationId, input.change_summary.trim());
|
|
16
|
+
}
|
|
9
17
|
return executeAppGenerateIcon(
|
|
10
18
|
input as unknown as AppGenerateIconInput,
|
|
11
19
|
appStore,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setAppCommitMessage } from "../../../../memory/app-git-service.js";
|
|
1
2
|
import * as appStore from "../../../../memory/app-store.js";
|
|
2
3
|
import { executeAppRefresh } from "../../../../tools/apps/executors.js";
|
|
3
4
|
import type {
|
|
@@ -7,7 +8,10 @@ import type {
|
|
|
7
8
|
|
|
8
9
|
export async function run(
|
|
9
10
|
input: Record<string, unknown>,
|
|
10
|
-
|
|
11
|
+
context: ToolContext,
|
|
11
12
|
): Promise<ToolExecutionResult> {
|
|
13
|
+
if (typeof input.change_summary === "string" && input.change_summary.trim()) {
|
|
14
|
+
setAppCommitMessage(context.conversationId, input.change_summary.trim());
|
|
15
|
+
}
|
|
12
16
|
return executeAppRefresh({ app_id: input.app_id as string }, appStore);
|
|
13
17
|
}
|
|
@@ -47,6 +47,14 @@
|
|
|
47
47
|
"is_primary": {
|
|
48
48
|
"type": "boolean",
|
|
49
49
|
"description": "Whether this is the primary channel for this type"
|
|
50
|
+
},
|
|
51
|
+
"external_user_id": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Platform-native user ID (e.g. Slack user ID like U12345). Used to cache user lookups."
|
|
54
|
+
},
|
|
55
|
+
"external_chat_id": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "Platform-native chat/DM channel ID (e.g. Slack DM channel like D12345). Used to cache DM channel lookups."
|
|
50
58
|
}
|
|
51
59
|
},
|
|
52
60
|
"required": ["type", "address"]
|
|
@@ -11,6 +11,8 @@ interface ContactChannel {
|
|
|
11
11
|
type: string;
|
|
12
12
|
address: string;
|
|
13
13
|
isPrimary: boolean;
|
|
14
|
+
externalUserId?: string | null;
|
|
15
|
+
externalChatId?: string | null;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
interface ContactResponse {
|
|
@@ -28,7 +30,14 @@ function formatContactSummary(c: ContactResponse): string {
|
|
|
28
30
|
parts.push(` Interactions: ${c.interactionCount}`);
|
|
29
31
|
if (c.channels.length > 0) {
|
|
30
32
|
const channelList = c.channels
|
|
31
|
-
.map((ch) =>
|
|
33
|
+
.map((ch) => {
|
|
34
|
+
let s = `${ch.type}:${ch.address}${ch.isPrimary ? "*" : ""}`;
|
|
35
|
+
const extras: string[] = [];
|
|
36
|
+
if (ch.externalUserId) extras.push(`userId: ${ch.externalUserId}`);
|
|
37
|
+
if (ch.externalChatId) extras.push(`chatId: ${ch.externalChatId}`);
|
|
38
|
+
if (extras.length > 0) s += ` (${extras.join(", ")})`;
|
|
39
|
+
return s;
|
|
40
|
+
})
|
|
32
41
|
.join(", ");
|
|
33
42
|
parts.push(` Channels: ${channelList}`);
|
|
34
43
|
}
|
|
@@ -11,6 +11,8 @@ interface ContactChannel {
|
|
|
11
11
|
type: string;
|
|
12
12
|
address: string;
|
|
13
13
|
isPrimary: boolean;
|
|
14
|
+
externalUserId?: string | null;
|
|
15
|
+
externalChatId?: string | null;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
interface ContactResponse {
|
|
@@ -30,7 +32,11 @@ function formatContact(c: ContactResponse): string {
|
|
|
30
32
|
lines.push(" Channels:");
|
|
31
33
|
for (const ch of c.channels) {
|
|
32
34
|
const primary = ch.isPrimary ? " (primary)" : "";
|
|
33
|
-
|
|
35
|
+
const extras: string[] = [];
|
|
36
|
+
if (ch.externalUserId) extras.push(`userId: ${ch.externalUserId}`);
|
|
37
|
+
if (ch.externalChatId) extras.push(`chatId: ${ch.externalChatId}`);
|
|
38
|
+
const extrasStr = extras.length > 0 ? ` (${extras.join(", ")})` : "";
|
|
39
|
+
lines.push(` - ${ch.type}: ${ch.address}${primary}${extrasStr}`);
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
42
|
return lines.join("\n");
|
|
@@ -53,12 +59,20 @@ export async function executeContactUpsert(
|
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
const rawChannels = input.channels as
|
|
56
|
-
| Array<{
|
|
62
|
+
| Array<{
|
|
63
|
+
type: string;
|
|
64
|
+
address: string;
|
|
65
|
+
is_primary?: boolean;
|
|
66
|
+
external_user_id?: string;
|
|
67
|
+
external_chat_id?: string;
|
|
68
|
+
}>
|
|
57
69
|
| undefined;
|
|
58
70
|
const channels = rawChannels?.map((ch) => ({
|
|
59
71
|
type: ch.type,
|
|
60
72
|
address: ch.address,
|
|
61
73
|
isPrimary: ch.is_primary,
|
|
74
|
+
externalUserId: ch.external_user_id,
|
|
75
|
+
externalChatId: ch.external_chat_id,
|
|
62
76
|
}));
|
|
63
77
|
|
|
64
78
|
try {
|