@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
|
@@ -13,7 +13,8 @@ mock.module("../../util/logger.js", () => ({
|
|
|
13
13
|
}),
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
|
-
// Stub config loader —
|
|
16
|
+
// Stub config loader — return a config with memory.v2.enabled=false so the
|
|
17
|
+
// v1 paths under test stay active.
|
|
17
18
|
mock.module("../../config/loader.js", () => ({
|
|
18
19
|
loadConfig: () => mockConfig,
|
|
19
20
|
getConfig: () => mockConfig,
|
|
@@ -21,7 +22,7 @@ mock.module("../../config/loader.js", () => ({
|
|
|
21
22
|
}));
|
|
22
23
|
|
|
23
24
|
// ── Controllable mocks for semantic search ─────────────────────────────
|
|
24
|
-
const mockConfig: unknown = {};
|
|
25
|
+
const mockConfig: unknown = { memory: { v2: { enabled: false } } };
|
|
25
26
|
|
|
26
27
|
let mockBackendStatus: {
|
|
27
28
|
enabled: boolean;
|
|
@@ -68,11 +69,7 @@ import { eq } from "drizzle-orm";
|
|
|
68
69
|
import { getDb } from "../../memory/db-connection.js";
|
|
69
70
|
import { initializeDb } from "../../memory/db-init.js";
|
|
70
71
|
import { memoryGraphNodes, memoryJobs } from "../../memory/schema.js";
|
|
71
|
-
import {
|
|
72
|
-
BadRequestError,
|
|
73
|
-
ConflictError,
|
|
74
|
-
NotFoundError,
|
|
75
|
-
} from "./errors.js";
|
|
72
|
+
import { BadRequestError, ConflictError, NotFoundError } from "./errors.js";
|
|
76
73
|
import { ROUTES } from "./memory-item-routes.js";
|
|
77
74
|
import type { RouteDefinition } from "./types.js";
|
|
78
75
|
|
|
@@ -269,7 +266,9 @@ describe("Memory Item Routes", () => {
|
|
|
269
266
|
content: "s2\nst2",
|
|
270
267
|
});
|
|
271
268
|
|
|
272
|
-
const res = await callHandler(route, {
|
|
269
|
+
const res = await callHandler(route, {
|
|
270
|
+
queryParams: { kind: "semantic" },
|
|
271
|
+
});
|
|
273
272
|
const body = (await res.json()) as {
|
|
274
273
|
items: Array<{ id: string }>;
|
|
275
274
|
total: number;
|
|
@@ -319,7 +318,9 @@ describe("Memory Item Routes", () => {
|
|
|
319
318
|
lastAccessed: 3000,
|
|
320
319
|
});
|
|
321
320
|
|
|
322
|
-
const res = await callHandler(route, {
|
|
321
|
+
const res = await callHandler(route, {
|
|
322
|
+
queryParams: { limit: "1", offset: "1" },
|
|
323
|
+
});
|
|
323
324
|
const body = (await res.json()) as {
|
|
324
325
|
items: Array<{ id: string }>;
|
|
325
326
|
total: number;
|
|
@@ -344,7 +345,9 @@ describe("Memory Item Routes", () => {
|
|
|
344
345
|
created: 1000,
|
|
345
346
|
});
|
|
346
347
|
|
|
347
|
-
const res = await callHandler(route, {
|
|
348
|
+
const res = await callHandler(route, {
|
|
349
|
+
queryParams: { sort: "firstSeenAt", order: "asc" },
|
|
350
|
+
});
|
|
348
351
|
const body = (await res.json()) as {
|
|
349
352
|
items: Array<{ id: string }>;
|
|
350
353
|
};
|
|
@@ -366,7 +369,9 @@ describe("Memory Item Routes", () => {
|
|
|
366
369
|
significance: 0.9,
|
|
367
370
|
});
|
|
368
371
|
|
|
369
|
-
const res = await callHandler(route, {
|
|
372
|
+
const res = await callHandler(route, {
|
|
373
|
+
queryParams: { sort: "importance", order: "desc" },
|
|
374
|
+
});
|
|
370
375
|
const body = (await res.json()) as {
|
|
371
376
|
items: Array<{ id: string }>;
|
|
372
377
|
};
|
|
@@ -418,7 +423,9 @@ describe("Memory Item Routes", () => {
|
|
|
418
423
|
},
|
|
419
424
|
];
|
|
420
425
|
|
|
421
|
-
const res = await callHandler(route, {
|
|
426
|
+
const res = await callHandler(route, {
|
|
427
|
+
queryParams: { search: "alice" },
|
|
428
|
+
});
|
|
422
429
|
const body = (await res.json()) as {
|
|
423
430
|
items: Array<{ id: string }>;
|
|
424
431
|
total: number;
|
|
@@ -503,7 +510,9 @@ describe("Memory Item Routes", () => {
|
|
|
503
510
|
];
|
|
504
511
|
|
|
505
512
|
// Request page 2 (offset=1, limit=1)
|
|
506
|
-
const res = await callHandler(route, {
|
|
513
|
+
const res = await callHandler(route, {
|
|
514
|
+
queryParams: { search: "item", limit: "1", offset: "1" },
|
|
515
|
+
});
|
|
507
516
|
const body = (await res.json()) as {
|
|
508
517
|
items: Array<{ id: string }>;
|
|
509
518
|
total: number;
|
|
@@ -563,7 +572,10 @@ describe("Memory Item Routes", () => {
|
|
|
563
572
|
content: "dark mode\nPrefers dark mode",
|
|
564
573
|
});
|
|
565
574
|
|
|
566
|
-
const res = await callHandler(route, {
|
|
575
|
+
const res = await callHandler(route, {
|
|
576
|
+
queryParams: {},
|
|
577
|
+
pathParams: { id: "i1" },
|
|
578
|
+
});
|
|
567
579
|
expect(res.status).toBe(200);
|
|
568
580
|
const body = (await res.json()) as {
|
|
569
581
|
item: { id: string; subject: string };
|
|
@@ -573,7 +585,10 @@ describe("Memory Item Routes", () => {
|
|
|
573
585
|
});
|
|
574
586
|
|
|
575
587
|
test("returns 404 for non-existent item", async () => {
|
|
576
|
-
const res = await callHandler(route, {
|
|
588
|
+
const res = await callHandler(route, {
|
|
589
|
+
queryParams: {},
|
|
590
|
+
pathParams: { id: "nonexistent" },
|
|
591
|
+
});
|
|
577
592
|
expect(res.status).toBe(404);
|
|
578
593
|
});
|
|
579
594
|
|
|
@@ -584,7 +599,10 @@ describe("Memory Item Routes", () => {
|
|
|
584
599
|
content: "some content\nsome statement",
|
|
585
600
|
});
|
|
586
601
|
|
|
587
|
-
const res = await callHandler(route, {
|
|
602
|
+
const res = await callHandler(route, {
|
|
603
|
+
queryParams: {},
|
|
604
|
+
pathParams: { id: "i1" },
|
|
605
|
+
});
|
|
588
606
|
const body = (await res.json()) as {
|
|
589
607
|
item: { supersedes: unknown; supersededBy: unknown };
|
|
590
608
|
};
|
|
@@ -862,7 +880,9 @@ describe("Memory Item Routes", () => {
|
|
|
862
880
|
});
|
|
863
881
|
|
|
864
882
|
test("returns 404 for non-existent item", async () => {
|
|
865
|
-
const res = await callHandler(route, {
|
|
883
|
+
const res = await callHandler(route, {
|
|
884
|
+
pathParams: { id: "nonexistent" },
|
|
885
|
+
});
|
|
866
886
|
expect(res.status).toBe(404);
|
|
867
887
|
});
|
|
868
888
|
|
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
import { z } from "zod";
|
|
26
26
|
|
|
27
27
|
import { getConfig } from "../../config/loader.js";
|
|
28
|
-
import { isMemoryV2ReadActive } from "../../memory/context-search/sources/memory-v2.js";
|
|
29
28
|
import { getDb } from "../../memory/db-connection.js";
|
|
30
29
|
import {
|
|
31
30
|
embedWithBackend,
|
|
@@ -176,11 +175,11 @@ async function searchNodesSemantic(
|
|
|
176
175
|
): Promise<{ ids: string[]; total: number } | null> {
|
|
177
176
|
try {
|
|
178
177
|
const config = getConfig();
|
|
179
|
-
// v2 owns the read path when
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
if (
|
|
178
|
+
// v2 owns the read path when enabled. Fall back to SQL search (the
|
|
179
|
+
// caller's `null` branch) instead of querying the v1 collection, which
|
|
180
|
+
// is in active retirement and a corrupted sparse segment can OOM-crash
|
|
181
|
+
// the shared Qdrant process.
|
|
182
|
+
if (config.memory.v2.enabled) return null;
|
|
184
183
|
const backendStatus = await getMemoryBackendStatus(config);
|
|
185
184
|
if (!backendStatus.provider) return null;
|
|
186
185
|
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
* Migrated from `ipc/routes/memory-v2-backfill.ts` and
|
|
5
5
|
* `ipc/routes/memory-v2-validate.ts` into the shared ROUTES array.
|
|
6
6
|
*/
|
|
7
|
+
import { stat } from "node:fs/promises";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
|
|
7
10
|
import { z } from "zod";
|
|
8
11
|
|
|
9
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
10
12
|
import { loadConfig } from "../../config/loader.js";
|
|
11
13
|
import {
|
|
12
14
|
applyCorrectionIfCalibrated,
|
|
@@ -22,12 +24,17 @@ import {
|
|
|
22
24
|
enqueueMemoryJob,
|
|
23
25
|
type MemoryJobType,
|
|
24
26
|
} from "../../memory/jobs-store.js";
|
|
27
|
+
import {
|
|
28
|
+
type ConceptFrequencyResponse,
|
|
29
|
+
getConceptFrequencySummary,
|
|
30
|
+
} from "../../memory/memory-v2-concept-frequency.js";
|
|
25
31
|
import {
|
|
26
32
|
getEdgeIndex,
|
|
27
33
|
totalEdgeCount,
|
|
28
34
|
validateEdgeTargets,
|
|
29
35
|
} from "../../memory/v2/edge-index.js";
|
|
30
36
|
import {
|
|
37
|
+
getConceptsDir,
|
|
31
38
|
listPages,
|
|
32
39
|
readPage,
|
|
33
40
|
renderPageContent,
|
|
@@ -43,11 +50,37 @@ import {
|
|
|
43
50
|
getConceptPageCorpusStats,
|
|
44
51
|
rebuildConceptPageCorpusStats,
|
|
45
52
|
} from "../../memory/v2/sparse-bm25.js";
|
|
53
|
+
import { getLogger } from "../../util/logger.js";
|
|
46
54
|
import { getWorkspaceDir } from "../../util/platform.js";
|
|
47
55
|
import { RouteError } from "./errors.js";
|
|
48
56
|
import type { RouteDefinition } from "./types.js";
|
|
49
57
|
import type { RouteHandlerArgs } from "./types.js";
|
|
50
58
|
|
|
59
|
+
const log = getLogger("memory-v2-routes");
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Wire-format error code emitted when v2 routes reject a request because
|
|
63
|
+
* `memory.v2.enabled` is false. Exported so tests and the macOS client can
|
|
64
|
+
* reference the same string without drift.
|
|
65
|
+
*/
|
|
66
|
+
export const MEMORY_V2_DISABLED_CODE = "MEMORY_V2_DISABLED";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Reject the request when memory v2 is not active. Returning 409 (rather
|
|
70
|
+
* than serving a partial response) keeps clients honest — the desktop
|
|
71
|
+
* Memories panel reads this code to render an explicit "disabled in
|
|
72
|
+
* config" empty state.
|
|
73
|
+
*/
|
|
74
|
+
function requireMemoryV2Enabled(): void {
|
|
75
|
+
if (!loadConfig().memory.v2.enabled) {
|
|
76
|
+
throw new RouteError(
|
|
77
|
+
"Memory v2 is not enabled — set memory.v2.enabled to true to use this command.",
|
|
78
|
+
MEMORY_V2_DISABLED_CODE,
|
|
79
|
+
409,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
51
84
|
// ── Backfill ────────────────────────────────────────────────────────────
|
|
52
85
|
|
|
53
86
|
const MemoryV2BackfillParams = z
|
|
@@ -72,6 +105,7 @@ const OP_TO_JOB_TYPE: Record<MemoryV2BackfillOp, MemoryJobType> = {
|
|
|
72
105
|
async function handleBackfill({
|
|
73
106
|
body = {},
|
|
74
107
|
}: RouteHandlerArgs): Promise<MemoryV2BackfillResult> {
|
|
108
|
+
requireMemoryV2Enabled();
|
|
75
109
|
const { op, force } = MemoryV2BackfillParams.parse(body);
|
|
76
110
|
const payload: Record<string, unknown> =
|
|
77
111
|
op === "migrate" && force === true ? { force: true } : {};
|
|
@@ -98,6 +132,7 @@ export type MemoryV2ValidateResult = {
|
|
|
98
132
|
async function handleValidate({
|
|
99
133
|
body = {},
|
|
100
134
|
}: RouteHandlerArgs): Promise<MemoryV2ValidateResult> {
|
|
135
|
+
requireMemoryV2Enabled();
|
|
101
136
|
MemoryV2ValidateParams.parse(body);
|
|
102
137
|
|
|
103
138
|
const workspaceDir = getWorkspaceDir();
|
|
@@ -154,6 +189,7 @@ export type MemoryV2GetConceptPageResult = {
|
|
|
154
189
|
async function handleGetConceptPage({
|
|
155
190
|
body = {},
|
|
156
191
|
}: RouteHandlerArgs): Promise<MemoryV2GetConceptPageResult> {
|
|
192
|
+
requireMemoryV2Enabled();
|
|
157
193
|
const { slug } = MemoryV2GetConceptPageParams.parse(body);
|
|
158
194
|
const workspaceDir = getWorkspaceDir();
|
|
159
195
|
let page;
|
|
@@ -176,6 +212,59 @@ async function handleGetConceptPage({
|
|
|
176
212
|
return { slug, rendered: renderPageContent(page) };
|
|
177
213
|
}
|
|
178
214
|
|
|
215
|
+
// ── List concept pages ──────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
const MemoryV2ListConceptPagesParams = z.object({}).strict();
|
|
218
|
+
|
|
219
|
+
export type MemoryV2ListConceptPagesResult = {
|
|
220
|
+
pages: Array<{
|
|
221
|
+
slug: string;
|
|
222
|
+
bodyBytes: number;
|
|
223
|
+
edgeCount: number;
|
|
224
|
+
updatedAtMs: number;
|
|
225
|
+
}>;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
async function handleListConceptPages({
|
|
229
|
+
body = {},
|
|
230
|
+
}: RouteHandlerArgs): Promise<MemoryV2ListConceptPagesResult> {
|
|
231
|
+
requireMemoryV2Enabled();
|
|
232
|
+
MemoryV2ListConceptPagesParams.parse(body);
|
|
233
|
+
|
|
234
|
+
const workspaceDir = getWorkspaceDir();
|
|
235
|
+
const conceptsDir = getConceptsDir(workspaceDir);
|
|
236
|
+
const slugs = await listPages(workspaceDir);
|
|
237
|
+
|
|
238
|
+
const settled = await Promise.all(
|
|
239
|
+
slugs.map(async (slug) => {
|
|
240
|
+
try {
|
|
241
|
+
const page = await readPage(workspaceDir, slug);
|
|
242
|
+
if (!page) return null;
|
|
243
|
+
const stats = await stat(join(conceptsDir, `${slug}.md`));
|
|
244
|
+
return {
|
|
245
|
+
slug,
|
|
246
|
+
bodyBytes: Buffer.byteLength(page.body, "utf8"),
|
|
247
|
+
edgeCount: page.frontmatter.edges.length,
|
|
248
|
+
updatedAtMs: Math.floor(stats.mtimeMs),
|
|
249
|
+
};
|
|
250
|
+
} catch (err) {
|
|
251
|
+
// A single corrupt page (bad YAML, schema mismatch, etc.) shouldn't
|
|
252
|
+
// poison the whole listing — the validate route is the place to
|
|
253
|
+
// surface those; this one is read-only and best-effort.
|
|
254
|
+
log.warn(
|
|
255
|
+
`Skipping concept page '${slug}' in list-concept-pages: ${err instanceof Error ? err.message : String(err)}`,
|
|
256
|
+
);
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
}),
|
|
260
|
+
);
|
|
261
|
+
const pages = settled.filter(
|
|
262
|
+
(p): p is MemoryV2ListConceptPagesResult["pages"][number] => p !== null,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
return { pages };
|
|
266
|
+
}
|
|
267
|
+
|
|
179
268
|
// ── Rebuild BM25 corpus stats ───────────────────────────────────────────
|
|
180
269
|
|
|
181
270
|
const MemoryV2RebuildCorpusStatsParams = z.object({}).strict();
|
|
@@ -190,6 +279,7 @@ export interface MemoryV2RebuildCorpusStatsResult {
|
|
|
190
279
|
async function handleRebuildCorpusStats({
|
|
191
280
|
body = {},
|
|
192
281
|
}: RouteHandlerArgs): Promise<MemoryV2RebuildCorpusStatsResult> {
|
|
282
|
+
requireMemoryV2Enabled();
|
|
193
283
|
MemoryV2RebuildCorpusStatsParams.parse(body);
|
|
194
284
|
const workspaceDir = getWorkspaceDir();
|
|
195
285
|
await rebuildConceptPageCorpusStats(workspaceDir);
|
|
@@ -221,23 +311,9 @@ export type MemoryV2ReembedSkillsResult = {
|
|
|
221
311
|
async function handleReembedSkills({
|
|
222
312
|
body = {},
|
|
223
313
|
}: RouteHandlerArgs): Promise<MemoryV2ReembedSkillsResult> {
|
|
314
|
+
requireMemoryV2Enabled();
|
|
224
315
|
MemoryV2ReembedSkillsParams.parse(body);
|
|
225
316
|
|
|
226
|
-
// Gate the route on both the feature flag and the per-workspace config
|
|
227
|
-
// toggle so the v2 skill collection never gets re-seeded against a
|
|
228
|
-
// workspace whose v2 subsystem is intentionally off.
|
|
229
|
-
const config = loadConfig();
|
|
230
|
-
if (
|
|
231
|
-
!isAssistantFeatureFlagEnabled("memory-v2-enabled", config) ||
|
|
232
|
-
!config.memory.v2.enabled
|
|
233
|
-
) {
|
|
234
|
-
throw new RouteError(
|
|
235
|
-
"Memory v2 is not enabled — flip both the memory-v2-enabled feature flag and memory.v2.enabled to use this command.",
|
|
236
|
-
"MEMORY_V2_DISABLED",
|
|
237
|
-
409,
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
317
|
// Unlike the queued backfill jobs above, this is a CLI-driven sync
|
|
242
318
|
// request: the operator wants the cache replaced before the next prompt
|
|
243
319
|
// assembly, so we await the seed inline rather than enqueueing it.
|
|
@@ -412,6 +488,7 @@ async function scoreChannel(
|
|
|
412
488
|
async function handleExplainSimilarity({
|
|
413
489
|
body = {},
|
|
414
490
|
}: RouteHandlerArgs): Promise<MemoryV2ExplainSimilarityResult> {
|
|
491
|
+
requireMemoryV2Enabled();
|
|
415
492
|
const params = MemoryV2ExplainSimilarityParams.parse(body);
|
|
416
493
|
const config = loadConfig();
|
|
417
494
|
const { dense_weight: denseWeight, sparse_weight: sparseWeight } =
|
|
@@ -459,6 +536,25 @@ async function handleExplainSimilarity({
|
|
|
459
536
|
};
|
|
460
537
|
}
|
|
461
538
|
|
|
539
|
+
// ── Concept injection frequency (debug-only) ────────────────────────────
|
|
540
|
+
|
|
541
|
+
const MemoryV2ConceptFrequencyParams = z
|
|
542
|
+
.object({
|
|
543
|
+
conversationId: z.string().min(1).optional(),
|
|
544
|
+
sinceMs: z.number().int().nonnegative().optional(),
|
|
545
|
+
})
|
|
546
|
+
.strict();
|
|
547
|
+
|
|
548
|
+
async function handleConceptFrequency({
|
|
549
|
+
body = {},
|
|
550
|
+
}: RouteHandlerArgs): Promise<ConceptFrequencyResponse> {
|
|
551
|
+
requireMemoryV2Enabled();
|
|
552
|
+
const { conversationId, sinceMs } =
|
|
553
|
+
MemoryV2ConceptFrequencyParams.parse(body);
|
|
554
|
+
const workspaceDir = getWorkspaceDir();
|
|
555
|
+
return getConceptFrequencySummary(workspaceDir, { conversationId, sinceMs });
|
|
556
|
+
}
|
|
557
|
+
|
|
462
558
|
// ── Fit anisotropy calibration ──────────────────────────────────────────
|
|
463
559
|
|
|
464
560
|
const MemoryV2FitAnisotropyParams = z
|
|
@@ -495,6 +591,7 @@ export interface MemoryV2FitAnisotropyResult {
|
|
|
495
591
|
async function handleFitAnisotropy({
|
|
496
592
|
body = {},
|
|
497
593
|
}: RouteHandlerArgs): Promise<MemoryV2FitAnisotropyResult> {
|
|
594
|
+
requireMemoryV2Enabled();
|
|
498
595
|
const { k, sample } = MemoryV2FitAnisotropyParams.parse(body);
|
|
499
596
|
const config = loadConfig();
|
|
500
597
|
|
|
@@ -581,6 +678,17 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
581
678
|
tags: ["memory"],
|
|
582
679
|
requestBody: MemoryV2GetConceptPageParams,
|
|
583
680
|
},
|
|
681
|
+
{
|
|
682
|
+
operationId: "memory_v2_list_concept_pages",
|
|
683
|
+
method: "POST",
|
|
684
|
+
endpoint: "memory/v2/list-concept-pages",
|
|
685
|
+
handler: handleListConceptPages,
|
|
686
|
+
summary: "List all memory v2 concept pages with metadata",
|
|
687
|
+
description:
|
|
688
|
+
"Returns slugs, body sizes, edge counts, and last-modified timestamps for every concept page on disk. Read-only; used by the desktop About → Memories surface to render a browse-able list.",
|
|
689
|
+
tags: ["memory"],
|
|
690
|
+
requestBody: MemoryV2ListConceptPagesParams,
|
|
691
|
+
},
|
|
584
692
|
{
|
|
585
693
|
operationId: "memory_v2_reembed_skills",
|
|
586
694
|
method: "POST",
|
|
@@ -588,7 +696,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
588
696
|
handler: handleReembedSkills,
|
|
589
697
|
summary: "Re-seed v2 skill entries from the current skill catalog",
|
|
590
698
|
description:
|
|
591
|
-
"Synchronously re-runs seedV2SkillEntries against the current skill catalog. Gated on
|
|
699
|
+
"Synchronously re-runs seedV2SkillEntries against the current skill catalog. Gated on config.memory.v2.enabled.",
|
|
592
700
|
tags: ["memory"],
|
|
593
701
|
requestBody: MemoryV2ReembedSkillsParams,
|
|
594
702
|
},
|
|
@@ -614,6 +722,17 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
614
722
|
tags: ["memory"],
|
|
615
723
|
requestBody: MemoryV2RebuildCorpusStatsParams,
|
|
616
724
|
},
|
|
725
|
+
{
|
|
726
|
+
operationId: "memory_v2_concept_frequency",
|
|
727
|
+
method: "POST",
|
|
728
|
+
endpoint: "memory/v2/concept-frequency",
|
|
729
|
+
handler: handleConceptFrequency,
|
|
730
|
+
summary: "Aggregate per-concept injection frequency from activation logs",
|
|
731
|
+
description:
|
|
732
|
+
"Debug-only. Aggregates the existing memory_v2_activation_logs table by (slug, status) and cross-references on-disk concept pages so an operator can see which concepts get injected often, which get scored but rejected, and which on-disk pages never even surface as candidates. Optional filters: conversationId narrows to a single conversation; sinceMs restricts to logs created at-or-after the given epoch ms timestamp.",
|
|
733
|
+
tags: ["memory"],
|
|
734
|
+
requestBody: MemoryV2ConceptFrequencyParams,
|
|
735
|
+
},
|
|
617
736
|
{
|
|
618
737
|
operationId: "memory_v2_fit_anisotropy",
|
|
619
738
|
method: "POST",
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal routes for daemon-owned OAuth connect flows (CLI gateway transport fix).
|
|
3
|
+
*
|
|
4
|
+
* POST internal/oauth/connect/start — starts the flow in the daemon, returns auth URL
|
|
5
|
+
* GET internal/oauth/connect/status/:state — polls current flow status
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
import { orchestrateOAuthConnect } from "../../oauth/connect-orchestrator.js";
|
|
11
|
+
import {
|
|
12
|
+
getOAuthConnectState,
|
|
13
|
+
setOAuthConnectComplete,
|
|
14
|
+
setOAuthConnectError,
|
|
15
|
+
setOAuthConnectPending,
|
|
16
|
+
} from "../../oauth/oauth-connect-state.js";
|
|
17
|
+
import { getLogger } from "../../util/logger.js";
|
|
18
|
+
import { BadRequestError, InternalError, NotFoundError } from "./errors.js";
|
|
19
|
+
import type { RouteDefinition } from "./types.js";
|
|
20
|
+
|
|
21
|
+
const log = getLogger("oauth-connect-routes");
|
|
22
|
+
|
|
23
|
+
async function handleOAuthConnectStart({
|
|
24
|
+
body,
|
|
25
|
+
}: {
|
|
26
|
+
body?: Record<string, unknown>;
|
|
27
|
+
}): Promise<{ auth_url: string; state: string }> {
|
|
28
|
+
const {
|
|
29
|
+
service,
|
|
30
|
+
clientId,
|
|
31
|
+
clientSecret,
|
|
32
|
+
callbackTransport,
|
|
33
|
+
requestedScopes,
|
|
34
|
+
} = (body ?? {}) as {
|
|
35
|
+
service: string;
|
|
36
|
+
clientId: string;
|
|
37
|
+
clientSecret?: string;
|
|
38
|
+
callbackTransport?: string;
|
|
39
|
+
requestedScopes?: string[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (!service) throw new BadRequestError("service is required");
|
|
43
|
+
if (!clientId) throw new BadRequestError("clientId is required");
|
|
44
|
+
if (callbackTransport !== "loopback" && callbackTransport !== "gateway") {
|
|
45
|
+
throw new BadRequestError(
|
|
46
|
+
'callbackTransport must be "loopback" or "gateway"',
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Capture resolvedState separately so the onDeferredComplete closure can
|
|
51
|
+
// reference it without a reference-before-assignment risk (the fire-and-forget
|
|
52
|
+
// tail only fires after the await resolves, by which point resolvedState is set).
|
|
53
|
+
// eslint-disable-next-line prefer-const -- intentional forward-declared binding
|
|
54
|
+
let resolvedState: string | undefined;
|
|
55
|
+
|
|
56
|
+
let result: Awaited<ReturnType<typeof orchestrateOAuthConnect>>;
|
|
57
|
+
try {
|
|
58
|
+
result = await orchestrateOAuthConnect({
|
|
59
|
+
service,
|
|
60
|
+
clientId,
|
|
61
|
+
clientSecret,
|
|
62
|
+
callbackTransport,
|
|
63
|
+
...(requestedScopes ? { requestedScopes } : {}),
|
|
64
|
+
isInteractive: false,
|
|
65
|
+
onDeferredComplete: (r) => {
|
|
66
|
+
if (!resolvedState) return;
|
|
67
|
+
if (r.success) {
|
|
68
|
+
setOAuthConnectComplete(resolvedState, r.service, r.accountInfo, r.grantedScopes);
|
|
69
|
+
} else {
|
|
70
|
+
setOAuthConnectError(resolvedState, r.service, r.error ?? "OAuth connect failed");
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
throw new InternalError(err instanceof Error ? err.message : String(err));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!result.success) {
|
|
79
|
+
throw new InternalError(result.error);
|
|
80
|
+
}
|
|
81
|
+
if (!result.deferred) {
|
|
82
|
+
throw new InternalError("Orchestrator returned non-deferred result");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
resolvedState = result.state;
|
|
86
|
+
setOAuthConnectPending(result.state, service);
|
|
87
|
+
log.info({ state: result.state, service }, "oauth connect flow started");
|
|
88
|
+
return { auth_url: result.authorizeUrl, state: result.state };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function handleOAuthConnectStatus({
|
|
92
|
+
pathParams,
|
|
93
|
+
}: {
|
|
94
|
+
pathParams?: Record<string, string>;
|
|
95
|
+
}): {
|
|
96
|
+
status: "pending" | "complete" | "error";
|
|
97
|
+
service: string;
|
|
98
|
+
account_info?: string;
|
|
99
|
+
granted_scopes?: string[];
|
|
100
|
+
error?: string;
|
|
101
|
+
} {
|
|
102
|
+
const { state } = pathParams as { state: string };
|
|
103
|
+
const flowState = getOAuthConnectState(state);
|
|
104
|
+
|
|
105
|
+
if (flowState === null) {
|
|
106
|
+
throw new NotFoundError(`No active OAuth connect flow for state "${state}"`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (flowState.status === "pending") return { status: "pending", service: flowState.service };
|
|
110
|
+
if (flowState.status === "complete") {
|
|
111
|
+
return {
|
|
112
|
+
status: "complete",
|
|
113
|
+
service: flowState.service,
|
|
114
|
+
...(flowState.accountInfo ? { account_info: flowState.accountInfo } : {}),
|
|
115
|
+
...(flowState.grantedScopes ? { granted_scopes: flowState.grantedScopes } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return { status: "error", service: flowState.service, error: flowState.error };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const ROUTES: RouteDefinition[] = [
|
|
122
|
+
{
|
|
123
|
+
operationId: "internal_oauth_connect_start",
|
|
124
|
+
endpoint: "internal/oauth/connect/start",
|
|
125
|
+
method: "POST",
|
|
126
|
+
summary: "Start daemon-owned OAuth connect flow",
|
|
127
|
+
description:
|
|
128
|
+
"Starts an OAuth connect flow in the daemon and returns the authorization URL for the CLI to open in the browser.",
|
|
129
|
+
tags: ["internal"],
|
|
130
|
+
requestBody: z.object({
|
|
131
|
+
service: z.string(),
|
|
132
|
+
clientId: z.string(),
|
|
133
|
+
clientSecret: z.string().optional(),
|
|
134
|
+
callbackTransport: z.enum(["loopback", "gateway"]),
|
|
135
|
+
requestedScopes: z.array(z.string()).optional(),
|
|
136
|
+
}),
|
|
137
|
+
handler: handleOAuthConnectStart,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
operationId: "internal_oauth_connect_status",
|
|
141
|
+
endpoint: "internal/oauth/connect/status/:state",
|
|
142
|
+
method: "GET",
|
|
143
|
+
summary: "Poll daemon OAuth connect flow status",
|
|
144
|
+
description:
|
|
145
|
+
"Returns the current status of an in-flight daemon-owned OAuth connect flow (pending/complete/error).",
|
|
146
|
+
tags: ["internal"],
|
|
147
|
+
pathParams: [{ name: "state" }],
|
|
148
|
+
additionalResponses: {
|
|
149
|
+
"404": { description: "No active OAuth connect flow for the given state token" },
|
|
150
|
+
},
|
|
151
|
+
handler: handleOAuthConnectStatus,
|
|
152
|
+
},
|
|
153
|
+
];
|
|
@@ -79,7 +79,7 @@ export function normalizeTelegramDestination(destination: string): string {
|
|
|
79
79
|
// Input / output types
|
|
80
80
|
// ---------------------------------------------------------------------------
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
interface StartOutboundParams {
|
|
83
83
|
channel: ChannelId;
|
|
84
84
|
destination?: string;
|
|
85
85
|
rebind?: boolean;
|
|
@@ -87,13 +87,13 @@ export interface StartOutboundParams {
|
|
|
87
87
|
originConversationId?: string;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
interface ResendOutboundParams {
|
|
91
91
|
channel: ChannelId;
|
|
92
92
|
/** Origin conversation ID so completion/failure pointers can route back on resend. */
|
|
93
93
|
originConversationId?: string;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
interface CancelOutboundParams {
|
|
97
97
|
channel: ChannelId;
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -102,7 +102,7 @@ export interface CancelOutboundParams {
|
|
|
102
102
|
* Maps 1:1 with the fields in ChannelVerificationSessionResponse minus the
|
|
103
103
|
* `type` discriminant.
|
|
104
104
|
*/
|
|
105
|
-
|
|
105
|
+
interface OutboundActionResult {
|
|
106
106
|
success: boolean;
|
|
107
107
|
error?: string;
|
|
108
108
|
message?: string;
|
|
@@ -38,21 +38,53 @@ export async function runScript(
|
|
|
38
38
|
env: buildSanitizedEnv(),
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
//
|
|
41
|
+
// Start consuming streams immediately so buffered output is available even on timeout.
|
|
42
|
+
// When the process is killed the pipe fds close and these promises resolve on their own.
|
|
43
|
+
const stdoutPromise = new Response(proc.stdout).text();
|
|
44
|
+
const stderrPromise = new Response(proc.stderr).text();
|
|
45
|
+
|
|
46
|
+
let timedOut = false;
|
|
47
|
+
|
|
42
48
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
43
49
|
const timer = setTimeout(() => {
|
|
50
|
+
timedOut = true;
|
|
44
51
|
proc.kill("SIGKILL");
|
|
45
52
|
reject(new Error(`Script timed out after ${timeoutMs}ms`));
|
|
46
53
|
}, timeoutMs);
|
|
47
54
|
timer.unref();
|
|
48
|
-
// Clean up timer if process finishes first
|
|
49
55
|
proc.exited.then(() => clearTimeout(timer));
|
|
50
56
|
});
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
/** How long to wait for pipes to drain after SIGKILL before giving up. */
|
|
59
|
+
const DRAIN_TIMEOUT_MS = 5_000;
|
|
60
|
+
|
|
61
|
+
let exitCode: number;
|
|
62
|
+
try {
|
|
63
|
+
exitCode = await Promise.race([proc.exited, timeoutPromise]);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (!timedOut) throw err;
|
|
66
|
+
// Collect whatever the process wrote before it was killed.
|
|
67
|
+
// Race each stream against a short drain window — if a background child
|
|
68
|
+
// process inherited the pipe fd, the stream would otherwise never reach
|
|
69
|
+
// EOF and block the scheduler tick indefinitely.
|
|
70
|
+
const empty = (ms: number): Promise<string> =>
|
|
71
|
+
new Promise((resolve) => setTimeout(() => resolve(""), ms));
|
|
72
|
+
const [stdoutStr, stderrStr] = await Promise.all([
|
|
73
|
+
Promise.race([stdoutPromise, empty(DRAIN_TIMEOUT_MS)]),
|
|
74
|
+
Promise.race([stderrPromise, empty(DRAIN_TIMEOUT_MS)]),
|
|
75
|
+
]);
|
|
76
|
+
const stdout = truncate(stdoutStr);
|
|
77
|
+
const timeoutMsg = `Script timed out after ${timeoutMs}ms`;
|
|
78
|
+
const stderr = truncate(stderrStr ? `${timeoutMsg}\n${stderrStr}` : timeoutMsg);
|
|
79
|
+
log.info(
|
|
80
|
+
{ command, timedOut: true, stdoutLen: stdout.length },
|
|
81
|
+
"Script timed out",
|
|
82
|
+
);
|
|
83
|
+
return { exitCode: 124, stdout, stderr };
|
|
84
|
+
}
|
|
53
85
|
|
|
54
|
-
const stdout = truncate(await
|
|
55
|
-
const stderr = truncate(await
|
|
86
|
+
const stdout = truncate(await stdoutPromise);
|
|
87
|
+
const stderr = truncate(await stderrPromise);
|
|
56
88
|
|
|
57
89
|
log.info(
|
|
58
90
|
{ command, exitCode, stdoutLen: stdout.length, stderrLen: stderr.length },
|