@vellumai/assistant 0.8.2 → 0.8.4
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 +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retryable and dead-lettered events, and replaying dead letters.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { and, eq, lte } from "drizzle-orm";
|
|
9
|
+
import { and, eq, lte, or } from "drizzle-orm";
|
|
10
10
|
|
|
11
11
|
import { getDb } from "./db-connection.js";
|
|
12
12
|
import {
|
|
@@ -44,6 +44,7 @@ export function acknowledgeDelivery(
|
|
|
44
44
|
db.update(channelInboundEvents)
|
|
45
45
|
.set({
|
|
46
46
|
deliveryStatus: "delivered",
|
|
47
|
+
retryAfter: null,
|
|
47
48
|
updatedAt: now,
|
|
48
49
|
})
|
|
49
50
|
.where(eq(channelInboundEvents.id, existing.id))
|
|
@@ -61,6 +62,23 @@ export function markProcessed(eventId: string): void {
|
|
|
61
62
|
.run();
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/** Mark an event's outbound callback delivery as complete. */
|
|
66
|
+
export function markDeliveryDelivered(eventId: string): void {
|
|
67
|
+
const db = getDb();
|
|
68
|
+
db.update(channelInboundEvents)
|
|
69
|
+
.set({
|
|
70
|
+
deliveryStatus: "delivered",
|
|
71
|
+
retryAfter: null,
|
|
72
|
+
updatedAt: Date.now(),
|
|
73
|
+
})
|
|
74
|
+
.where(eq(channelInboundEvents.id, eventId))
|
|
75
|
+
.run();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function errorMessage(err: unknown): string {
|
|
79
|
+
return err instanceof Error ? err.message : String(err);
|
|
80
|
+
}
|
|
81
|
+
|
|
64
82
|
/**
|
|
65
83
|
* Record a processing failure. Classifies the error to decide whether
|
|
66
84
|
* the event should be retried (status='failed') or dead-lettered
|
|
@@ -79,7 +97,7 @@ export function recordProcessingFailure(eventId: string, err: unknown): void {
|
|
|
79
97
|
|
|
80
98
|
const attempts = (row?.attempts ?? 0) + 1;
|
|
81
99
|
const category = classifyError(err);
|
|
82
|
-
const errorMsg =
|
|
100
|
+
const errorMsg = errorMessage(err);
|
|
83
101
|
|
|
84
102
|
if (category === "fatal" || attempts >= RETRY_MAX_ATTEMPTS) {
|
|
85
103
|
db.update(channelInboundEvents)
|
|
@@ -107,6 +125,51 @@ export function recordProcessingFailure(eventId: string, err: unknown): void {
|
|
|
107
125
|
}
|
|
108
126
|
}
|
|
109
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Record an outbound callback delivery failure without changing the processing
|
|
130
|
+
* status. Delivery uses its own retry budget so a turn that needed processing
|
|
131
|
+
* retries still gets a full delivery retry window.
|
|
132
|
+
*/
|
|
133
|
+
export function recordDeliveryFailure(eventId: string, err: unknown): void {
|
|
134
|
+
const db = getDb();
|
|
135
|
+
const now = Date.now();
|
|
136
|
+
|
|
137
|
+
const row = db
|
|
138
|
+
.select({ attempts: channelInboundEvents.deliveryAttempts })
|
|
139
|
+
.from(channelInboundEvents)
|
|
140
|
+
.where(eq(channelInboundEvents.id, eventId))
|
|
141
|
+
.get();
|
|
142
|
+
|
|
143
|
+
const attempts = (row?.attempts ?? 0) + 1;
|
|
144
|
+
const category = classifyError(err);
|
|
145
|
+
const errorMsg = errorMessage(err);
|
|
146
|
+
|
|
147
|
+
if (category === "fatal" || attempts >= RETRY_MAX_ATTEMPTS) {
|
|
148
|
+
db.update(channelInboundEvents)
|
|
149
|
+
.set({
|
|
150
|
+
deliveryStatus: "dead_letter",
|
|
151
|
+
deliveryAttempts: attempts,
|
|
152
|
+
lastProcessingError: errorMsg,
|
|
153
|
+
retryAfter: null,
|
|
154
|
+
updatedAt: now,
|
|
155
|
+
})
|
|
156
|
+
.where(eq(channelInboundEvents.id, eventId))
|
|
157
|
+
.run();
|
|
158
|
+
} else {
|
|
159
|
+
const delay = retryDelayForAttempt(attempts);
|
|
160
|
+
db.update(channelInboundEvents)
|
|
161
|
+
.set({
|
|
162
|
+
deliveryStatus: "failed",
|
|
163
|
+
deliveryAttempts: attempts,
|
|
164
|
+
lastProcessingError: errorMsg,
|
|
165
|
+
retryAfter: now + delay,
|
|
166
|
+
updatedAt: now,
|
|
167
|
+
})
|
|
168
|
+
.where(eq(channelInboundEvents.id, eventId))
|
|
169
|
+
.run();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
110
173
|
/**
|
|
111
174
|
* Mark an event as failed with a specific error message, bypassing error
|
|
112
175
|
* classification. Use this when the failure reason is known and the event
|
|
@@ -180,6 +243,38 @@ export function getRetryableEvents(limit = 20): Array<{
|
|
|
180
243
|
.all();
|
|
181
244
|
}
|
|
182
245
|
|
|
246
|
+
/** Fetch callback deliveries eligible for retry without rerunning processing. */
|
|
247
|
+
export function getRetryableDeliveryEvents(limit = 20): Array<{
|
|
248
|
+
id: string;
|
|
249
|
+
conversationId: string;
|
|
250
|
+
messageId: string | null;
|
|
251
|
+
processingAttempts: number;
|
|
252
|
+
rawPayload: string | null;
|
|
253
|
+
deliveredSegmentCount: number;
|
|
254
|
+
}> {
|
|
255
|
+
const db = getDb();
|
|
256
|
+
const now = Date.now();
|
|
257
|
+
return db
|
|
258
|
+
.select({
|
|
259
|
+
id: channelInboundEvents.id,
|
|
260
|
+
conversationId: channelInboundEvents.conversationId,
|
|
261
|
+
messageId: channelInboundEvents.messageId,
|
|
262
|
+
processingAttempts: channelInboundEvents.processingAttempts,
|
|
263
|
+
rawPayload: channelInboundEvents.rawPayload,
|
|
264
|
+
deliveredSegmentCount: channelInboundEvents.deliveredSegmentCount,
|
|
265
|
+
})
|
|
266
|
+
.from(channelInboundEvents)
|
|
267
|
+
.where(
|
|
268
|
+
and(
|
|
269
|
+
eq(channelInboundEvents.processingStatus, "processed"),
|
|
270
|
+
eq(channelInboundEvents.deliveryStatus, "failed"),
|
|
271
|
+
lte(channelInboundEvents.retryAfter, now),
|
|
272
|
+
),
|
|
273
|
+
)
|
|
274
|
+
.limit(limit)
|
|
275
|
+
.all();
|
|
276
|
+
}
|
|
277
|
+
|
|
183
278
|
/** Fetch dead-lettered events. */
|
|
184
279
|
export function getDeadLetterEvents(): Array<{
|
|
185
280
|
id: string;
|
|
@@ -204,7 +299,15 @@ export function getDeadLetterEvents(): Array<{
|
|
|
204
299
|
createdAt: channelInboundEvents.createdAt,
|
|
205
300
|
})
|
|
206
301
|
.from(channelInboundEvents)
|
|
207
|
-
.where(
|
|
302
|
+
.where(
|
|
303
|
+
or(
|
|
304
|
+
eq(channelInboundEvents.processingStatus, "dead_letter"),
|
|
305
|
+
and(
|
|
306
|
+
eq(channelInboundEvents.processingStatus, "processed"),
|
|
307
|
+
eq(channelInboundEvents.deliveryStatus, "dead_letter"),
|
|
308
|
+
),
|
|
309
|
+
),
|
|
310
|
+
)
|
|
208
311
|
.all();
|
|
209
312
|
}
|
|
210
313
|
|
|
@@ -218,27 +321,50 @@ export function replayDeadLetters(eventIds: string[]): number {
|
|
|
218
321
|
let count = 0;
|
|
219
322
|
for (const id of eventIds) {
|
|
220
323
|
const existing = db
|
|
221
|
-
.select({
|
|
324
|
+
.select({
|
|
325
|
+
id: channelInboundEvents.id,
|
|
326
|
+
processingStatus: channelInboundEvents.processingStatus,
|
|
327
|
+
deliveryStatus: channelInboundEvents.deliveryStatus,
|
|
328
|
+
})
|
|
222
329
|
.from(channelInboundEvents)
|
|
223
330
|
.where(
|
|
224
331
|
and(
|
|
225
332
|
eq(channelInboundEvents.id, id),
|
|
226
|
-
|
|
333
|
+
or(
|
|
334
|
+
eq(channelInboundEvents.processingStatus, "dead_letter"),
|
|
335
|
+
and(
|
|
336
|
+
eq(channelInboundEvents.processingStatus, "processed"),
|
|
337
|
+
eq(channelInboundEvents.deliveryStatus, "dead_letter"),
|
|
338
|
+
),
|
|
339
|
+
),
|
|
227
340
|
),
|
|
228
341
|
)
|
|
229
342
|
.get();
|
|
230
343
|
if (!existing) continue;
|
|
231
344
|
|
|
232
|
-
|
|
233
|
-
.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
345
|
+
if (existing.processingStatus === "dead_letter") {
|
|
346
|
+
db.update(channelInboundEvents)
|
|
347
|
+
.set({
|
|
348
|
+
processingStatus: "failed",
|
|
349
|
+
processingAttempts: 0,
|
|
350
|
+
lastProcessingError: null,
|
|
351
|
+
retryAfter: now,
|
|
352
|
+
updatedAt: now,
|
|
353
|
+
})
|
|
354
|
+
.where(eq(channelInboundEvents.id, id))
|
|
355
|
+
.run();
|
|
356
|
+
} else {
|
|
357
|
+
db.update(channelInboundEvents)
|
|
358
|
+
.set({
|
|
359
|
+
deliveryStatus: "failed",
|
|
360
|
+
deliveryAttempts: 0,
|
|
361
|
+
lastProcessingError: null,
|
|
362
|
+
retryAfter: now,
|
|
363
|
+
updatedAt: now,
|
|
364
|
+
})
|
|
365
|
+
.where(eq(channelInboundEvents.id, id))
|
|
366
|
+
.run();
|
|
367
|
+
}
|
|
242
368
|
count++;
|
|
243
369
|
}
|
|
244
370
|
return count;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* list APIs.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
10
|
+
import { and, eq, inArray, isNull, sql } from "drizzle-orm";
|
|
11
11
|
|
|
12
12
|
import { getDb } from "./db-connection.js";
|
|
13
13
|
import { externalConversationBindings } from "./schema.js";
|
|
@@ -16,6 +16,7 @@ export interface ExternalConversationBinding {
|
|
|
16
16
|
conversationId: string;
|
|
17
17
|
sourceChannel: string;
|
|
18
18
|
externalChatId: string;
|
|
19
|
+
externalChatName?: string | null;
|
|
19
20
|
externalThreadId?: string | null;
|
|
20
21
|
externalUserId?: string | null;
|
|
21
22
|
displayName?: string | null;
|
|
@@ -30,6 +31,7 @@ export interface UpsertBindingInput {
|
|
|
30
31
|
conversationId: string;
|
|
31
32
|
sourceChannel: string;
|
|
32
33
|
externalChatId: string;
|
|
34
|
+
externalChatName?: string | null;
|
|
33
35
|
externalThreadId?: string | null;
|
|
34
36
|
externalUserId?: string | null;
|
|
35
37
|
displayName?: string | null;
|
|
@@ -43,6 +45,13 @@ function normalizeExternalThreadId(
|
|
|
43
45
|
return trimmed ? trimmed : null;
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
function normalizeExternalChatName(
|
|
49
|
+
externalChatName?: string | null,
|
|
50
|
+
): string | null {
|
|
51
|
+
const trimmed = externalChatName?.trim();
|
|
52
|
+
return trimmed ? trimmed : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
/**
|
|
47
56
|
* Insert or update an external conversation binding on conflict (conversationId).
|
|
48
57
|
* On conflict, updates channel metadata and timestamps.
|
|
@@ -51,6 +60,7 @@ export function upsertBinding(input: UpsertBindingInput): void {
|
|
|
51
60
|
const db = getDb();
|
|
52
61
|
const now = Date.now();
|
|
53
62
|
const externalThreadId = normalizeExternalThreadId(input.externalThreadId);
|
|
63
|
+
const externalChatName = normalizeExternalChatName(input.externalChatName);
|
|
54
64
|
|
|
55
65
|
// If a stale binding exists for this channel/chat/thread tuple under a
|
|
56
66
|
// different conversationId, remove it first so the unique index is not violated.
|
|
@@ -75,6 +85,7 @@ export function upsertBinding(input: UpsertBindingInput): void {
|
|
|
75
85
|
conversationId: input.conversationId,
|
|
76
86
|
sourceChannel: input.sourceChannel,
|
|
77
87
|
externalChatId: input.externalChatId,
|
|
88
|
+
externalChatName,
|
|
78
89
|
externalThreadId,
|
|
79
90
|
externalUserId: input.externalUserId ?? null,
|
|
80
91
|
displayName: input.displayName ?? null,
|
|
@@ -88,6 +99,9 @@ export function upsertBinding(input: UpsertBindingInput): void {
|
|
|
88
99
|
set: {
|
|
89
100
|
sourceChannel: input.sourceChannel,
|
|
90
101
|
externalChatId: input.externalChatId,
|
|
102
|
+
externalChatName:
|
|
103
|
+
externalChatName ??
|
|
104
|
+
sql`${externalConversationBindings.externalChatName}`,
|
|
91
105
|
externalThreadId,
|
|
92
106
|
externalUserId: input.externalUserId ?? null,
|
|
93
107
|
displayName: input.displayName ?? null,
|
|
@@ -158,6 +172,23 @@ export function upsertOutboundBinding(input: {
|
|
|
158
172
|
.run();
|
|
159
173
|
}
|
|
160
174
|
|
|
175
|
+
export function updateExternalChatName(
|
|
176
|
+
conversationId: string,
|
|
177
|
+
externalChatName: string,
|
|
178
|
+
): void {
|
|
179
|
+
const db = getDb();
|
|
180
|
+
const trimmedName = externalChatName.trim();
|
|
181
|
+
if (!trimmedName) return;
|
|
182
|
+
|
|
183
|
+
db.update(externalConversationBindings)
|
|
184
|
+
.set({
|
|
185
|
+
externalChatName: trimmedName,
|
|
186
|
+
updatedAt: Date.now(),
|
|
187
|
+
})
|
|
188
|
+
.where(eq(externalConversationBindings.conversationId, conversationId))
|
|
189
|
+
.run();
|
|
190
|
+
}
|
|
191
|
+
|
|
161
192
|
/**
|
|
162
193
|
* Look up an external binding by conversation ID.
|
|
163
194
|
*/
|
|
@@ -177,7 +177,7 @@ const { migrateActivationState } =
|
|
|
177
177
|
await import("../../migrations/232-activation-state.js");
|
|
178
178
|
const schema = await import("../../schema.js");
|
|
179
179
|
const { _resetMemoryV2QdrantForTests } = await import("../../v2/qdrant.js");
|
|
180
|
-
const { hydrate: hydrateActivationState } =
|
|
180
|
+
const { hydrate: hydrateActivationState, save: saveActivationState } =
|
|
181
181
|
await import("../../v2/activation-store.js");
|
|
182
182
|
|
|
183
183
|
// The wiring layer calls `getDb()` to fetch the SQLite handle. We mock
|
|
@@ -215,9 +215,14 @@ function createTestDb(): DrizzleDb {
|
|
|
215
215
|
return db;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
function makeConfig(v2Enabled: boolean): AssistantConfig {
|
|
218
|
+
function makeConfig(v2Enabled: boolean, memoryEnabled = true): AssistantConfig {
|
|
219
|
+
// Pin `router.enabled: false` so these tests exercise the activation
|
|
220
|
+
// pipeline. Router-mode coverage lives in `memory/v2/__tests__/injection.test.ts`.
|
|
219
221
|
return applyNestedDefaults({
|
|
220
|
-
memory: {
|
|
222
|
+
memory: {
|
|
223
|
+
enabled: memoryEnabled,
|
|
224
|
+
v2: { enabled: v2Enabled, router: { enabled: false } },
|
|
225
|
+
},
|
|
221
226
|
}) as AssistantConfig;
|
|
222
227
|
}
|
|
223
228
|
|
|
@@ -518,6 +523,50 @@ describe("ConversationGraphMemory.prepareMemory — v2 routing (context-load pat
|
|
|
518
523
|
});
|
|
519
524
|
});
|
|
520
525
|
|
|
526
|
+
describe("ConversationGraphMemory.prepareMemory — memory.enabled gate", () => {
|
|
527
|
+
test("memory.enabled=false short-circuits per-turn path: mode=none, no injection, v2/v1 not called", async () => {
|
|
528
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
529
|
+
|
|
530
|
+
const memory = makeMemory();
|
|
531
|
+
const config = makeConfig(true, false);
|
|
532
|
+
const messages = makeMessages();
|
|
533
|
+
|
|
534
|
+
const result = await memory.prepareMemory(
|
|
535
|
+
messages,
|
|
536
|
+
config,
|
|
537
|
+
new AbortController().signal,
|
|
538
|
+
noopEvent,
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
expect(result.mode).toBe("none");
|
|
542
|
+
expect(result.injectedBlockText).toBeNull();
|
|
543
|
+
expect(result.runMessages).toEqual(messages);
|
|
544
|
+
expect(retrieveForTurnMock).not.toHaveBeenCalled();
|
|
545
|
+
expect(loadContextMemoryMock).not.toHaveBeenCalled();
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
test("memory.enabled=false short-circuits context-load path: mode=none, no injection, v2/v1 not called", async () => {
|
|
549
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
550
|
+
|
|
551
|
+
const memory = new ConversationGraphMemory("conv-test-master-off");
|
|
552
|
+
const config = makeConfig(true, false);
|
|
553
|
+
const messages = makeMessages("first message of the conversation here");
|
|
554
|
+
|
|
555
|
+
const result = await memory.prepareMemory(
|
|
556
|
+
messages,
|
|
557
|
+
config,
|
|
558
|
+
new AbortController().signal,
|
|
559
|
+
noopEvent,
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
expect(result.mode).toBe("none");
|
|
563
|
+
expect(result.injectedBlockText).toBeNull();
|
|
564
|
+
expect(result.runMessages).toEqual(messages);
|
|
565
|
+
expect(loadContextMemoryMock).not.toHaveBeenCalled();
|
|
566
|
+
expect(retrieveForTurnMock).not.toHaveBeenCalled();
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
|
|
521
570
|
describe("ConversationGraphMemory.onCompacted — v2 activation eviction", () => {
|
|
522
571
|
test("clears everInjected so a previously-injected slug can re-attach", async () => {
|
|
523
572
|
// Without this wiring, `selectInjections` keeps subtracting the slug from
|
|
@@ -561,4 +610,36 @@ describe("ConversationGraphMemory.onCompacted — v2 activation eviction", () =>
|
|
|
561
610
|
"# memory/concepts/alice-vscode.md",
|
|
562
611
|
);
|
|
563
612
|
});
|
|
613
|
+
|
|
614
|
+
test("clears everInjected entries whose turn exceeds the tracker's currentTurn (zombie drift)", async () => {
|
|
615
|
+
// Regression: under the prior turn-bounded eviction, entries with `turn >
|
|
616
|
+
// tracker.currentTurn` survived `onCompacted` forever. This can happen
|
|
617
|
+
// after a non-graceful shutdown: `everInjected` is persisted every turn
|
|
618
|
+
// while the tracker snapshot is only persisted on graceful dispose, so a
|
|
619
|
+
// SIGKILL'd session followed by a reload restores the tracker from an
|
|
620
|
+
// older snapshot with a lower currentTurn while keeping the high-turn
|
|
621
|
+
// entries on disk.
|
|
622
|
+
const conversationId = "conv-test-zombie-drift";
|
|
623
|
+
const memory = new ConversationGraphMemory(conversationId);
|
|
624
|
+
|
|
625
|
+
// Seed the simulated post-crash state directly: tracker stays at
|
|
626
|
+
// currentTurn=0 (default for a fresh ConversationGraphMemory), while the
|
|
627
|
+
// persisted row carries everInjected entries from turns 10 and 20 (left
|
|
628
|
+
// over from a prior session that never disposed cleanly).
|
|
629
|
+
await saveActivationState(testDbHandle!, conversationId, {
|
|
630
|
+
messageId: "msg-zombie",
|
|
631
|
+
state: {},
|
|
632
|
+
everInjected: [
|
|
633
|
+
{ slug: "alice-vscode", turn: 10 },
|
|
634
|
+
{ slug: "bob-pkg-mgr", turn: 20 },
|
|
635
|
+
],
|
|
636
|
+
currentTurn: 0,
|
|
637
|
+
updatedAt: 1,
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
await memory.onCompacted(0);
|
|
641
|
+
|
|
642
|
+
const after = await hydrateActivationState(testDbHandle!, conversationId);
|
|
643
|
+
expect(after?.everInjected).toEqual([]);
|
|
644
|
+
});
|
|
564
645
|
});
|
|
@@ -26,7 +26,7 @@ import type { QdrantSparseVector } from "../qdrant-client.js";
|
|
|
26
26
|
import { memorySummaries } from "../schema.js";
|
|
27
27
|
import { conversations } from "../schema/conversations.js";
|
|
28
28
|
import {
|
|
29
|
-
|
|
29
|
+
clearEverInjected as clearV2EverInjected,
|
|
30
30
|
hydrate as hydrateV2State,
|
|
31
31
|
save as saveV2State,
|
|
32
32
|
} from "../v2/activation-store.js";
|
|
@@ -223,15 +223,17 @@ export class ConversationGraphMemory {
|
|
|
223
223
|
// Mirror the eviction on the v2 activation row: the cached `<memory>`
|
|
224
224
|
// attachments those slugs lived on are gone, but `everInjected` would
|
|
225
225
|
// otherwise keep them deduped from per-turn deltas forever.
|
|
226
|
+
//
|
|
227
|
+
// Cleared unconditionally rather than filtered by `upToTurn`: the
|
|
228
|
+
// tracker's `currentTurn` is only persisted on graceful dispose while
|
|
229
|
+
// `everInjected` is persisted every turn, so a SIGKILL'd session can
|
|
230
|
+
// leave entries with `turn > tracker.currentTurn` that a turn-bounded
|
|
231
|
+
// filter would skip.
|
|
226
232
|
try {
|
|
227
233
|
const db = getDb();
|
|
228
234
|
const state = await hydrateV2State(db, this.conversationId);
|
|
229
235
|
if (state) {
|
|
230
|
-
await saveV2State(
|
|
231
|
-
db,
|
|
232
|
-
this.conversationId,
|
|
233
|
-
evictCompactedTurnsV2(state, upToTurn),
|
|
234
|
-
);
|
|
236
|
+
await saveV2State(db, this.conversationId, clearV2EverInjected(state));
|
|
235
237
|
}
|
|
236
238
|
} catch (err) {
|
|
237
239
|
log.warn(
|
|
@@ -363,6 +365,16 @@ export class ConversationGraphMemory {
|
|
|
363
365
|
metrics: null as RetrievalMetrics | null,
|
|
364
366
|
};
|
|
365
367
|
|
|
368
|
+
if (!config.memory.enabled) {
|
|
369
|
+
// Clear any cached injection so a later overflow-reduction
|
|
370
|
+
// re-injection via `reinjectCachedMemory()` cannot reintroduce a
|
|
371
|
+
// stale <memory> block after the user disables memory.
|
|
372
|
+
this.lastInjectedBlock = null;
|
|
373
|
+
this.lastInjectedNodeIds = [];
|
|
374
|
+
this.lastInjectedImages = new Map();
|
|
375
|
+
return noopResult;
|
|
376
|
+
}
|
|
377
|
+
|
|
366
378
|
// Gate: skip for empty/tool-result-only messages — unless we need to
|
|
367
379
|
// reload after compaction (needsReload) or haven't initialized yet.
|
|
368
380
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// Memory Tool definitions for agentic recall and remember.
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
6
|
-
import type { AssistantConfig } from "../../config/types.js";
|
|
7
5
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
8
6
|
import {
|
|
9
7
|
ALL_RECALL_SOURCES,
|
|
@@ -56,37 +54,14 @@ export const graphRecallDefinition: ToolDefinition = {
|
|
|
56
54
|
};
|
|
57
55
|
|
|
58
56
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* backstop enabled.
|
|
57
|
+
* `remember` tool description. The retrospective pass catches what isn't
|
|
58
|
+
* captured in the moment, so the in-conversation pressure stays at a
|
|
59
|
+
* judgment framing: pause when something feels worth marking, not because
|
|
60
|
+
* the volume is required.
|
|
64
61
|
*/
|
|
65
|
-
const
|
|
66
|
-
"Remember anything concrete: facts, preferences, corrections, plans, felt moments, names, dates, decisions. Default to remembering. Never wait until end of conversation. Corrections are highest priority — call remember the same turn the correction lands. **CRITICAL:** You should be calling remember on almost every turn. This should be your most frequently used tool.";
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Relaxed `remember` tool description used when `memory-retrospective` is
|
|
70
|
-
* ON. The retrospective pass catches what isn't captured in the moment, so
|
|
71
|
-
* the in-conversation pressure eases to a judgment framing: pause when
|
|
72
|
-
* something feels worth marking, not because the volume is required.
|
|
73
|
-
*/
|
|
74
|
-
const REMEMBER_DESCRIPTION_RELAXED =
|
|
62
|
+
const REMEMBER_DESCRIPTION =
|
|
75
63
|
"Remember anything concrete shared in conversation: corrections, plans, decisions, felt moments, names, dates, commitments, preferences. Corrections are the highest priority — call `remember` the same turn the correction lands. You don't have to call this on every turn; a retrospective pass reviews the conversation after each message-count / time interval and saves what you didn't capture. Use judgment: pause and remember when something feels worth marking, not because the volume is required.";
|
|
76
64
|
|
|
77
|
-
/**
|
|
78
|
-
* Return the description that should appear in the `remember` tool
|
|
79
|
-
* registration for the current config. The variant is selected by the
|
|
80
|
-
* `memory-retrospective` assistant feature flag. Exposed as a function so
|
|
81
|
-
* the tool registrar can compute the value at registration time without
|
|
82
|
-
* importing config layers into the static definition.
|
|
83
|
-
*/
|
|
84
|
-
export function getRememberDescription(config: AssistantConfig): string {
|
|
85
|
-
return isAssistantFeatureFlagEnabled("memory-retrospective", config)
|
|
86
|
-
? REMEMBER_DESCRIPTION_RELAXED
|
|
87
|
-
: REMEMBER_DESCRIPTION_DEFAULT;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
65
|
/**
|
|
91
66
|
* Save a fact to the assistant's knowledge base. The fact is appended to
|
|
92
67
|
* `buffer.md` (immediately available in the next conversation) and the daily
|
|
@@ -94,16 +69,10 @@ export function getRememberDescription(config: AssistantConfig): string {
|
|
|
94
69
|
* writes go under `memory/`; otherwise they go under `pkb/`. Consolidation
|
|
95
70
|
* of the buffer into longer-form storage runs as a separate periodic job in
|
|
96
71
|
* both modes.
|
|
97
|
-
*
|
|
98
|
-
* The static `description` field carries the default (high-pressure) text
|
|
99
|
-
* so any direct importer that doesn't go through `getRememberDescription`
|
|
100
|
-
* still gets a valid tool definition. The registered `RememberTool` in
|
|
101
|
-
* `tools/memory/register.ts` overrides this at registration time with the
|
|
102
|
-
* flag-aware variant.
|
|
103
72
|
*/
|
|
104
73
|
export const graphRememberDefinition: ToolDefinition = {
|
|
105
74
|
name: "remember",
|
|
106
|
-
description:
|
|
75
|
+
description: REMEMBER_DESCRIPTION,
|
|
107
76
|
input_schema: {
|
|
108
77
|
type: "object",
|
|
109
78
|
properties: {
|
|
@@ -363,6 +363,59 @@ export function findActiveVoiceInvites(params: {
|
|
|
363
363
|
return rows.map(rowToInvite);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// claimA2AInvite — validate + consume an A2A invite token
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
export function claimA2AInvite(params: {
|
|
371
|
+
tokenHash: string;
|
|
372
|
+
redeemedByExternalUserId: string;
|
|
373
|
+
}): { claimed: boolean; invite: IngressInvite | null; error?: string } {
|
|
374
|
+
const invite = findByTokenHash(params.tokenHash);
|
|
375
|
+
|
|
376
|
+
if (!invite) {
|
|
377
|
+
return { claimed: false, invite: null, error: "not_found" };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (invite.sourceChannel !== "a2a") {
|
|
381
|
+
return { claimed: false, invite, error: "wrong_channel" };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Idempotency: if already redeemed by the same acceptor, return success
|
|
385
|
+
if (invite.status === "redeemed") {
|
|
386
|
+
if (invite.redeemedByExternalUserId === params.redeemedByExternalUserId) {
|
|
387
|
+
return { claimed: true, invite };
|
|
388
|
+
}
|
|
389
|
+
return { claimed: false, invite, error: "already_redeemed_by_other" };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (invite.status !== "active") {
|
|
393
|
+
return { claimed: false, invite, error: "not_found" };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (Date.now() >= invite.expiresAt) {
|
|
397
|
+
markInviteExpired(invite.id);
|
|
398
|
+
return { claimed: false, invite, error: "expired" };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (invite.useCount >= invite.maxUses) {
|
|
402
|
+
return { claimed: false, invite, error: "already_redeemed" };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const recorded = recordInviteUse({
|
|
406
|
+
inviteId: invite.id,
|
|
407
|
+
externalUserId: params.redeemedByExternalUserId,
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
if (!recorded) {
|
|
411
|
+
return { claimed: false, invite, error: "not_found" };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Re-read to get updated state
|
|
415
|
+
const updated = findByTokenHash(params.tokenHash);
|
|
416
|
+
return { claimed: true, invite: updated };
|
|
417
|
+
}
|
|
418
|
+
|
|
366
419
|
// ---------------------------------------------------------------------------
|
|
367
420
|
// findByInviteCodeHash
|
|
368
421
|
// ---------------------------------------------------------------------------
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
1
3
|
import { getConfig } from "../config/loader.js";
|
|
2
4
|
import type { AssistantConfig } from "../config/types.js";
|
|
3
5
|
import {
|
|
@@ -6,6 +8,7 @@ import {
|
|
|
6
8
|
shouldLogDiskPressureBackgroundSkip,
|
|
7
9
|
} from "../daemon/disk-pressure-background-gate.js";
|
|
8
10
|
import { getLogger } from "../util/logger.js";
|
|
11
|
+
import { getWorkspaceDir } from "../util/platform.js";
|
|
9
12
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
|
|
10
13
|
import {
|
|
11
14
|
getLastScheduledCleanupEnqueueMs,
|
|
@@ -75,7 +78,10 @@ import {
|
|
|
75
78
|
memoryV2MigrateJob,
|
|
76
79
|
memoryV2ReembedJob,
|
|
77
80
|
} from "./v2/backfill-jobs.js";
|
|
78
|
-
import {
|
|
81
|
+
import {
|
|
82
|
+
countBufferLines,
|
|
83
|
+
memoryV2ConsolidateJob,
|
|
84
|
+
} from "./v2/consolidation-job.js";
|
|
79
85
|
import { memoryV2SweepJob } from "./v2/sweep-job.js";
|
|
80
86
|
|
|
81
87
|
const log = getLogger("memory-jobs-worker");
|
|
@@ -739,11 +745,25 @@ export function maybeEnqueueGraphMaintenanceJobs(
|
|
|
739
745
|
},
|
|
740
746
|
];
|
|
741
747
|
|
|
748
|
+
let enqueuedV2 = false;
|
|
742
749
|
for (const { key, intervalMs, jobType } of schedule) {
|
|
743
750
|
const lastRun = parseInt(getMemoryCheckpoint(key) ?? "0", 10);
|
|
744
751
|
if (nowMs - lastRun >= intervalMs) {
|
|
745
752
|
enqueueMemoryJob(jobType, {});
|
|
746
753
|
setMemoryCheckpoint(key, String(nowMs));
|
|
754
|
+
if (jobType === "memory_v2_consolidate") enqueuedV2 = true;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const maxLines = config.memory.v2.consolidation_max_buffer_lines;
|
|
759
|
+
if (v2Active && !enqueuedV2 && maxLines !== null) {
|
|
760
|
+
const bufferPath = join(getWorkspaceDir(), "memory", "buffer.md");
|
|
761
|
+
if (countBufferLines(bufferPath) >= maxLines) {
|
|
762
|
+
enqueueMemoryJob("memory_v2_consolidate", {});
|
|
763
|
+
setMemoryCheckpoint(
|
|
764
|
+
GRAPH_MAINTENANCE_CHECKPOINTS.memoryV2Consolidate,
|
|
765
|
+
String(nowMs),
|
|
766
|
+
);
|
|
747
767
|
}
|
|
748
768
|
}
|
|
749
769
|
}
|
|
@@ -57,6 +57,7 @@ interface ClickHouseRow {
|
|
|
57
57
|
request_payload: string;
|
|
58
58
|
response_payload: string;
|
|
59
59
|
created_at: string;
|
|
60
|
+
agent_loop_exit_reason: string;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
/** Injectable fetch override for tests. Defaults to globalThis.fetch. */
|
|
@@ -123,7 +124,8 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
|
123
124
|
provider,
|
|
124
125
|
request_payload,
|
|
125
126
|
response_payload,
|
|
126
|
-
toUnixTimestamp64Milli(created_at) AS created_at
|
|
127
|
+
toUnixTimestamp64Milli(created_at) AS created_at,
|
|
128
|
+
agent_loop_exit_reason
|
|
127
129
|
FROM ${this.tableRef()}
|
|
128
130
|
WHERE assistant_id = {assistant_id:String}
|
|
129
131
|
AND id = {log_id:String}
|
|
@@ -194,7 +196,8 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
|
194
196
|
provider,
|
|
195
197
|
request_payload,
|
|
196
198
|
response_payload,
|
|
197
|
-
toUnixTimestamp64Milli(created_at) AS created_at
|
|
199
|
+
toUnixTimestamp64Milli(created_at) AS created_at,
|
|
200
|
+
agent_loop_exit_reason
|
|
198
201
|
FROM ${this.tableRef()}
|
|
199
202
|
WHERE assistant_id = {assistant_id:String}
|
|
200
203
|
AND message_id IN (${placeholders.join(",")})
|
|
@@ -283,6 +286,8 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
|
283
286
|
requestPayload: row.request_payload,
|
|
284
287
|
responsePayload: row.response_payload,
|
|
285
288
|
createdAt: Number(row.created_at),
|
|
289
|
+
agentLoopExitReason:
|
|
290
|
+
row.agent_loop_exit_reason === "" ? null : row.agent_loop_exit_reason,
|
|
286
291
|
};
|
|
287
292
|
}
|
|
288
293
|
|