@vellumai/assistant 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +45 -29
- package/Dockerfile +1 -0
- package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
- package/bun.lock +3 -0
- package/docs/architecture/memory.md +5 -2
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
- package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
- package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
- package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
- package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- package/openapi.yaml +470 -25
- package/package.json +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +291 -0
- package/src/__tests__/app-control-flow.test.ts +21 -11
- package/src/__tests__/approval-cascade.test.ts +8 -16
- package/src/__tests__/approval-routes-http.test.ts +6 -0
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/assistant-event.test.ts +0 -10
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +48 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- package/src/__tests__/call-constants.test.ts +10 -1
- package/src/__tests__/call-controller.test.ts +127 -0
- package/src/__tests__/call-conversation-messages.test.ts +8 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +4 -2
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
- package/src/__tests__/config-loader-backfill.test.ts +379 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
- package/src/__tests__/config-watcher.test.ts +140 -69
- package/src/__tests__/context-search-agent-runner.test.ts +61 -3
- package/src/__tests__/context-search-conversations-source.test.ts +0 -24
- package/src/__tests__/context-search-fanout.test.ts +0 -1
- package/src/__tests__/context-search-memory-source.test.ts +6 -33
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +12 -7
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +457 -8
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
- package/src/__tests__/conversation-error.test.ts +150 -3
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
- package/src/__tests__/conversation-process-callsite.test.ts +38 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +74 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-store.test.ts +0 -18
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
- package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +476 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +61 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credentials-cli.test.ts +7 -0
- package/src/__tests__/cu-unified-flow.test.ts +176 -10
- package/src/__tests__/date-context.test.ts +164 -2
- package/src/__tests__/disk-pressure-guard.test.ts +262 -0
- package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
- package/src/__tests__/disk-pressure-policy.test.ts +241 -0
- package/src/__tests__/disk-pressure-routes.test.ts +379 -0
- package/src/__tests__/disk-pressure-tools.test.ts +277 -0
- package/src/__tests__/disk-usage.test.ts +150 -0
- package/src/__tests__/events-client-registration.test.ts +52 -0
- package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
- package/src/__tests__/file-write-tool.test.ts +4 -10
- package/src/__tests__/filing-service.test.ts +2 -20
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
- package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +260 -11
- package/src/__tests__/host-app-control-proxy.test.ts +195 -25
- package/src/__tests__/host-bash-proxy.test.ts +227 -34
- package/src/__tests__/host-bash-routes.test.ts +178 -13
- package/src/__tests__/host-cu-proxy.test.ts +210 -3
- package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
- package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
- package/src/__tests__/host-file-proxy.test.ts +268 -6
- package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
- package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
- package/src/__tests__/http-user-message-parity.test.ts +107 -1
- package/src/__tests__/injector-chain.test.ts +36 -16
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
- package/src/__tests__/managed-profile-guard.test.ts +18 -0
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/notification-decision-fallback.test.ts +91 -0
- package/src/__tests__/notification-decision-strategy.test.ts +22 -0
- package/src/__tests__/oauth-cli.test.ts +121 -0
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- package/src/__tests__/onboarding-persona-write.test.ts +308 -0
- package/src/__tests__/openai-provider.test.ts +45 -8
- package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
- package/src/__tests__/platform-callback-registration.test.ts +21 -4
- package/src/__tests__/platform.test.ts +2 -1
- package/src/__tests__/playbook-execution.test.ts +0 -43
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
- package/src/__tests__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/relay-server.test.ts +60 -5
- package/src/__tests__/runtime-events-sse.test.ts +4 -8
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -1
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
- package/src/__tests__/secret-response-routing.test.ts +7 -5
- package/src/__tests__/server-history-render.test.ts +82 -0
- package/src/__tests__/skill-include-graph.test.ts +31 -0
- package/src/__tests__/skill-load-tool.test.ts +44 -16
- package/src/__tests__/skills.test.ts +39 -0
- package/src/__tests__/suggestion-routes.test.ts +46 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
- package/src/__tests__/tool-executor.test.ts +155 -0
- package/src/__tests__/twilio-validation.test.ts +2 -2
- package/src/__tests__/voice-session-bridge.test.ts +3 -0
- package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
- package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
- package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
- package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +78 -0
- package/src/agent/loop.ts +11 -0
- package/src/approvals/guardian-request-resolvers.ts +3 -32
- package/src/backup/snapshot-lock.ts +2 -27
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-constants.ts +5 -8
- package/src/calls/call-controller.ts +130 -67
- package/src/calls/call-conversation-messages.ts +46 -10
- package/src/calls/relay-server.ts +7 -1
- package/src/calls/voice-session-bridge.ts +1 -1
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/contacts.ts +64 -25
- package/src/cli/commands/credentials.ts +56 -0
- package/src/cli/commands/memory-v2.ts +11 -10
- package/src/cli/commands/oauth/__tests__/connect.test.ts +401 -219
- package/src/cli/commands/oauth/connect.ts +124 -40
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
- package/src/cli/commands/platform/index.ts +16 -7
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/program.ts +4 -2
- package/src/config/assistant-feature-flags.ts +13 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
- package/src/config/env.ts +0 -8
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +199 -27
- package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +2 -0
- package/src/config/schemas/memory-lifecycle.ts +13 -0
- package/src/config/schemas/memory-v2.ts +76 -12
- package/src/config/schemas/platform.ts +43 -3
- package/src/config/schemas/services.ts +28 -0
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -25
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/config-watcher.ts +85 -57
- package/src/daemon/conversation-agent-loop-handlers.ts +38 -0
- package/src/daemon/conversation-agent-loop.ts +183 -43
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +22 -10
- package/src/daemon/conversation-process.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +26 -0
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +211 -29
- package/src/daemon/conversation-tool-setup.ts +66 -19
- package/src/daemon/conversation.ts +18 -23
- package/src/daemon/date-context.ts +71 -22
- package/src/daemon/disk-pressure-background-gate.ts +73 -0
- package/src/daemon/disk-pressure-guard.ts +343 -0
- package/src/daemon/disk-pressure-policy.ts +163 -0
- package/src/daemon/handlers/shared.ts +26 -1
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +137 -41
- package/src/daemon/host-bash-proxy.ts +47 -22
- package/src/daemon/host-browser-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +50 -4
- package/src/daemon/host-file-proxy.ts +44 -8
- package/src/daemon/host-transfer-proxy.ts +97 -6
- package/src/daemon/lifecycle.ts +167 -101
- package/src/daemon/meet-host-supervisor.ts +4 -4
- package/src/daemon/meet-manifest-loader.ts +0 -1
- package/src/daemon/memory-v2-startup.ts +66 -15
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +4 -0
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- package/src/daemon/message-types/messages.ts +22 -1
- package/src/daemon/profiler-run-store.ts +5 -5
- package/src/daemon/tool-setup-types.ts +2 -2
- package/src/documents/document-store.ts +119 -0
- package/src/filing/filing-service.ts +29 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
- package/src/heartbeat/heartbeat-run-store.ts +13 -0
- package/src/heartbeat/heartbeat-service.ts +205 -31
- package/src/home/feed-scheduler.ts +18 -0
- package/src/inbound/platform-callback-registration.ts +8 -15
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/assistant-server.ts +149 -38
- package/src/ipc/gateway-client.ts +37 -3
- package/src/ipc/skill-server.ts +99 -42
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/protocol.ts +5 -7
- package/src/media/image-service.ts +1 -7
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +34 -51
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
- package/src/memory/admin.ts +5 -9
- package/src/memory/context-search/agent-runner.ts +19 -2
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +1 -16
- package/src/memory/context-search/sources/memory.ts +2 -3
- package/src/memory/context-search/sources/pkb.ts +2 -3
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +4 -12
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +136 -82
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
- package/src/memory/graph/conversation-graph-memory.ts +72 -61
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +11 -67
- package/src/memory/graph/graph-search.ts +4 -24
- package/src/memory/graph/retriever.test.ts +12 -1
- package/src/memory/graph/retriever.ts +10 -15
- package/src/memory/graph/tool-handlers.ts +3 -4
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/indexer.ts +53 -45
- package/src/memory/job-handlers/backfill.ts +2 -11
- package/src/memory/job-handlers/cleanup.ts +43 -0
- package/src/memory/job-handlers/embedding.ts +6 -8
- package/src/memory/job-handlers/summarization.ts +2 -7
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
- package/src/memory/jobs/embed-concept-page.ts +223 -87
- package/src/memory/jobs-store.ts +48 -0
- package/src/memory/jobs-worker.ts +85 -43
- package/src/memory/memory-v2-activation-log-store.ts +32 -14
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +7 -0
- package/src/memory/pkb/pkb-search.ts +4 -5
- package/src/memory/qdrant-client.ts +3 -13
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/search/semantic.ts +10 -72
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +346 -255
- package/src/memory/v2/__tests__/consolidation-job.test.ts +61 -40
- package/src/memory/v2/__tests__/injection.test.ts +297 -190
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +326 -9
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +113 -196
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/static-context.test.ts +77 -14
- package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
- package/src/memory/v2/activation.ts +149 -156
- package/src/memory/v2/consolidation-job.ts +69 -20
- package/src/memory/v2/injection.ts +75 -68
- package/src/memory/v2/page-store.ts +39 -0
- package/src/memory/v2/prompts/consolidation.ts +41 -1
- package/src/memory/v2/qdrant.ts +306 -46
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +77 -110
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +82 -59
- package/src/memory/v2/static-context.ts +26 -8
- package/src/memory/v2/sweep-job.ts +5 -6
- package/src/memory/v2/types.ts +17 -10
- package/src/notifications/copy-composer.ts +47 -0
- package/src/notifications/decision-engine.ts +46 -0
- package/src/notifications/signal.ts +4 -0
- package/src/oauth/AGENTS.md +3 -1
- package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- package/src/permissions/gateway-threshold-reader.ts +116 -8
- package/src/permissions/prompter.ts +86 -96
- package/src/permissions/secret-prompter.ts +31 -31
- package/src/plugins/defaults/injectors.ts +36 -4
- package/src/plugins/defaults/memory-retrieval.ts +5 -6
- package/src/plugins/types.ts +7 -0
- package/src/proactive-artifact/aux-message-injector.ts +74 -0
- package/src/proactive-artifact/decision.test.ts +226 -0
- package/src/proactive-artifact/decision.ts +165 -0
- package/src/proactive-artifact/index.ts +7 -0
- package/src/proactive-artifact/job.test.ts +914 -0
- package/src/proactive-artifact/job.ts +366 -0
- package/src/proactive-artifact/message-copy.ts +58 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +21 -7
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/SOUL.md +13 -28
- package/src/providers/__tests__/retry-callsite.test.ts +222 -1
- package/src/providers/model-intents.ts +7 -0
- package/src/providers/openrouter/client.ts +8 -0
- package/src/providers/retry.ts +50 -0
- package/src/providers/types.ts +1 -0
- package/src/runtime/__tests__/agent-wake.test.ts +456 -3
- package/src/runtime/agent-wake.ts +238 -100
- package/src/runtime/assistant-event-hub.ts +36 -6
- package/src/runtime/assistant-event.ts +0 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/same-actor.ts +216 -0
- package/src/runtime/channel-approvals.ts +3 -2
- package/src/runtime/channel-retry-sweep.ts +65 -1
- package/src/runtime/local-actor-identity.ts +52 -11
- package/src/runtime/pending-interactions.ts +27 -15
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
- package/src/runtime/routes/approval-routes.ts +7 -3
- package/src/runtime/routes/client-routes.ts +20 -2
- package/src/runtime/routes/consolidation-routes.ts +8 -9
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-query-routes.ts +44 -1
- package/src/runtime/routes/conversation-routes.ts +35 -26
- package/src/runtime/routes/debug-bash-routes.ts +165 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +6 -2
- package/src/runtime/routes/documents-routes.ts +2 -75
- package/src/runtime/routes/events-routes.ts +41 -9
- package/src/runtime/routes/filing-routes.ts +2 -3
- package/src/runtime/routes/host-bash-routes.ts +23 -3
- package/src/runtime/routes/host-cu-routes.ts +33 -6
- package/src/runtime/routes/host-file-routes.ts +32 -6
- package/src/runtime/routes/host-transfer-routes.ts +79 -16
- package/src/runtime/routes/identity-routes.ts +7 -138
- package/src/runtime/routes/inbound-message-handler.ts +77 -12
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/memory-item-routes.test.ts +37 -17
- package/src/runtime/routes/memory-item-routes.ts +5 -6
- package/src/runtime/routes/memory-v2-routes.ts +136 -17
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- package/src/schedule/run-script.ts +37 -5
- package/src/schedule/scheduler.ts +20 -1
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/secure-keys.ts +55 -0
- package/src/skills/include-graph.ts +35 -13
- package/src/skills/remote-skill-policy.ts +4 -10
- package/src/subagent/index.ts +1 -7
- package/src/subagent/manager.ts +1 -15
- package/src/tasks/task-runner.ts +0 -1
- package/src/tasks/task-store.ts +0 -3
- package/src/tools/background-tool-registry.ts +17 -3
- package/src/tools/document/document-tool.ts +20 -0
- package/src/tools/executor.ts +18 -2
- package/src/tools/host-filesystem/edit.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +43 -1
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +43 -1
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +56 -11
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +43 -1
- package/src/tools/host-terminal/host-shell.ts +13 -6
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +14 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/permission-checker.ts +15 -0
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- package/src/tools/skills/load.ts +24 -20
- package/src/tools/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +31 -6
- package/src/tools/tool-name-aliases.ts +19 -0
- package/src/tools/types.ts +43 -3
- package/src/tts/provider-catalog.ts +3 -5
- package/src/util/disk-usage.ts +138 -0
- package/src/util/platform.ts +21 -11
- package/src/util/process-liveness.ts +26 -0
- package/src/workspace/heartbeat-service.ts +19 -0
- package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +14 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
- package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
- package/src/workspace/migrations/registry.ts +14 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -404
- package/src/signals/bash.ts +0 -198
|
@@ -10,7 +10,6 @@ mock.module("../../../util/logger.js", () => ({
|
|
|
10
10
|
import {
|
|
11
11
|
sampleConcepts as sharedSampleConcepts,
|
|
12
12
|
sampleConfig,
|
|
13
|
-
sampleSkills,
|
|
14
13
|
} from "../../../memory/__tests__/fixtures/memory-v2-activation-fixtures.js";
|
|
15
14
|
|
|
16
15
|
let rawConfigFixture: Record<string, unknown> = {};
|
|
@@ -36,7 +35,6 @@ import {
|
|
|
36
35
|
backfillMemoryV2ActivationMessageId,
|
|
37
36
|
type MemoryV2ConceptRowRecord,
|
|
38
37
|
type MemoryV2ConfigSnapshot,
|
|
39
|
-
type MemoryV2SkillRowRecord,
|
|
40
38
|
recordMemoryV2ActivationLog,
|
|
41
39
|
} from "../../../memory/memory-v2-activation-log-store.js";
|
|
42
40
|
import {
|
|
@@ -153,7 +151,6 @@ describe("GET /v1/messages/:id/llm-context — memoryV2Activation", () => {
|
|
|
153
151
|
turn: 4,
|
|
154
152
|
mode: "per-turn",
|
|
155
153
|
concepts: sampleConcepts,
|
|
156
|
-
skills: sampleSkills,
|
|
157
154
|
config: sampleConfig,
|
|
158
155
|
});
|
|
159
156
|
backfillMemoryV2ActivationMessageId(conversationId, messageId);
|
|
@@ -163,7 +160,6 @@ describe("GET /v1/messages/:id/llm-context — memoryV2Activation", () => {
|
|
|
163
160
|
turn: number;
|
|
164
161
|
mode: "context-load" | "per-turn";
|
|
165
162
|
concepts: MemoryV2ConceptRowRecord[];
|
|
166
|
-
skills: MemoryV2SkillRowRecord[];
|
|
167
163
|
config: MemoryV2ConfigSnapshot;
|
|
168
164
|
} | null;
|
|
169
165
|
memoryRecall: unknown;
|
|
@@ -173,7 +169,6 @@ describe("GET /v1/messages/:id/llm-context — memoryV2Activation", () => {
|
|
|
173
169
|
expect(body.memoryV2Activation!.turn).toBe(4);
|
|
174
170
|
expect(body.memoryV2Activation!.mode).toBe("per-turn");
|
|
175
171
|
expect(body.memoryV2Activation!.concepts).toEqual(sampleConcepts);
|
|
176
|
-
expect(body.memoryV2Activation!.skills).toEqual(sampleSkills);
|
|
177
172
|
expect(body.memoryV2Activation!.config).toEqual(sampleConfig);
|
|
178
173
|
// Backwards-compat: memoryRecall field still present.
|
|
179
174
|
expect(body).toHaveProperty("memoryRecall");
|
|
@@ -90,7 +90,7 @@ describe("setHeartbeatConfig handler", () => {
|
|
|
90
90
|
// invalidation + getConfig() read picked up the new on-disk state.
|
|
91
91
|
expect(result.success).toBe(true);
|
|
92
92
|
expect(result.enabled).toBe(true);
|
|
93
|
-
expect(result.intervalMs).toBe(
|
|
93
|
+
expect(result.intervalMs).toBe(30 * 60_000);
|
|
94
94
|
expect(result.activeHoursStart).toBe(8);
|
|
95
95
|
expect(result.activeHoursEnd).toBe(22);
|
|
96
96
|
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the memory v2 route handlers in `memory-v2-routes.ts`.
|
|
3
|
+
*
|
|
4
|
+
* Currently focused on `memory_v2_list_concept_pages`:
|
|
5
|
+
* - empty workspace → returns no pages
|
|
6
|
+
* - populated workspace → surfaces slug, bodyBytes, edgeCount, updatedAtMs
|
|
7
|
+
* - corrupt page on disk → logged-and-skipped, does not poison listing
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
11
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
15
|
+
|
|
16
|
+
import { writePage } from "../../../memory/v2/page-store.js";
|
|
17
|
+
import type { ConceptPage } from "../../../memory/v2/types.js";
|
|
18
|
+
import type { MemoryV2ListConceptPagesResult } from "../memory-v2-routes.js";
|
|
19
|
+
import { ROUTES } from "../memory-v2-routes.js";
|
|
20
|
+
import type { RouteDefinition } from "../types.js";
|
|
21
|
+
|
|
22
|
+
// ─── Setup ─────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
let workspaceDir: string;
|
|
25
|
+
let origWorkspaceDir: string | undefined;
|
|
26
|
+
|
|
27
|
+
function findHandler(operationId: string): RouteDefinition["handler"] {
|
|
28
|
+
const route = ROUTES.find((r) => r.operationId === operationId);
|
|
29
|
+
if (!route) throw new Error(`Route ${operationId} not found`);
|
|
30
|
+
return route.handler;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
workspaceDir = mkdtempSync(join(tmpdir(), "vellum-memv2-list-"));
|
|
35
|
+
origWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
|
|
36
|
+
process.env.VELLUM_WORKSPACE_DIR = workspaceDir;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
if (origWorkspaceDir === undefined) {
|
|
41
|
+
delete process.env.VELLUM_WORKSPACE_DIR;
|
|
42
|
+
} else {
|
|
43
|
+
process.env.VELLUM_WORKSPACE_DIR = origWorkspaceDir;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
47
|
+
} catch {
|
|
48
|
+
// best-effort cleanup
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ─── Tests ─────────────────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
describe("memory_v2_list_concept_pages handler", () => {
|
|
55
|
+
test("returns empty list for an empty workspace", async () => {
|
|
56
|
+
const handler = findHandler("memory_v2_list_concept_pages");
|
|
57
|
+
const result = (await handler({
|
|
58
|
+
body: {},
|
|
59
|
+
})) as MemoryV2ListConceptPagesResult;
|
|
60
|
+
|
|
61
|
+
expect(result).toEqual({ pages: [] });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("returns slugs, body bytes, edge counts, and mtimes for populated workspace", async () => {
|
|
65
|
+
const before = Date.now();
|
|
66
|
+
|
|
67
|
+
const pages: ConceptPage[] = [
|
|
68
|
+
{
|
|
69
|
+
slug: "alice",
|
|
70
|
+
frontmatter: { edges: ["bob", "carol"], ref_files: [] },
|
|
71
|
+
body: "Alice prefers VS Code.\n",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
slug: "bob",
|
|
75
|
+
frontmatter: { edges: [], ref_files: [] },
|
|
76
|
+
body: "Bob ships at end of day.\nLikes async standups.\n",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
slug: "people/carol",
|
|
80
|
+
frontmatter: { edges: ["alice"], ref_files: [] },
|
|
81
|
+
body: "Carol leads the platform team.\n",
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
for (const page of pages) {
|
|
85
|
+
await writePage(workspaceDir, page);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const handler = findHandler("memory_v2_list_concept_pages");
|
|
89
|
+
const result = (await handler({
|
|
90
|
+
body: {},
|
|
91
|
+
})) as MemoryV2ListConceptPagesResult;
|
|
92
|
+
|
|
93
|
+
expect(result.pages).toHaveLength(3);
|
|
94
|
+
|
|
95
|
+
const bySlug = new Map(result.pages.map((p) => [p.slug, p]));
|
|
96
|
+
|
|
97
|
+
const alice = bySlug.get("alice");
|
|
98
|
+
expect(alice).toBeDefined();
|
|
99
|
+
expect(alice!.bodyBytes).toBe(Buffer.byteLength(pages[0]!.body, "utf8"));
|
|
100
|
+
expect(alice!.edgeCount).toBe(2);
|
|
101
|
+
expect(alice!.updatedAtMs).toBeGreaterThanOrEqual(before);
|
|
102
|
+
// updatedAtMs must be an integer on the wire — Swift clients decode it as
|
|
103
|
+
// Int64 and a sub-millisecond float (which fs.Stats.mtimeMs returns by
|
|
104
|
+
// default) breaks JSONDecoder strict number parsing.
|
|
105
|
+
expect(Number.isInteger(alice!.updatedAtMs)).toBe(true);
|
|
106
|
+
|
|
107
|
+
const bob = bySlug.get("bob");
|
|
108
|
+
expect(bob).toBeDefined();
|
|
109
|
+
expect(bob!.bodyBytes).toBe(Buffer.byteLength(pages[1]!.body, "utf8"));
|
|
110
|
+
expect(bob!.edgeCount).toBe(0);
|
|
111
|
+
expect(bob!.updatedAtMs).toBeGreaterThanOrEqual(before);
|
|
112
|
+
expect(Number.isInteger(bob!.updatedAtMs)).toBe(true);
|
|
113
|
+
|
|
114
|
+
const carol = bySlug.get("people/carol");
|
|
115
|
+
expect(carol).toBeDefined();
|
|
116
|
+
expect(carol!.bodyBytes).toBe(Buffer.byteLength(pages[2]!.body, "utf8"));
|
|
117
|
+
expect(carol!.edgeCount).toBe(1);
|
|
118
|
+
expect(carol!.updatedAtMs).toBeGreaterThanOrEqual(before);
|
|
119
|
+
expect(Number.isInteger(carol!.updatedAtMs)).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("tolerates a single corrupt page — returns valid pages and skips the broken one", async () => {
|
|
123
|
+
await writePage(workspaceDir, {
|
|
124
|
+
slug: "valid-page",
|
|
125
|
+
frontmatter: { edges: [], ref_files: [] },
|
|
126
|
+
body: "Body of the valid page.\n",
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// A `.md` file with frontmatter that fails schema validation — `edges`
|
|
130
|
+
// must be a list of strings, not a single number — so `readPage` throws.
|
|
131
|
+
const conceptsDir = join(workspaceDir, "memory", "concepts");
|
|
132
|
+
await mkdir(conceptsDir, { recursive: true });
|
|
133
|
+
await writeFile(
|
|
134
|
+
join(conceptsDir, "broken.md"),
|
|
135
|
+
"---\nedges: 42\n---\nbroken body\n",
|
|
136
|
+
"utf-8",
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const handler = findHandler("memory_v2_list_concept_pages");
|
|
140
|
+
const result = (await handler({
|
|
141
|
+
body: {},
|
|
142
|
+
})) as MemoryV2ListConceptPagesResult;
|
|
143
|
+
|
|
144
|
+
expect(result.pages).toHaveLength(1);
|
|
145
|
+
expect(result.pages.map((p) => p.slug)).toEqual(["valid-page"]);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -62,8 +62,9 @@ function handleConfirm({ body }: RouteHandlerArgs) {
|
|
|
62
62
|
throw new BadRequestError("decision must resolve to allow or deny");
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
// Validation passed —
|
|
66
|
-
|
|
65
|
+
// Validation passed. Use get() here — the prompter (or ACP directResolve path)
|
|
66
|
+
// owns deregistration via pendingInteractions.resolve().
|
|
67
|
+
const interaction = peeked;
|
|
67
68
|
|
|
68
69
|
log.info(
|
|
69
70
|
{
|
|
@@ -93,7 +94,9 @@ function handleConfirm({ body }: RouteHandlerArgs) {
|
|
|
93
94
|
});
|
|
94
95
|
|
|
95
96
|
// ACP permissions: resolve directly without a Conversation object.
|
|
97
|
+
// No PermissionPrompter involved, so the route owns deregistration.
|
|
96
98
|
if (interaction.directResolve) {
|
|
99
|
+
pendingInteractions.resolve(requestId);
|
|
97
100
|
interaction.directResolve(effectiveDecision as UserDecision);
|
|
98
101
|
return { accepted: true };
|
|
99
102
|
}
|
|
@@ -139,7 +142,8 @@ function handleSecret({ body }: RouteHandlerArgs) {
|
|
|
139
142
|
throw new BadRequestError('delivery must be "store" or "transient_send"');
|
|
140
143
|
}
|
|
141
144
|
|
|
142
|
-
|
|
145
|
+
// Use get() — SecretPrompter.resolveSecret() owns deregistration.
|
|
146
|
+
const interaction = pendingInteractions.get(requestId);
|
|
143
147
|
if (!interaction) {
|
|
144
148
|
throw new NotFoundError("No pending interaction found for this requestId");
|
|
145
149
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import type { HostProxyCapability } from "../../channels/types.js";
|
|
11
|
+
import { isHttpAuthDisabled } from "../../config/env.js";
|
|
11
12
|
import { datesToISO } from "../../util/json.js";
|
|
12
13
|
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
13
14
|
import { NotFoundError } from "./errors.js";
|
|
@@ -33,7 +34,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
33
34
|
responseBody: z.object({
|
|
34
35
|
clients: z.array(z.object({}).passthrough()),
|
|
35
36
|
}),
|
|
36
|
-
handler: ({ queryParams }) => {
|
|
37
|
+
handler: ({ queryParams, headers }) => {
|
|
37
38
|
const capability = queryParams?.capability as
|
|
38
39
|
| HostProxyCapability
|
|
39
40
|
| undefined;
|
|
@@ -42,8 +43,25 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
42
43
|
? assistantEventHub.listClientsByCapability(capability)
|
|
43
44
|
: assistantEventHub.listClients();
|
|
44
45
|
|
|
46
|
+
// Defense-in-depth: filter the listing to clients owned by the calling
|
|
47
|
+
// actor so users cannot enumerate other users' connected client IDs.
|
|
48
|
+
// Clients with no stored `actorPrincipalId` (legacy SSE subscribers from
|
|
49
|
+
// before host-proxy-same-user, service-gateway tokens) are filtered out
|
|
50
|
+
// — fail-closed is the right default for this security boundary.
|
|
51
|
+
// Dev-bypass mode (DISABLE_HTTP_AUTH=true, mirroring
|
|
52
|
+
// require-bound-guardian.ts) preserves the previous "return all" behavior
|
|
53
|
+
// for platform-managed deployments where the platform handles auth.
|
|
54
|
+
const callerPrincipalId = headers?.["x-vellum-actor-principal-id"];
|
|
55
|
+
const filtered = isHttpAuthDisabled()
|
|
56
|
+
? clients
|
|
57
|
+
: clients.filter(
|
|
58
|
+
(c) =>
|
|
59
|
+
c.actorPrincipalId !== undefined &&
|
|
60
|
+
c.actorPrincipalId === callerPrincipalId,
|
|
61
|
+
);
|
|
62
|
+
|
|
45
63
|
return {
|
|
46
|
-
clients:
|
|
64
|
+
clients: filtered.map((c) =>
|
|
47
65
|
datesToISO({
|
|
48
66
|
clientId: c.clientId,
|
|
49
67
|
interfaceId: c.interfaceId,
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
* only surface its config and provide an on-demand trigger for the Settings UI.
|
|
9
9
|
*
|
|
10
10
|
* `available` mirrors the filing route's `available` field: it reflects which
|
|
11
|
-
* background memory job is active for this instance. When
|
|
12
|
-
* is
|
|
11
|
+
* background memory job is active for this instance. When
|
|
12
|
+
* `config.memory.v2.enabled` is false, consolidation returns
|
|
13
|
+
* `available: false` and the UI hides the row.
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import { z } from "zod";
|
|
16
17
|
|
|
17
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
18
18
|
import { getConfig } from "../../config/loader.js";
|
|
19
19
|
import { getMemoryCheckpoint } from "../../memory/checkpoints.js";
|
|
20
20
|
import {
|
|
@@ -26,7 +26,7 @@ import { BadRequestError } from "./errors.js";
|
|
|
26
26
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
27
27
|
|
|
28
28
|
function isConsolidationAvailable(): boolean {
|
|
29
|
-
return
|
|
29
|
+
return getConfig().memory.v2.enabled;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function consolidationIntervalMs(): number {
|
|
@@ -66,14 +66,13 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
66
66
|
success: z.boolean(),
|
|
67
67
|
}),
|
|
68
68
|
handler: async (_args: RouteHandlerArgs) => {
|
|
69
|
-
const
|
|
70
|
-
const v2Config = getConfig().memory.v2;
|
|
69
|
+
const enabled = getConfig().memory.v2.enabled;
|
|
71
70
|
const intervalMs = consolidationIntervalMs();
|
|
72
71
|
const lastRunAt = readLastRunAt();
|
|
73
72
|
const nextRunAt = lastRunAt != null ? lastRunAt + intervalMs : null;
|
|
74
73
|
return {
|
|
75
|
-
available,
|
|
76
|
-
enabled
|
|
74
|
+
available: enabled,
|
|
75
|
+
enabled,
|
|
77
76
|
intervalMs,
|
|
78
77
|
nextRunAt,
|
|
79
78
|
lastRunAt,
|
|
@@ -99,7 +98,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
99
98
|
handler: async (_args: RouteHandlerArgs) => {
|
|
100
99
|
if (!isConsolidationAvailable()) {
|
|
101
100
|
throw new BadRequestError(
|
|
102
|
-
"Consolidation is not available (memory
|
|
101
|
+
"Consolidation is not available (memory.v2.enabled is false)",
|
|
103
102
|
);
|
|
104
103
|
}
|
|
105
104
|
// Coalesce: don't pile up duplicate jobs if the worker hasn't picked up
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
-
deleteContact,
|
|
15
14
|
getAssistantContactMetadata,
|
|
16
15
|
getChannelById,
|
|
17
16
|
getContact,
|
|
@@ -42,7 +41,6 @@ import {
|
|
|
42
41
|
import {
|
|
43
42
|
BadRequestError,
|
|
44
43
|
ConflictError,
|
|
45
|
-
ForbiddenError,
|
|
46
44
|
NotFoundError,
|
|
47
45
|
} from "./errors.js";
|
|
48
46
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
@@ -154,17 +152,6 @@ function handleGetContact(contactId: string) {
|
|
|
154
152
|
};
|
|
155
153
|
}
|
|
156
154
|
|
|
157
|
-
function handleDeleteContact(contactId: string) {
|
|
158
|
-
const result = deleteContact(contactId);
|
|
159
|
-
if (result === "not_found") {
|
|
160
|
-
throw new NotFoundError(`Contact "${contactId}" not found`);
|
|
161
|
-
}
|
|
162
|
-
if (result === "is_guardian") {
|
|
163
|
-
throw new ForbiddenError("Cannot delete a guardian contact");
|
|
164
|
-
}
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
155
|
// ---------------------------------------------------------------------------
|
|
169
156
|
// Invite handlers (transport-agnostic)
|
|
170
157
|
// ---------------------------------------------------------------------------
|
|
@@ -502,18 +489,6 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
502
489
|
handler: ({ pathParams }: RouteHandlerArgs) =>
|
|
503
490
|
handleGetContact(pathParams!.id),
|
|
504
491
|
},
|
|
505
|
-
{
|
|
506
|
-
operationId: "deleteContact",
|
|
507
|
-
endpoint: "contacts/:id",
|
|
508
|
-
method: "DELETE",
|
|
509
|
-
policyKey: "contacts",
|
|
510
|
-
summary: "Delete a contact",
|
|
511
|
-
description: "Delete a contact by ID.",
|
|
512
|
-
tags: ["contacts"],
|
|
513
|
-
responseStatus: "204",
|
|
514
|
-
handler: ({ pathParams }: RouteHandlerArgs) =>
|
|
515
|
-
handleDeleteContact(pathParams!.id),
|
|
516
|
-
},
|
|
517
492
|
{
|
|
518
493
|
operationId: "upsert_contact",
|
|
519
494
|
endpoint: "contacts",
|
|
@@ -22,7 +22,9 @@ import { z } from "zod";
|
|
|
22
22
|
|
|
23
23
|
import {
|
|
24
24
|
deepMergeOverwrite,
|
|
25
|
+
fillContextDefaultsForMissingKeys,
|
|
25
26
|
getConfig,
|
|
27
|
+
getDeploymentContextDefaults,
|
|
26
28
|
invalidateConfigCache,
|
|
27
29
|
loadRawConfig,
|
|
28
30
|
saveRawConfig,
|
|
@@ -312,9 +314,50 @@ async function handleSetEmbeddingConfig({ body }: RouteHandlerArgs) {
|
|
|
312
314
|
}
|
|
313
315
|
}
|
|
314
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Apply deployment-context defaults to a raw config payload before it goes
|
|
319
|
+
* out over the wire from `GET /v1/config`. The in-memory `loadConfig()`
|
|
320
|
+
* already layers these defaults for daemon-internal consumers; the GET
|
|
321
|
+
* response needs the same treatment so external clients (macOS, web, CLI)
|
|
322
|
+
* see the effective value rather than `undefined` when the daemon hasn't
|
|
323
|
+
* persisted an explicit choice yet. Without this, macOS's
|
|
324
|
+
* `loadServiceModes(config:)` short-circuits when `services.inference.mode`
|
|
325
|
+
* is missing and falls back to the SwiftUI `@Published` default of
|
|
326
|
+
* "your-own", which renders the wrong segment selection on freshly-hatched
|
|
327
|
+
* platform-managed assistants.
|
|
328
|
+
*
|
|
329
|
+
* Guards against `loadRawConfig()` handing us a value that is technically
|
|
330
|
+
* valid JSON but not a plain object (e.g. literal `null`, a number, or an
|
|
331
|
+
* array). `loadRawConfig` is typed `Record<string, unknown>` but `JSON.parse`
|
|
332
|
+
* itself doesn't enforce that — a malformed-but-parseable `config.json`
|
|
333
|
+
* would blow up `fillContextDefaultsForMissingKeys` on its `target[key]` /
|
|
334
|
+
* `fileConfig[key]` accesses, turning `GET /v1/config` into a 500 where it
|
|
335
|
+
* used to succeed (returning the malformed payload as-is). When `raw` is
|
|
336
|
+
* not a plain object, we return it unchanged.
|
|
337
|
+
*
|
|
338
|
+
* Exported for direct unit testing.
|
|
339
|
+
*/
|
|
340
|
+
export function applyContextDefaultsToRawConfig(raw: unknown): unknown {
|
|
341
|
+
const contextDefaults = getDeploymentContextDefaults();
|
|
342
|
+
if (
|
|
343
|
+
Object.keys(contextDefaults).length === 0 ||
|
|
344
|
+
raw === null ||
|
|
345
|
+
typeof raw !== "object" ||
|
|
346
|
+
Array.isArray(raw)
|
|
347
|
+
) {
|
|
348
|
+
return raw;
|
|
349
|
+
}
|
|
350
|
+
fillContextDefaultsForMissingKeys(
|
|
351
|
+
raw as Record<string, unknown>,
|
|
352
|
+
raw as Record<string, unknown>,
|
|
353
|
+
contextDefaults,
|
|
354
|
+
);
|
|
355
|
+
return raw;
|
|
356
|
+
}
|
|
357
|
+
|
|
315
358
|
function handleGetConfig() {
|
|
316
359
|
try {
|
|
317
|
-
return loadRawConfig();
|
|
360
|
+
return applyContextDefaultsToRawConfig(loadRawConfig());
|
|
318
361
|
} catch (err) {
|
|
319
362
|
const message = err instanceof Error ? err.message : String(err);
|
|
320
363
|
throw new InternalError(`Failed to read config: ${message}`);
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
resolveSlash,
|
|
40
40
|
} from "../../daemon/conversation-slash.js";
|
|
41
41
|
import { getOrCreateConversation as getOrCreateConversationInstance } from "../../daemon/conversation-store.js";
|
|
42
|
+
import { canonicalizeTimeZone } from "../../daemon/date-context.js";
|
|
42
43
|
import {
|
|
43
44
|
getCannedFirstGreeting,
|
|
44
45
|
isWakeUpGreeting,
|
|
@@ -86,6 +87,8 @@ import {
|
|
|
86
87
|
getOrCreateConversation,
|
|
87
88
|
} from "../../memory/conversation-key-store.js";
|
|
88
89
|
import { searchConversations } from "../../memory/conversation-queries.js";
|
|
90
|
+
import { normalizeOnboardingContext } from "../../prompts/normalize-onboarding.js";
|
|
91
|
+
import { writeOnboardingSection } from "../../prompts/persona-resolver.js";
|
|
89
92
|
import { getConfiguredProvider } from "../../providers/provider-send-message.js";
|
|
90
93
|
import type { Provider } from "../../providers/types.js";
|
|
91
94
|
import { checkIngressForSecrets } from "../../security/secret-ingress.js";
|
|
@@ -999,7 +1002,7 @@ function mergeConsecutiveAssistantMessages(messages: MessageRow[]): {
|
|
|
999
1002
|
/**
|
|
1000
1003
|
* Persist the pre-chat onboarding payload to disk.
|
|
1001
1004
|
*
|
|
1002
|
-
* Runs only on the very first message of a fresh conversation.
|
|
1005
|
+
* Runs only on the very first message of a fresh conversation. Four
|
|
1003
1006
|
* artifacts are produced:
|
|
1004
1007
|
*
|
|
1005
1008
|
* 1. `data/onboarding-context.json` — sidecar read by the
|
|
@@ -1007,12 +1010,15 @@ function mergeConsecutiveAssistantMessages(messages: MessageRow[]): {
|
|
|
1007
1010
|
* the pure-recomputation write cycle (every turn boundary rebuilds
|
|
1008
1011
|
* facts from markdown; the sidecar is the durable source for the
|
|
1009
1012
|
* tool/task/tone chips).
|
|
1010
|
-
* 2. `IDENTITY.md`
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
1015
|
-
* 3.
|
|
1013
|
+
* 2. `IDENTITY.md` — assistant persona seed file, only written when
|
|
1014
|
+
* missing so we never clobber existing content. Feeds the system
|
|
1015
|
+
* prompt and the relationship-state writer's `parseIdentity`
|
|
1016
|
+
* helper after a daemon restart when the in-memory onboarding
|
|
1017
|
+
* context is gone.
|
|
1018
|
+
* 3. Onboarding section in the guardian persona file — written via
|
|
1019
|
+
* `writeOnboardingSection`, which handles the user's preferred
|
|
1020
|
+
* name (with fallback to root `USER.md`).
|
|
1021
|
+
* 4. `data/relationship-state.json` — kicked off fire-and-forget so
|
|
1016
1022
|
* the Home page can populate immediately on first visit instead
|
|
1017
1023
|
* of waiting for the first agent-turn boundary.
|
|
1018
1024
|
*
|
|
@@ -1057,25 +1063,11 @@ export function persistOnboardingArtifacts(onboarding: {
|
|
|
1057
1063
|
}
|
|
1058
1064
|
}
|
|
1059
1065
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
const content = readFileSync(userPath, "utf-8");
|
|
1066
|
-
const updated = content.replace(
|
|
1067
|
-
/^- (?:\*\*)?Name:(?:\*\*)?\s*.*$/m,
|
|
1068
|
-
() => `- **Name:** ${userName}`,
|
|
1069
|
-
);
|
|
1070
|
-
if (updated !== content) {
|
|
1071
|
-
writeFileSync(userPath, updated, "utf-8");
|
|
1072
|
-
}
|
|
1073
|
-
} else {
|
|
1074
|
-
writeFileSync(userPath, `# User\n\n- **Name:** ${userName}\n`, "utf-8");
|
|
1075
|
-
}
|
|
1076
|
-
} catch (err) {
|
|
1077
|
-
log.warn({ err, userPath }, "Failed to seed USER.md from onboarding");
|
|
1078
|
-
}
|
|
1066
|
+
try {
|
|
1067
|
+
const normalized = normalizeOnboardingContext(onboarding);
|
|
1068
|
+
writeOnboardingSection(normalized);
|
|
1069
|
+
} catch (err) {
|
|
1070
|
+
log.warn({ err }, "Failed to write onboarding section to persona file");
|
|
1079
1071
|
}
|
|
1080
1072
|
|
|
1081
1073
|
void writeRelationshipState().catch((err) => {
|
|
@@ -1104,6 +1096,7 @@ export async function handleSendMessage(
|
|
|
1104
1096
|
bypassSecretCheck?: boolean;
|
|
1105
1097
|
hostHomeDir?: string;
|
|
1106
1098
|
hostUsername?: string;
|
|
1099
|
+
clientTimezone?: unknown;
|
|
1107
1100
|
clientId?: string;
|
|
1108
1101
|
clientMessageId?: string;
|
|
1109
1102
|
inferenceProfile?: string | null;
|
|
@@ -1183,6 +1176,10 @@ export async function handleSendMessage(
|
|
|
1183
1176
|
)}`,
|
|
1184
1177
|
);
|
|
1185
1178
|
}
|
|
1179
|
+
const clientTimezone =
|
|
1180
|
+
typeof body.clientTimezone === "string"
|
|
1181
|
+
? (canonicalizeTimeZone(body.clientTimezone) ?? undefined)
|
|
1182
|
+
: undefined;
|
|
1186
1183
|
|
|
1187
1184
|
// When conversationKey is omitted, derive a stable default from
|
|
1188
1185
|
// sourceChannel + sourceInterface so that repeated calls from the same
|
|
@@ -1302,10 +1299,12 @@ export async function handleSendMessage(
|
|
|
1302
1299
|
interfaceId: sourceInterface,
|
|
1303
1300
|
hostHomeDir: body.hostHomeDir,
|
|
1304
1301
|
hostUsername: body.hostUsername,
|
|
1302
|
+
...(clientTimezone ? { clientTimezone } : {}),
|
|
1305
1303
|
} satisfies HostProxyTransportMetadata)
|
|
1306
1304
|
: ({
|
|
1307
1305
|
channelId: sourceChannel,
|
|
1308
1306
|
interfaceId: sourceInterface,
|
|
1307
|
+
...(clientTimezone ? { clientTimezone } : {}),
|
|
1309
1308
|
} satisfies NonHostProxyTransportMetadata);
|
|
1310
1309
|
|
|
1311
1310
|
const conversation = await smDeps.getOrCreateConversation(
|
|
@@ -1991,6 +1990,13 @@ async function generateLlmSuggestion(
|
|
|
1991
1990
|
// prefill-safe model. Keep `stop_sequences: ["</reply>"]` as an
|
|
1992
1991
|
// early-termination hint; the parser below handles both tagged and
|
|
1993
1992
|
// untagged responses so untagged "casual answer" replies still work.
|
|
1993
|
+
//
|
|
1994
|
+
// Force `thinking: disabled` + `effort: none` so the call works on any
|
|
1995
|
+
// user profile — including thinking-enabled profiles (Opus 4.x at
|
|
1996
|
+
// `effort: high|xhigh`, etc.) where Anthropic 400s on `temperature` ≠ 1
|
|
1997
|
+
// when thinking is enabled or in adaptive mode. A 60-token reply chip
|
|
1998
|
+
// doesn't benefit from extended thinking anyway, and burning thinking
|
|
1999
|
+
// tokens here would be wasteful.
|
|
1994
2000
|
const response = await provider.sendMessage(
|
|
1995
2001
|
[{ role: "user", content: [{ type: "text", text: userPrompt }] }],
|
|
1996
2002
|
[], // no tools
|
|
@@ -2001,6 +2007,8 @@ async function generateLlmSuggestion(
|
|
|
2001
2007
|
max_tokens: 60,
|
|
2002
2008
|
stop_sequences: ["</reply>"],
|
|
2003
2009
|
temperature: 0.7,
|
|
2010
|
+
thinking: { type: "disabled" },
|
|
2011
|
+
effort: "none",
|
|
2004
2012
|
},
|
|
2005
2013
|
},
|
|
2006
2014
|
);
|
|
@@ -2274,6 +2282,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
2274
2282
|
.optional(),
|
|
2275
2283
|
conversationType: z.string().optional(),
|
|
2276
2284
|
slashCommand: z.string().optional(),
|
|
2285
|
+
clientTimezone: z.string().optional(),
|
|
2277
2286
|
inferenceProfile: z.string().nullable().optional(),
|
|
2278
2287
|
riskThreshold: z.enum(VALID_RISK_THRESHOLDS).optional(),
|
|
2279
2288
|
}),
|