@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
|
@@ -21,6 +21,7 @@ import { httpError } from "../http-errors.js";
|
|
|
21
21
|
import type { RouteDefinition } from "../http-router.js";
|
|
22
22
|
import {
|
|
23
23
|
cancelOutbound,
|
|
24
|
+
deliverVerificationSlack,
|
|
24
25
|
normalizeTelegramDestination,
|
|
25
26
|
resendOutbound,
|
|
26
27
|
startOutbound,
|
|
@@ -118,12 +119,20 @@ export async function handleCreateVerificationSession(
|
|
|
118
119
|
verificationRateLimiter.recordFailure(rateLimitKey);
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
// Dispatch Slack DM delivery from the daemon process (not sandboxed).
|
|
123
|
+
if (result._pendingSlackDm) {
|
|
124
|
+
const { userId, text, assistantId: aid } = result._pendingSlackDm;
|
|
125
|
+
deliverVerificationSlack(userId, text, aid);
|
|
126
|
+
}
|
|
127
|
+
|
|
121
128
|
const status = result.success
|
|
122
129
|
? 200
|
|
123
130
|
: result.error === "rate_limited"
|
|
124
131
|
? 429
|
|
125
132
|
: 400;
|
|
126
|
-
|
|
133
|
+
// Strip internal field from the response
|
|
134
|
+
const { _pendingSlackDm: _, ...publicResult } = result;
|
|
135
|
+
return Response.json(publicResult, { status });
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
// Inbound challenge path
|
|
@@ -167,12 +176,20 @@ export async function handleResendVerificationSession(
|
|
|
167
176
|
channel: body.channel,
|
|
168
177
|
originConversationId: body.originConversationId,
|
|
169
178
|
});
|
|
179
|
+
|
|
180
|
+
// Dispatch Slack DM delivery from the daemon process (not sandboxed).
|
|
181
|
+
if (result._pendingSlackDm) {
|
|
182
|
+
const { userId, text, assistantId: aid } = result._pendingSlackDm;
|
|
183
|
+
deliverVerificationSlack(userId, text, aid);
|
|
184
|
+
}
|
|
185
|
+
|
|
170
186
|
const status = result.success
|
|
171
187
|
? 200
|
|
172
188
|
: result.error === "rate_limited"
|
|
173
189
|
? 429
|
|
174
190
|
: 400;
|
|
175
|
-
|
|
191
|
+
const { _pendingSlackDm: _, ...publicResult } = result;
|
|
192
|
+
return Response.json(publicResult, { status });
|
|
176
193
|
}
|
|
177
194
|
|
|
178
195
|
/**
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* POST /v1/conversations/switch — switch to an existing conversation
|
|
5
5
|
* PATCH /v1/conversations/:id/name — rename a conversation
|
|
6
6
|
* DELETE /v1/conversations — clear all conversations
|
|
7
|
+
* POST /v1/conversations/:id/wipe — wipe conversation and revert memory
|
|
7
8
|
* DELETE /v1/conversations/:id — delete a single conversation
|
|
8
9
|
* POST /v1/conversations/:id/cancel — cancel generation
|
|
9
10
|
* POST /v1/conversations/:id/undo — undo last message
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
import {
|
|
15
16
|
batchSetDisplayOrders,
|
|
16
17
|
deleteConversation,
|
|
18
|
+
wipeConversation,
|
|
17
19
|
} from "../../memory/conversation-crud.js";
|
|
18
20
|
import {
|
|
19
21
|
resolveConversationId,
|
|
@@ -121,6 +123,57 @@ export function conversationManagementRouteDefinitions(
|
|
|
121
123
|
return new Response(null, { status: 204 });
|
|
122
124
|
},
|
|
123
125
|
},
|
|
126
|
+
{
|
|
127
|
+
endpoint: "conversations/:id/wipe",
|
|
128
|
+
method: "POST",
|
|
129
|
+
policyKey: "conversations/wipe",
|
|
130
|
+
handler: async ({ params }) => {
|
|
131
|
+
const resolvedId = resolveConversationId(params.id);
|
|
132
|
+
if (!resolvedId) {
|
|
133
|
+
return httpError(
|
|
134
|
+
"NOT_FOUND",
|
|
135
|
+
`Conversation ${params.id} not found`,
|
|
136
|
+
404,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
deps.destroyConversation(resolvedId);
|
|
140
|
+
const result = wipeConversation(resolvedId);
|
|
141
|
+
// Enqueue Qdrant vector cleanup jobs
|
|
142
|
+
for (const segId of result.segmentIds) {
|
|
143
|
+
enqueueMemoryJob("delete_qdrant_vectors", {
|
|
144
|
+
targetType: "segment",
|
|
145
|
+
targetId: segId,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
for (const itemId of result.orphanedItemIds) {
|
|
149
|
+
enqueueMemoryJob("delete_qdrant_vectors", {
|
|
150
|
+
targetType: "item",
|
|
151
|
+
targetId: itemId,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
for (const summaryId of result.deletedSummaryIds) {
|
|
155
|
+
enqueueMemoryJob("delete_qdrant_vectors", {
|
|
156
|
+
targetType: "summary",
|
|
157
|
+
targetId: summaryId,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
log.info(
|
|
161
|
+
{
|
|
162
|
+
conversationId: resolvedId,
|
|
163
|
+
unsuperseded: result.unsupersededItemIds.length,
|
|
164
|
+
summariesDeleted: result.deletedSummaryIds.length,
|
|
165
|
+
jobsCancelled: result.cancelledJobCount,
|
|
166
|
+
},
|
|
167
|
+
"Wiped conversation and reverted memory changes",
|
|
168
|
+
);
|
|
169
|
+
return Response.json({
|
|
170
|
+
wiped: true,
|
|
171
|
+
unsupersededItems: result.unsupersededItemIds.length,
|
|
172
|
+
deletedSummaries: result.deletedSummaryIds.length,
|
|
173
|
+
cancelledJobs: result.cancelledJobCount,
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
},
|
|
124
177
|
{
|
|
125
178
|
endpoint: "conversations/:id",
|
|
126
179
|
method: "DELETE",
|
|
@@ -163,7 +216,8 @@ export function conversationManagementRouteDefinitions(
|
|
|
163
216
|
method: "POST",
|
|
164
217
|
policyKey: "conversations/cancel",
|
|
165
218
|
handler: ({ params }) => {
|
|
166
|
-
|
|
219
|
+
const resolvedId = resolveConversationId(params.id) ?? params.id;
|
|
220
|
+
deps.cancelGeneration(resolvedId);
|
|
167
221
|
return new Response(null, { status: 202 });
|
|
168
222
|
},
|
|
169
223
|
},
|
|
@@ -200,7 +200,7 @@ export function conversationQueryRouteDefinitions(
|
|
|
200
200
|
requestId: params.id,
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
|
-
if (result.reason === "
|
|
203
|
+
if (result.reason === "conversation_not_found") {
|
|
204
204
|
return httpError("NOT_FOUND", "Conversation not found", 404);
|
|
205
205
|
}
|
|
206
206
|
return httpError("NOT_FOUND", "Queued message not found", 404);
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
createCanonicalGuardianRequest,
|
|
36
36
|
generateCanonicalRequestCode,
|
|
37
37
|
listPendingRequestsByConversationScope,
|
|
38
|
+
resolveCanonicalGuardianRequest,
|
|
38
39
|
} from "../../memory/canonical-guardian-store.js";
|
|
39
40
|
import {
|
|
40
41
|
addMessage,
|
|
@@ -58,6 +59,7 @@ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
|
58
59
|
import type { AuthContext } from "../auth/types.js";
|
|
59
60
|
import { bridgeConfirmationRequestToGuardian } from "../confirmation-request-guardian-bridge.js";
|
|
60
61
|
import { routeGuardianReply } from "../guardian-reply-router.js";
|
|
62
|
+
import { healGuardianBindingDrift } from "../guardian-vellum-migration.js";
|
|
61
63
|
import { httpError } from "../http-errors.js";
|
|
62
64
|
import type { RouteDefinition } from "../http-router.js";
|
|
63
65
|
import type {
|
|
@@ -492,16 +494,16 @@ function makeHubPublisher(
|
|
|
492
494
|
}
|
|
493
495
|
|
|
494
496
|
// ServerMessage is a large union; conversationId exists on most but not all variants.
|
|
495
|
-
const
|
|
497
|
+
const msgConversationId =
|
|
496
498
|
"conversationId" in msg &&
|
|
497
499
|
typeof (msg as { conversationId?: unknown }).conversationId === "string"
|
|
498
500
|
? (msg as { conversationId: string }).conversationId
|
|
499
501
|
: undefined;
|
|
500
|
-
const
|
|
502
|
+
const resolvedConversationId = msgConversationId ?? conversationId;
|
|
501
503
|
const event = buildAssistantEvent(
|
|
502
504
|
DAEMON_INTERNAL_ASSISTANT_ID,
|
|
503
505
|
msg,
|
|
504
|
-
|
|
506
|
+
resolvedConversationId,
|
|
505
507
|
);
|
|
506
508
|
hubChain = (async () => {
|
|
507
509
|
await hubChain;
|
|
@@ -646,12 +648,44 @@ export async function handleSendMessage(
|
|
|
646
648
|
// the same trust resolution pipeline that channel ingress uses.
|
|
647
649
|
if (authContext.actorPrincipalId) {
|
|
648
650
|
const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
|
|
649
|
-
|
|
651
|
+
let trustCtx = resolveTrustContext({
|
|
650
652
|
assistantId,
|
|
651
653
|
sourceChannel: "vellum",
|
|
652
654
|
conversationExternalId: "local",
|
|
653
655
|
actorExternalId: authContext.actorPrincipalId,
|
|
654
656
|
});
|
|
657
|
+
if (trustCtx.trustClass === "unknown") {
|
|
658
|
+
// Attempt to heal guardian binding drift: after a DB reset the
|
|
659
|
+
// guardian binding gets a new vellum-principal-* UUID while the
|
|
660
|
+
// client still holds a valid JWT with the old one. The signing
|
|
661
|
+
// key survives the reset, so the JWT is authentic — just stale.
|
|
662
|
+
const healed = healGuardianBindingDrift(authContext.actorPrincipalId);
|
|
663
|
+
if (healed) {
|
|
664
|
+
trustCtx = resolveTrustContext({
|
|
665
|
+
assistantId,
|
|
666
|
+
sourceChannel: "vellum",
|
|
667
|
+
conversationExternalId: "local",
|
|
668
|
+
actorExternalId: authContext.actorPrincipalId,
|
|
669
|
+
});
|
|
670
|
+
log.info(
|
|
671
|
+
{
|
|
672
|
+
actorPrincipalId: authContext.actorPrincipalId,
|
|
673
|
+
trustClass: trustCtx.trustClass,
|
|
674
|
+
},
|
|
675
|
+
"Trust re-resolved after guardian binding drift heal",
|
|
676
|
+
);
|
|
677
|
+
} else {
|
|
678
|
+
log.warn(
|
|
679
|
+
{
|
|
680
|
+
actorPrincipalId: authContext.actorPrincipalId,
|
|
681
|
+
sourceChannel,
|
|
682
|
+
trustClass: trustCtx.trustClass,
|
|
683
|
+
principalType: authContext.principalType,
|
|
684
|
+
},
|
|
685
|
+
"JWT-verified actor resolved to unknown trust class — possible guardian binding drift (e.g. DB reset without re-bootstrap)",
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
655
689
|
conversation.setTrustContext(withSourceChannel(sourceChannel, trustCtx));
|
|
656
690
|
} else {
|
|
657
691
|
// Service principals (svc_gateway) or tokens without an actor ID
|
|
@@ -811,6 +845,11 @@ export async function handleSendMessage(
|
|
|
811
845
|
state: "denied" as const,
|
|
812
846
|
source: "auto_deny" as const,
|
|
813
847
|
});
|
|
848
|
+
// Sync canonical guardian request status so stale "pending" DB
|
|
849
|
+
// records don't get matched by later guardian reply routing.
|
|
850
|
+
resolveCanonicalGuardianRequest(interaction.requestId, "pending", {
|
|
851
|
+
status: "denied",
|
|
852
|
+
});
|
|
814
853
|
}
|
|
815
854
|
}
|
|
816
855
|
conversation.denyAllPendingConfirmations();
|
|
@@ -842,6 +881,11 @@ export async function handleSendMessage(
|
|
|
842
881
|
state: "denied" as const,
|
|
843
882
|
source: "auto_deny" as const,
|
|
844
883
|
});
|
|
884
|
+
// Sync canonical guardian request status so stale "pending" DB
|
|
885
|
+
// records don't get matched by later guardian reply routing.
|
|
886
|
+
resolveCanonicalGuardianRequest(interaction.requestId, "pending", {
|
|
887
|
+
status: "denied",
|
|
888
|
+
});
|
|
845
889
|
}
|
|
846
890
|
}
|
|
847
891
|
conversation.denyAllPendingConfirmations();
|
|
@@ -931,7 +975,7 @@ export async function handleSendMessage(
|
|
|
931
975
|
);
|
|
932
976
|
|
|
933
977
|
// Defer event publishing to next tick so the HTTP response reaches the
|
|
934
|
-
// client first. This ensures the client's
|
|
978
|
+
// client first. This ensures the client's serverToLocalConversationMap is
|
|
935
979
|
// populated before SSE events arrive, preventing dropped events in new
|
|
936
980
|
// desktop conversations.
|
|
937
981
|
//
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handlers for conversation starter endpoints.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/conversation-starters — list conversation starters (chips)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { and, desc, eq, inArray, like } from "drizzle-orm";
|
|
8
|
+
|
|
9
|
+
import { getDb } from "../../memory/db.js";
|
|
10
|
+
import { enqueueMemoryJob } from "../../memory/jobs-store.js";
|
|
11
|
+
import { rawGet } from "../../memory/raw-query.js";
|
|
12
|
+
import { conversationStarters, memoryJobs } from "../../memory/schema.js";
|
|
13
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Strongest-first ordering — maximize category diversity so the top four
|
|
17
|
+
// chips form a coherent, non-repetitive row.
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
interface StarterItem {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
prompt: string;
|
|
24
|
+
category: string | null;
|
|
25
|
+
batch: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Re-order starters so adjacent items have distinct categories wherever
|
|
30
|
+
* possible. Within each category, preserve the original (batch-desc) order.
|
|
31
|
+
* This is deterministic — same input always produces the same output.
|
|
32
|
+
*/
|
|
33
|
+
export function orderStrongestFirst<T extends StarterItem>(items: T[]): T[] {
|
|
34
|
+
if (items.length <= 1) return items;
|
|
35
|
+
|
|
36
|
+
// Group by category, preserving original order within each group
|
|
37
|
+
const byCategory = new Map<string, T[]>();
|
|
38
|
+
for (const item of items) {
|
|
39
|
+
const cat = item.category ?? "other";
|
|
40
|
+
let group = byCategory.get(cat);
|
|
41
|
+
if (!group) {
|
|
42
|
+
group = [];
|
|
43
|
+
byCategory.set(cat, group);
|
|
44
|
+
}
|
|
45
|
+
group.push(item);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Prefer categories with the most remaining items so the row stays varied
|
|
49
|
+
// early without burying the dominant themes entirely.
|
|
50
|
+
const sortedGroups = [...byCategory.entries()]
|
|
51
|
+
.sort((a, b) => b[1].length - a[1].length)
|
|
52
|
+
.map(([, group]) => ({ items: group, idx: 0 }));
|
|
53
|
+
|
|
54
|
+
const result: T[] = [];
|
|
55
|
+
const seenCategories = new Set<string>();
|
|
56
|
+
let lastCategory: string | null = null;
|
|
57
|
+
|
|
58
|
+
while (result.length < items.length) {
|
|
59
|
+
let picked = false;
|
|
60
|
+
const availableGroups = sortedGroups.filter(
|
|
61
|
+
(group) => group.idx < group.items.length,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const unseenGroups = availableGroups.filter((group) => {
|
|
65
|
+
const category = group.items[group.idx]?.category ?? "other";
|
|
66
|
+
return category !== lastCategory && !seenCategories.has(category);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const nextGroups =
|
|
70
|
+
unseenGroups.length > 0
|
|
71
|
+
? unseenGroups
|
|
72
|
+
: availableGroups.filter((group) => {
|
|
73
|
+
const category = group.items[group.idx]?.category ?? "other";
|
|
74
|
+
return category !== lastCategory;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// First pass: prefer unseen categories, then avoid adjacent duplicates.
|
|
78
|
+
for (const group of nextGroups) {
|
|
79
|
+
if (group.idx >= group.items.length) continue;
|
|
80
|
+
const candidate = group.items[group.idx];
|
|
81
|
+
const cat = candidate.category ?? "other";
|
|
82
|
+
result.push(candidate);
|
|
83
|
+
group.idx++;
|
|
84
|
+
seenCategories.add(cat);
|
|
85
|
+
lastCategory = cat;
|
|
86
|
+
picked = true;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Fallback: if all remaining items share the same category, just pick next
|
|
91
|
+
if (!picked) {
|
|
92
|
+
for (const group of availableGroups) {
|
|
93
|
+
if (group.idx < group.items.length) {
|
|
94
|
+
const candidate = group.items[group.idx];
|
|
95
|
+
const cat = candidate.category ?? "other";
|
|
96
|
+
result.push(candidate);
|
|
97
|
+
group.idx++;
|
|
98
|
+
seenCategories.add(cat);
|
|
99
|
+
lastCategory = cat;
|
|
100
|
+
picked = true;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!picked) break;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// GET /v1/conversation-starters
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
function handleListConversationStarters(url: URL): Response {
|
|
117
|
+
const limitParam = Math.min(
|
|
118
|
+
Math.max(1, Number(url.searchParams.get("limit") ?? 4)),
|
|
119
|
+
20,
|
|
120
|
+
);
|
|
121
|
+
const offsetParam = Math.max(0, Number(url.searchParams.get("offset") ?? 0));
|
|
122
|
+
const scopeId = url.searchParams.get("scope_id") ?? "default";
|
|
123
|
+
|
|
124
|
+
const db = getDb();
|
|
125
|
+
|
|
126
|
+
// Fetch chips (ranked by model, newest batch first)
|
|
127
|
+
const rawItems = db
|
|
128
|
+
.select({
|
|
129
|
+
id: conversationStarters.id,
|
|
130
|
+
label: conversationStarters.label,
|
|
131
|
+
prompt: conversationStarters.prompt,
|
|
132
|
+
category: conversationStarters.category,
|
|
133
|
+
batch: conversationStarters.generationBatch,
|
|
134
|
+
})
|
|
135
|
+
.from(conversationStarters)
|
|
136
|
+
.where(
|
|
137
|
+
and(
|
|
138
|
+
eq(conversationStarters.scopeId, scopeId),
|
|
139
|
+
eq(conversationStarters.cardType, "chip"),
|
|
140
|
+
),
|
|
141
|
+
)
|
|
142
|
+
.orderBy(
|
|
143
|
+
desc(conversationStarters.generationBatch),
|
|
144
|
+
desc(conversationStarters.createdAt),
|
|
145
|
+
)
|
|
146
|
+
.limit(limitParam)
|
|
147
|
+
.offset(offsetParam)
|
|
148
|
+
.all();
|
|
149
|
+
|
|
150
|
+
const countRow = rawGet<{ c: number }>(
|
|
151
|
+
`SELECT COUNT(*) AS c FROM conversation_starters WHERE scope_id = ? AND card_type = 'chip'`,
|
|
152
|
+
scopeId,
|
|
153
|
+
);
|
|
154
|
+
const total = countRow?.c ?? 0;
|
|
155
|
+
|
|
156
|
+
// If starters exist, return them immediately.
|
|
157
|
+
if (total > 0) {
|
|
158
|
+
return Response.json({
|
|
159
|
+
starters: orderStrongestFirst(rawItems),
|
|
160
|
+
total,
|
|
161
|
+
status: "ready",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// No starters — check whether we have memory items to generate from.
|
|
166
|
+
const memoryCount = rawGet<{ c: number }>(
|
|
167
|
+
`SELECT COUNT(*) AS c FROM memory_items WHERE status = 'active' AND scope_id = ?`,
|
|
168
|
+
scopeId,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (!memoryCount || memoryCount.c === 0) {
|
|
172
|
+
return Response.json({ starters: [], total: 0, status: "empty" });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Memory items exist but no starters yet — ensure a generation job is queued.
|
|
176
|
+
const existing = db
|
|
177
|
+
.select({ id: memoryJobs.id })
|
|
178
|
+
.from(memoryJobs)
|
|
179
|
+
.where(
|
|
180
|
+
and(
|
|
181
|
+
eq(memoryJobs.type, "generate_conversation_starters"),
|
|
182
|
+
inArray(memoryJobs.status, ["pending", "running"]),
|
|
183
|
+
like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
.get();
|
|
187
|
+
|
|
188
|
+
if (!existing) {
|
|
189
|
+
enqueueMemoryJob("generate_conversation_starters", { scopeId });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return Response.json({ starters: [], total: 0, status: "generating" });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Route definitions
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
export function conversationStarterRouteDefinitions(): RouteDefinition[] {
|
|
200
|
+
return [
|
|
201
|
+
{
|
|
202
|
+
endpoint: "conversation-starters",
|
|
203
|
+
method: "GET",
|
|
204
|
+
handler: (ctx) => handleListConversationStarters(ctx.url),
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
}
|
|
@@ -19,6 +19,7 @@ import { getLogger } from "../../util/logger.js";
|
|
|
19
19
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
20
20
|
import { mintCredentialPair } from "../auth/credential-service.js";
|
|
21
21
|
import { httpError } from "../http-errors.js";
|
|
22
|
+
import { isPrivateAddress } from "../middleware/auth.js";
|
|
22
23
|
|
|
23
24
|
/** Bun server shape needed for requestIP -- avoids importing the full Bun type. */
|
|
24
25
|
type ServerWithRequestIP = {
|
|
@@ -71,30 +72,33 @@ function ensureGuardianPrincipal(assistantId: string): {
|
|
|
71
72
|
return { guardianPrincipalId, isNew: true };
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
/** Loopback addresses — used to gate the bootstrap endpoint to local-only. */
|
|
75
|
-
const LOOPBACK_ADDRESSES = new Set(["127.0.0.1", "::1", "::ffff:127.0.0.1"]);
|
|
76
|
-
|
|
77
75
|
/**
|
|
78
76
|
* Handle POST /v1/guardian/init
|
|
79
77
|
*
|
|
80
78
|
* Body: { platform: 'macos', deviceId: string }
|
|
81
79
|
* Returns: { guardianPrincipalId, accessToken, isNew }
|
|
82
80
|
*
|
|
83
|
-
* This endpoint is
|
|
84
|
-
* obtain actor tokens exclusively through
|
|
81
|
+
* This endpoint is restricted to private-network peers (loopback, RFC 1918,
|
|
82
|
+
* Docker bridge, etc.). iOS devices obtain actor tokens exclusively through
|
|
83
|
+
* the QR pairing flow.
|
|
85
84
|
*/
|
|
86
85
|
export async function handleGuardianBootstrap(
|
|
87
86
|
req: Request,
|
|
88
87
|
server: ServerWithRequestIP,
|
|
89
88
|
): Promise<Response> {
|
|
90
|
-
// Reject
|
|
91
|
-
|
|
89
|
+
// Reject requests forwarded from public networks. The gateway sets
|
|
90
|
+
// x-forwarded-for to the real client IP; if that IP is on a private
|
|
91
|
+
// network (loopback, Docker bridge, RFC 1918) the request is still
|
|
92
|
+
// considered local. Only reject when the forwarded IP is public.
|
|
93
|
+
const forwarded = req.headers.get("x-forwarded-for");
|
|
94
|
+
const forwardedIp = forwarded ? forwarded.split(",")[0].trim() : null;
|
|
95
|
+
if (forwardedIp && !isPrivateAddress(forwardedIp) && !isHttpAuthDisabled()) {
|
|
92
96
|
return httpError("FORBIDDEN", "Bootstrap endpoint is local-only", 403);
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
// Reject non-loopback
|
|
99
|
+
// Reject non-private-network peers (allows loopback, Docker bridge, etc.)
|
|
96
100
|
const peerIp = server.requestIP(req)?.address;
|
|
97
|
-
if ((!peerIp || !
|
|
101
|
+
if ((!peerIp || !isPrivateAddress(peerIp)) && !isHttpAuthDisabled()) {
|
|
98
102
|
return httpError("FORBIDDEN", "Bootstrap endpoint is local-only", 403);
|
|
99
103
|
}
|
|
100
104
|
|
|
@@ -134,7 +134,7 @@ export function handleEscalationIntercept(
|
|
|
134
134
|
void emitNotificationSignal({
|
|
135
135
|
sourceEventName: "ingress.escalation",
|
|
136
136
|
sourceChannel: sourceChannel as NotificationSourceChannel,
|
|
137
|
-
|
|
137
|
+
sourceContextId: conversationId,
|
|
138
138
|
attentionHints: {
|
|
139
139
|
requiresAction: true,
|
|
140
140
|
urgency: "high",
|
|
@@ -231,7 +231,7 @@ export async function handleVerificationIntercept(
|
|
|
231
231
|
void emitNotificationSignal({
|
|
232
232
|
sourceEventName: "ingress.trusted_contact.activated",
|
|
233
233
|
sourceChannel: sourceChannel as NotificationSourceChannel,
|
|
234
|
-
|
|
234
|
+
sourceContextId: conversationId,
|
|
235
235
|
attentionHints: {
|
|
236
236
|
requiresAction: false,
|
|
237
237
|
urgency: "low",
|
|
@@ -11,12 +11,19 @@
|
|
|
11
11
|
* results with is_valid flag and detailed error descriptions.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { join } from "node:path";
|
|
14
15
|
import { Database } from "bun:sqlite";
|
|
15
16
|
|
|
16
17
|
import { invalidateConfigCache } from "../../config/loader.js";
|
|
17
18
|
import { resetDb } from "../../memory/db-connection.js";
|
|
19
|
+
import { clearCache as clearTrustCache } from "../../permissions/trust-store.js";
|
|
18
20
|
import { getLogger } from "../../util/logger.js";
|
|
19
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
getDbPath,
|
|
23
|
+
getHooksDir,
|
|
24
|
+
getRootDir,
|
|
25
|
+
getWorkspaceDir,
|
|
26
|
+
} from "../../util/platform.js";
|
|
20
27
|
import { httpError } from "../http-errors.js";
|
|
21
28
|
import type { RouteDefinition } from "../http-router.js";
|
|
22
29
|
import { buildExportVBundle } from "../migrations/vbundle-builder.js";
|
|
@@ -135,13 +142,14 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
|
|
|
135
142
|
|
|
136
143
|
try {
|
|
137
144
|
const { archive, manifest } = buildExportVBundle({
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
trustPath: join(getRootDir(), "protected", "trust.json"),
|
|
146
|
+
hooksDir: getHooksDir(),
|
|
147
|
+
workspaceDir: getWorkspaceDir(),
|
|
140
148
|
source: "runtime-export",
|
|
141
149
|
description,
|
|
142
150
|
checkpoint: () => {
|
|
151
|
+
const dbPath = getDbPath();
|
|
143
152
|
try {
|
|
144
|
-
const dbPath = getDbPath();
|
|
145
153
|
const db = new Database(dbPath);
|
|
146
154
|
try {
|
|
147
155
|
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
@@ -149,10 +157,10 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
|
|
|
149
157
|
db.close();
|
|
150
158
|
}
|
|
151
159
|
} catch (err) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
);
|
|
160
|
+
// Best-effort: if the DB can't be checkpointed (e.g. not a valid
|
|
161
|
+
// SQLite file, missing WAL, etc.) we still proceed with the export
|
|
162
|
+
// using whatever is on disk.
|
|
163
|
+
log.warn({ err }, "WAL checkpoint failed — exporting without checkpoint");
|
|
156
164
|
}
|
|
157
165
|
},
|
|
158
166
|
});
|
|
@@ -289,8 +297,9 @@ export async function handleMigrationImportPreflight(
|
|
|
289
297
|
|
|
290
298
|
// Step 2: Analyze what would change on import
|
|
291
299
|
const pathResolver = new DefaultPathResolver(
|
|
292
|
-
|
|
293
|
-
|
|
300
|
+
join(getRootDir(), "protected"),
|
|
301
|
+
getWorkspaceDir(),
|
|
302
|
+
getHooksDir(),
|
|
294
303
|
);
|
|
295
304
|
|
|
296
305
|
const report = analyzeImport({
|
|
@@ -371,8 +380,9 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
|
|
|
371
380
|
}
|
|
372
381
|
|
|
373
382
|
const pathResolver = new DefaultPathResolver(
|
|
374
|
-
|
|
375
|
-
|
|
383
|
+
join(getRootDir(), "protected"),
|
|
384
|
+
getWorkspaceDir(),
|
|
385
|
+
getHooksDir(),
|
|
376
386
|
);
|
|
377
387
|
|
|
378
388
|
// Close the live SQLite connection before overwriting assistant.db on disk.
|
|
@@ -384,6 +394,7 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
|
|
|
384
394
|
pathResolver,
|
|
385
395
|
preValidatedManifest: validation.manifest,
|
|
386
396
|
preValidatedEntries: validation.entries,
|
|
397
|
+
workspaceDir: getWorkspaceDir(),
|
|
387
398
|
});
|
|
388
399
|
|
|
389
400
|
if (!result.ok) {
|
|
@@ -420,8 +431,9 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
|
|
|
420
431
|
);
|
|
421
432
|
}
|
|
422
433
|
|
|
423
|
-
// Invalidate in-process
|
|
434
|
+
// Invalidate in-process caches so imported settings.json and trust.json take effect
|
|
424
435
|
invalidateConfigCache();
|
|
436
|
+
clearTrustCache();
|
|
425
437
|
|
|
426
438
|
return Response.json(result.report);
|
|
427
439
|
} catch (err) {
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
} from "../../config/loader.js";
|
|
12
12
|
import type { CesClient } from "../../credential-execution/client.js";
|
|
13
13
|
import { setSentryOrganizationId } from "../../instrument.js";
|
|
14
|
+
import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
|
|
15
|
+
import { validateAnthropicApiKey } from "../../providers/anthropic/client.js";
|
|
14
16
|
import { initializeProviders } from "../../providers/registry.js";
|
|
15
17
|
import { credentialKey } from "../../security/credential-key.js";
|
|
16
18
|
import {
|
|
@@ -129,6 +131,21 @@ export async function handleAddSecret(
|
|
|
129
131
|
400,
|
|
130
132
|
);
|
|
131
133
|
}
|
|
134
|
+
// Validate Anthropic API keys before storing
|
|
135
|
+
if (name === "anthropic") {
|
|
136
|
+
const validation = await validateAnthropicApiKey(value);
|
|
137
|
+
if (!validation.valid) {
|
|
138
|
+
log.warn(
|
|
139
|
+
{ provider: name, reason: validation.reason },
|
|
140
|
+
"API key validation failed",
|
|
141
|
+
);
|
|
142
|
+
return Response.json(
|
|
143
|
+
{ success: false, error: validation.reason },
|
|
144
|
+
{ status: 422 },
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
132
149
|
const stored = await setSecureKeyAsync(name, value);
|
|
133
150
|
if (!stored) {
|
|
134
151
|
return httpError(
|
|
@@ -201,6 +218,7 @@ export async function handleAddSecret(
|
|
|
201
218
|
);
|
|
202
219
|
}
|
|
203
220
|
upsertCredentialMetadata(service, field, {});
|
|
221
|
+
await syncManualTokenConnection(service);
|
|
204
222
|
if (service === "vellum" && field === "platform_base_url") {
|
|
205
223
|
setPlatformBaseUrl(effectiveValue);
|
|
206
224
|
}
|