@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
|
@@ -33,11 +33,52 @@ let priorRetroMessages: Array<{ role: string; content: string }> = [];
|
|
|
33
33
|
|
|
34
34
|
let mockWakeResult: { invoked: boolean; reason?: string } = { invoked: true };
|
|
35
35
|
let mockWakeThrows: Error | null = null;
|
|
36
|
-
let wakeCalls: Array<{
|
|
36
|
+
let wakeCalls: Array<{
|
|
37
|
+
conversationId: string;
|
|
38
|
+
hint: string;
|
|
39
|
+
opts: Record<string, unknown>;
|
|
40
|
+
}> = [];
|
|
37
41
|
let bootstrappedConversationId = "bg-conv-new";
|
|
38
42
|
let bootstrapCalls: Array<{ forkParentConversationId?: string }> = [];
|
|
39
43
|
let deletedConversationIds: string[] = [];
|
|
40
44
|
|
|
45
|
+
// Fork-path mocks. Flag off by default so legacy-path tests stay untouched.
|
|
46
|
+
let forkFlagEnabled = false;
|
|
47
|
+
let forkedConversationId = "fork-conv-1";
|
|
48
|
+
let forkCalls: Array<{
|
|
49
|
+
conversationId: string;
|
|
50
|
+
throughMessageId?: string;
|
|
51
|
+
source: string;
|
|
52
|
+
title: string;
|
|
53
|
+
conversationType?: string;
|
|
54
|
+
groupId?: string;
|
|
55
|
+
}> = [];
|
|
56
|
+
let addMessageCalls: Array<{
|
|
57
|
+
conversationId: string;
|
|
58
|
+
role: string;
|
|
59
|
+
content: string;
|
|
60
|
+
metadata: unknown;
|
|
61
|
+
}> = [];
|
|
62
|
+
|
|
63
|
+
// Per-conversation overrides for getConversation. Lets fork-path tests stage
|
|
64
|
+
// a fork-kind prior retrospective row alongside the default legacy stub.
|
|
65
|
+
type ConversationStub = {
|
|
66
|
+
source: string;
|
|
67
|
+
forkParentMessageId: string | null;
|
|
68
|
+
title?: string;
|
|
69
|
+
};
|
|
70
|
+
let conversationOverrides: Record<string, ConversationStub> = {};
|
|
71
|
+
|
|
72
|
+
// Per-conversation overrides for getMessages so fork-path tests can return
|
|
73
|
+
// fork-shaped message rows (with metadata stamps + createdAt boundaries).
|
|
74
|
+
type StubMessage = {
|
|
75
|
+
role: string;
|
|
76
|
+
content: string;
|
|
77
|
+
createdAt: number;
|
|
78
|
+
metadata: string | null;
|
|
79
|
+
};
|
|
80
|
+
let messagesByConversationId: Record<string, StubMessage[]> = {};
|
|
81
|
+
|
|
41
82
|
mock.module("../memory-retrospective-state.js", () => ({
|
|
42
83
|
getRetrospectiveState: (_id: string) => mockState,
|
|
43
84
|
upsertRetrospectiveState: (args: {
|
|
@@ -55,20 +96,97 @@ mock.module("../memory-retrospective-state.js", () => ({
|
|
|
55
96
|
mock.module("../conversation-crud.js", () => ({
|
|
56
97
|
getMessagesAfter: (_id: string, _afterId: string | null) => newMessages,
|
|
57
98
|
getMessages: (id: string) => {
|
|
99
|
+
if (messagesByConversationId[id]) return messagesByConversationId[id];
|
|
58
100
|
if (id === priorRetroId) return priorRetroMessages;
|
|
59
101
|
return [];
|
|
60
102
|
},
|
|
61
103
|
findMostRecentRetrospectiveFor: (_id: string) =>
|
|
62
104
|
priorRetroId ? { id: priorRetroId } : null,
|
|
105
|
+
// The fork path calls `getConversation(sourceConversationId)` to read the
|
|
106
|
+
// source's title for the fork title. `collectPriorRetrospectiveRemembers`
|
|
107
|
+
// also calls it with the prior retro id to discriminate legacy vs fork
|
|
108
|
+
// sources — for that id return a legacy-shaped row by default so existing
|
|
109
|
+
// tests exercise the unchanged extract-everything code path.
|
|
110
|
+
// `conversationOverrides` lets per-test setup stage fork-kind priors.
|
|
111
|
+
getConversation: (id: string) => {
|
|
112
|
+
if (conversationOverrides[id]) return conversationOverrides[id];
|
|
113
|
+
if (id === priorRetroId) {
|
|
114
|
+
return {
|
|
115
|
+
source: "memory-retrospective",
|
|
116
|
+
forkParentMessageId: null,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
source: "user",
|
|
121
|
+
forkParentMessageId: null,
|
|
122
|
+
title: "Source conversation",
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
forkConversation: (params: {
|
|
126
|
+
conversationId: string;
|
|
127
|
+
throughMessageId?: string;
|
|
128
|
+
source: string;
|
|
129
|
+
title: string;
|
|
130
|
+
conversationType?: string;
|
|
131
|
+
groupId?: string;
|
|
132
|
+
}) => {
|
|
133
|
+
forkCalls.push(params);
|
|
134
|
+
return { id: forkedConversationId };
|
|
135
|
+
},
|
|
136
|
+
addMessage: async (
|
|
137
|
+
conversationId: string,
|
|
138
|
+
role: string,
|
|
139
|
+
content: string,
|
|
140
|
+
metadata: unknown,
|
|
141
|
+
) => {
|
|
142
|
+
addMessageCalls.push({ conversationId, role, content, metadata });
|
|
143
|
+
},
|
|
63
144
|
deleteConversation: (id: string) => {
|
|
64
145
|
deletedConversationIds.push(id);
|
|
65
146
|
},
|
|
66
147
|
}));
|
|
67
148
|
|
|
149
|
+
mock.module("../../config/assistant-feature-flags.js", () => ({
|
|
150
|
+
isAssistantFeatureFlagEnabled: (flag: string) =>
|
|
151
|
+
flag === "memory-retrospective-fork" && forkFlagEnabled,
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
let transcriptFormatterCalls: Array<{
|
|
155
|
+
messageIds: string[];
|
|
156
|
+
timeZone?: string;
|
|
157
|
+
assistantName?: string | null;
|
|
158
|
+
userName?: string | null;
|
|
159
|
+
}> = [];
|
|
160
|
+
|
|
68
161
|
mock.module("../../export/transcript-formatter.js", () => ({
|
|
69
162
|
formatMessageSliceForTranscript: (
|
|
70
163
|
messages: Array<{ id: string; createdAt: number }>,
|
|
71
|
-
|
|
164
|
+
options: {
|
|
165
|
+
timeZone?: string;
|
|
166
|
+
assistantName?: string | null;
|
|
167
|
+
userName?: string | null;
|
|
168
|
+
} = {},
|
|
169
|
+
) => {
|
|
170
|
+
transcriptFormatterCalls.push({
|
|
171
|
+
messageIds: messages.map((m) => m.id),
|
|
172
|
+
timeZone: options.timeZone,
|
|
173
|
+
assistantName: options.assistantName,
|
|
174
|
+
userName: options.userName,
|
|
175
|
+
});
|
|
176
|
+
return messages.map((m) => `[msg ${m.id}]`).join("\n");
|
|
177
|
+
},
|
|
178
|
+
}));
|
|
179
|
+
|
|
180
|
+
let mockAssistantName: string | null = "Bob";
|
|
181
|
+
let mockUserName: string | null = "Alice";
|
|
182
|
+
|
|
183
|
+
mock.module("../../daemon/identity-helpers.js", () => ({
|
|
184
|
+
getAssistantName: () => mockAssistantName,
|
|
185
|
+
resolveUserName: (_workspaceDir: string) => mockUserName,
|
|
186
|
+
}));
|
|
187
|
+
|
|
188
|
+
mock.module("../../util/platform.js", () => ({
|
|
189
|
+
getWorkspaceDir: () => "/tmp/test-workspace",
|
|
72
190
|
}));
|
|
73
191
|
|
|
74
192
|
mock.module("../conversation-bootstrap.js", () => ({
|
|
@@ -85,11 +203,14 @@ mock.module("../../daemon/trust-context.js", () => ({
|
|
|
85
203
|
}));
|
|
86
204
|
|
|
87
205
|
mock.module("../../runtime/agent-wake.js", () => ({
|
|
88
|
-
wakeAgentForOpportunity: async (
|
|
89
|
-
conversationId: string;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
206
|
+
wakeAgentForOpportunity: async (
|
|
207
|
+
opts: { conversationId: string; hint: string } & Record<string, unknown>,
|
|
208
|
+
) => {
|
|
209
|
+
wakeCalls.push({
|
|
210
|
+
conversationId: opts.conversationId,
|
|
211
|
+
hint: opts.hint,
|
|
212
|
+
opts,
|
|
213
|
+
});
|
|
93
214
|
if (mockWakeThrows) throw mockWakeThrows;
|
|
94
215
|
return mockWakeResult;
|
|
95
216
|
},
|
|
@@ -102,9 +223,19 @@ mock.module("../jobs-store.js", () => ({
|
|
|
102
223
|
import type { MemoryJob } from "../jobs-store.js";
|
|
103
224
|
import { memoryRetrospectiveJob } from "../memory-retrospective-job.js";
|
|
104
225
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
226
|
+
function makeConfig(
|
|
227
|
+
overrides: { userTimezone?: string; detectedTimezone?: string } = {},
|
|
228
|
+
): Parameters<typeof memoryRetrospectiveJob>[1] {
|
|
229
|
+
return {
|
|
230
|
+
memory: { v2: { enabled: true } },
|
|
231
|
+
ui: {
|
|
232
|
+
userTimezone: overrides.userTimezone,
|
|
233
|
+
detectedTimezone: overrides.detectedTimezone,
|
|
234
|
+
},
|
|
235
|
+
} as unknown as Parameters<typeof memoryRetrospectiveJob>[1];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const stubConfig = makeConfig();
|
|
108
239
|
|
|
109
240
|
function makeJob(conversationId = "src-conv-1"): MemoryJob<{
|
|
110
241
|
conversationId?: string;
|
|
@@ -155,6 +286,15 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
155
286
|
bootstrappedConversationId = "bg-conv-new";
|
|
156
287
|
bootstrapCalls = [];
|
|
157
288
|
deletedConversationIds = [];
|
|
289
|
+
transcriptFormatterCalls = [];
|
|
290
|
+
mockAssistantName = "Bob";
|
|
291
|
+
mockUserName = "Alice";
|
|
292
|
+
forkFlagEnabled = false;
|
|
293
|
+
forkedConversationId = "fork-conv-1";
|
|
294
|
+
forkCalls = [];
|
|
295
|
+
addMessageCalls = [];
|
|
296
|
+
conversationOverrides = {};
|
|
297
|
+
messagesByConversationId = {};
|
|
158
298
|
});
|
|
159
299
|
|
|
160
300
|
test("first-run happy path: no state row, no prior retrospective, both pointer fields set on success", async () => {
|
|
@@ -274,6 +414,44 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
274
414
|
expect(hint).toContain("- a real save");
|
|
275
415
|
});
|
|
276
416
|
|
|
417
|
+
test("transcript is formatted in the configured user timezone and the prompt discloses it", async () => {
|
|
418
|
+
const config = makeConfig({ userTimezone: "America/Los_Angeles" });
|
|
419
|
+
await memoryRetrospectiveJob(makeJob(), config);
|
|
420
|
+
|
|
421
|
+
expect(transcriptFormatterCalls).toHaveLength(1);
|
|
422
|
+
expect(transcriptFormatterCalls[0]!.timeZone).toBe("America/Los_Angeles");
|
|
423
|
+
|
|
424
|
+
const hint = wakeCalls[0]!.hint;
|
|
425
|
+
expect(hint).toContain("Timestamps are in America/Los_Angeles.");
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
test("detected timezone is used when no manual override is set", async () => {
|
|
429
|
+
const config = makeConfig({ detectedTimezone: "Europe/Berlin" });
|
|
430
|
+
await memoryRetrospectiveJob(makeJob(), config);
|
|
431
|
+
|
|
432
|
+
expect(transcriptFormatterCalls[0]!.timeZone).toBe("Europe/Berlin");
|
|
433
|
+
|
|
434
|
+
const hint = wakeCalls[0]!.hint;
|
|
435
|
+
expect(hint).toContain("Timestamps are in Europe/Berlin.");
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test("resolved assistant and user display names are passed to the transcript formatter", async () => {
|
|
439
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
440
|
+
|
|
441
|
+
expect(transcriptFormatterCalls).toHaveLength(1);
|
|
442
|
+
expect(transcriptFormatterCalls[0]!.assistantName).toBe("Bob");
|
|
443
|
+
expect(transcriptFormatterCalls[0]!.userName).toBe("Alice");
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("formatter receives null names when identity files are missing — formatter handles fallback", async () => {
|
|
447
|
+
mockAssistantName = null;
|
|
448
|
+
mockUserName = null;
|
|
449
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
450
|
+
|
|
451
|
+
expect(transcriptFormatterCalls[0]!.assistantName).toBeNull();
|
|
452
|
+
expect(transcriptFormatterCalls[0]!.userName).toBeNull();
|
|
453
|
+
});
|
|
454
|
+
|
|
277
455
|
test("non-remember tool_use blocks in the prior retro are ignored", async () => {
|
|
278
456
|
priorRetroId = "prior-retro-conv-1";
|
|
279
457
|
priorRetroMessages = [
|
|
@@ -325,4 +503,223 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
325
503
|
const hint = wakeCalls[0]!.hint;
|
|
326
504
|
expect(hint).toContain("<\u200B/already_remembered>");
|
|
327
505
|
});
|
|
506
|
+
|
|
507
|
+
test("fork path: persisted instruction is stamped with hidden: true so the UI list serializer drops it", async () => {
|
|
508
|
+
forkFlagEnabled = true;
|
|
509
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
510
|
+
|
|
511
|
+
expect(addMessageCalls).toHaveLength(1);
|
|
512
|
+
expect(addMessageCalls[0]!.conversationId).toBe("fork-conv-1");
|
|
513
|
+
expect(addMessageCalls[0]!.role).toBe("user");
|
|
514
|
+
expect(addMessageCalls[0]!.metadata).toEqual({
|
|
515
|
+
kind: "memory_retrospective_instruction",
|
|
516
|
+
hidden: true,
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
test("fork path: forked retrospective is bucketed as background under the retrospective group", async () => {
|
|
521
|
+
forkFlagEnabled = true;
|
|
522
|
+
const outcome = await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
523
|
+
|
|
524
|
+
expect(outcome.kind).toBe("invoked");
|
|
525
|
+
expect(forkCalls).toHaveLength(1);
|
|
526
|
+
expect(forkCalls[0]!.conversationType).toBe("background");
|
|
527
|
+
expect(forkCalls[0]!.groupId).toBe("system:background");
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
test("fork path: wake opts include suppressWakeSurface so clients don't render an empty wake card on top of the '(Retrospective)' fork", async () => {
|
|
531
|
+
forkFlagEnabled = true;
|
|
532
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
533
|
+
|
|
534
|
+
expect(forkCalls).toHaveLength(1);
|
|
535
|
+
expect(wakeCalls).toHaveLength(1);
|
|
536
|
+
expect(wakeCalls[0]!.conversationId).toBe("fork-conv-1");
|
|
537
|
+
const opts = wakeCalls[0]!.opts;
|
|
538
|
+
expect(opts.suppressWakeSurface).toBe(true);
|
|
539
|
+
// Sanity: the other fork-specific opts the handler relies on are still set.
|
|
540
|
+
expect(opts.skipHintInjection).toBe(true);
|
|
541
|
+
expect(opts.suppressAutoCompaction).toBe(true);
|
|
542
|
+
expect(opts.hintRole).toBe("user");
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test("fork path: fork is pinned to the computed cutoffMessageId so late-arriving messages don't sneak into this run", async () => {
|
|
546
|
+
// Without `throughMessageId`, the fork snapshots the latest source
|
|
547
|
+
// message at fork time. If a new user/assistant turn lands between the
|
|
548
|
+
// slice read and the fork, this run would process the late turn while
|
|
549
|
+
// state advances only to `cutoffMessageId`, causing the next
|
|
550
|
+
// retrospective to reprocess it.
|
|
551
|
+
forkFlagEnabled = true;
|
|
552
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
553
|
+
|
|
554
|
+
expect(forkCalls).toHaveLength(1);
|
|
555
|
+
expect(forkCalls[0]!.throughMessageId).toBe("m3");
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
test("fork path: prior fork-kind retrospective with nested-fork ancestry still surfaces its post-fork remembers in <already_remembered>", async () => {
|
|
559
|
+
// The source conversation was itself a fork. Its assistant messages
|
|
560
|
+
// therefore carry `forkSourceMessageId` values pointing at the
|
|
561
|
+
// ANCESTOR's message ids — not at the new fork's `forkParentMessageId`.
|
|
562
|
+
// The boundary detector must locate the boundary by scanning for the
|
|
563
|
+
// last metadata stamp regardless of value, not by equality against
|
|
564
|
+
// `forkParentMessageId` (which would miss every copied row and lose
|
|
565
|
+
// dedup context).
|
|
566
|
+
forkFlagEnabled = true;
|
|
567
|
+
priorRetroId = "prior-fork-retro-1";
|
|
568
|
+
|
|
569
|
+
// The fork's `forkParentMessageId` is the source conv's tip ("m-src-2"),
|
|
570
|
+
// but the cloned messages preserve ancestor stamps ("m-ancestor-*").
|
|
571
|
+
conversationOverrides[priorRetroId] = {
|
|
572
|
+
source: "memory-retrospective-fork",
|
|
573
|
+
forkParentMessageId: "m-src-2",
|
|
574
|
+
};
|
|
575
|
+
messagesByConversationId[priorRetroId] = [
|
|
576
|
+
// Copied prefix — note metadata stamps point at the ANCESTOR, not
|
|
577
|
+
// `forkParentMessageId`. The old detector would return null here.
|
|
578
|
+
{
|
|
579
|
+
role: "user",
|
|
580
|
+
content: JSON.stringify([{ type: "text", text: "hi" }]),
|
|
581
|
+
createdAt: 1000,
|
|
582
|
+
metadata: JSON.stringify({ forkSourceMessageId: "m-ancestor-1" }),
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
role: "assistant",
|
|
586
|
+
// An inline `remember` from the source conv (should NOT leak into
|
|
587
|
+
// dedup baseline — it's part of the copied prefix, not the post-fork
|
|
588
|
+
// retrospective tail).
|
|
589
|
+
content: JSON.stringify([
|
|
590
|
+
{
|
|
591
|
+
type: "tool_use",
|
|
592
|
+
name: "remember",
|
|
593
|
+
input: { content: "source-inline save — must be excluded" },
|
|
594
|
+
},
|
|
595
|
+
]),
|
|
596
|
+
createdAt: 2000,
|
|
597
|
+
metadata: JSON.stringify({ forkSourceMessageId: "m-ancestor-2" }),
|
|
598
|
+
},
|
|
599
|
+
// Post-fork instruction (no forkSourceMessageId) + the wake's tail
|
|
600
|
+
// assistant turn with the retrospective's own remember call.
|
|
601
|
+
{
|
|
602
|
+
role: "user",
|
|
603
|
+
content: JSON.stringify([
|
|
604
|
+
{ type: "text", text: "Retrospective instruction" },
|
|
605
|
+
]),
|
|
606
|
+
createdAt: 3000,
|
|
607
|
+
metadata: JSON.stringify({
|
|
608
|
+
kind: "memory_retrospective_instruction",
|
|
609
|
+
hidden: true,
|
|
610
|
+
}),
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
role: "assistant",
|
|
614
|
+
content: JSON.stringify([
|
|
615
|
+
{
|
|
616
|
+
type: "tool_use",
|
|
617
|
+
name: "remember",
|
|
618
|
+
input: { content: "retrospective save — must be included" },
|
|
619
|
+
},
|
|
620
|
+
]),
|
|
621
|
+
createdAt: 4000,
|
|
622
|
+
metadata: null,
|
|
623
|
+
},
|
|
624
|
+
];
|
|
625
|
+
|
|
626
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
627
|
+
|
|
628
|
+
// The fork path persists the prompt as a user-role message, not via the
|
|
629
|
+
// wake's hint. Pull the rendered text block out of the persisted JSON.
|
|
630
|
+
expect(addMessageCalls).toHaveLength(1);
|
|
631
|
+
const blocks = JSON.parse(addMessageCalls[0]!.content) as Array<{
|
|
632
|
+
type: string;
|
|
633
|
+
text: string;
|
|
634
|
+
}>;
|
|
635
|
+
const instructionText = blocks[0]!.text;
|
|
636
|
+
expect(instructionText).toContain(
|
|
637
|
+
"- retrospective save — must be included",
|
|
638
|
+
);
|
|
639
|
+
expect(instructionText).not.toContain("source-inline save");
|
|
640
|
+
// Sanity: the "first retrospective" sentinel should not appear — we
|
|
641
|
+
// located dedup context.
|
|
642
|
+
expect(instructionText).not.toContain(
|
|
643
|
+
"(none — this is your first retrospective over this conversation)",
|
|
644
|
+
);
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
test("fork path: prior fork-kind retrospective with no copied messages degrades to empty dedup", async () => {
|
|
648
|
+
// Corrupted/empty fork-kind prior: no message carries
|
|
649
|
+
// `forkSourceMessageId`. The detector should return null and the
|
|
650
|
+
// handler should treat dedup as empty rather than dumping everything
|
|
651
|
+
// (which would leak any pre-fork content into the baseline).
|
|
652
|
+
forkFlagEnabled = true;
|
|
653
|
+
priorRetroId = "prior-fork-retro-2";
|
|
654
|
+
|
|
655
|
+
conversationOverrides[priorRetroId] = {
|
|
656
|
+
source: "memory-retrospective-fork",
|
|
657
|
+
forkParentMessageId: "m-src-2",
|
|
658
|
+
};
|
|
659
|
+
messagesByConversationId[priorRetroId] = [
|
|
660
|
+
{
|
|
661
|
+
role: "assistant",
|
|
662
|
+
content: JSON.stringify([
|
|
663
|
+
{
|
|
664
|
+
type: "tool_use",
|
|
665
|
+
name: "remember",
|
|
666
|
+
input: { content: "would-be-leaked save" },
|
|
667
|
+
},
|
|
668
|
+
]),
|
|
669
|
+
createdAt: 1000,
|
|
670
|
+
metadata: null,
|
|
671
|
+
},
|
|
672
|
+
];
|
|
673
|
+
|
|
674
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
675
|
+
|
|
676
|
+
expect(addMessageCalls).toHaveLength(1);
|
|
677
|
+
const blocks = JSON.parse(addMessageCalls[0]!.content) as Array<{
|
|
678
|
+
type: string;
|
|
679
|
+
text: string;
|
|
680
|
+
}>;
|
|
681
|
+
const instructionText = blocks[0]!.text;
|
|
682
|
+
expect(instructionText).not.toContain("- would-be-leaked save");
|
|
683
|
+
expect(instructionText).toContain(
|
|
684
|
+
"(none — this is your first retrospective over this conversation)",
|
|
685
|
+
);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
test("fork path: prompt anchors review window at first turn_context current_time and disambiguates first-pass vs incremental", async () => {
|
|
689
|
+
forkFlagEnabled = true;
|
|
690
|
+
// Stage a user turn whose content carries a turn_context current_time
|
|
691
|
+
// block — the handler should anchor the prompt at that timestamp.
|
|
692
|
+
newMessages = [
|
|
693
|
+
{
|
|
694
|
+
id: "m1",
|
|
695
|
+
createdAt: Date.parse("2026-05-11T10:00:00Z"),
|
|
696
|
+
role: "user",
|
|
697
|
+
content: JSON.stringify([
|
|
698
|
+
{
|
|
699
|
+
type: "text",
|
|
700
|
+
text: "<turn_context>\ncurrent_time: 2026-05-11T10:00:00-07:00\n</turn_context>\n\nhi",
|
|
701
|
+
},
|
|
702
|
+
]),
|
|
703
|
+
},
|
|
704
|
+
// Wake's response — no turn_context, not used as anchor.
|
|
705
|
+
{
|
|
706
|
+
id: "m2",
|
|
707
|
+
createdAt: Date.parse("2026-05-11T10:05:00Z"),
|
|
708
|
+
role: "assistant",
|
|
709
|
+
content: JSON.stringify([{ type: "text", text: "hello" }]),
|
|
710
|
+
},
|
|
711
|
+
] as Array<{ id: string; createdAt: number } & Record<string, unknown>>;
|
|
712
|
+
|
|
713
|
+
// Incremental run — `lastProcessedMessageId` already set.
|
|
714
|
+
mockState = {
|
|
715
|
+
conversationId: "src-conv-1",
|
|
716
|
+
lastProcessedMessageId: "prev-msg",
|
|
717
|
+
lastRunAt: Date.now() - 60 * 60 * 1000,
|
|
718
|
+
};
|
|
719
|
+
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
720
|
+
|
|
721
|
+
expect(addMessageCalls).toHaveLength(1);
|
|
722
|
+
expect(forkCalls).toHaveLength(1);
|
|
723
|
+
expect(forkCalls[0]!.throughMessageId).toBe("m2");
|
|
724
|
+
});
|
|
328
725
|
});
|