@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
|
@@ -250,7 +250,7 @@ describe("pairDeliveryWithConversation", () => {
|
|
|
250
250
|
// Should NOT be the JSON dump
|
|
251
251
|
expect(contentArg).not.toContain('"raw"');
|
|
252
252
|
// Should be the runtime-composed seed from copy.title/body
|
|
253
|
-
expect(contentArg).toContain("
|
|
253
|
+
expect(contentArg).toContain("Daily standup");
|
|
254
254
|
});
|
|
255
255
|
|
|
256
256
|
test("rejects very short conversationSeedMessage and uses runtime composer", async () => {
|
|
@@ -273,7 +273,7 @@ describe("pairDeliveryWithConversation", () => {
|
|
|
273
273
|
const contentArg = addMessageMock.mock.calls[0]![2] as string;
|
|
274
274
|
expect(contentArg).not.toBe("Hi");
|
|
275
275
|
// Runtime composer builds from copy.title/body
|
|
276
|
-
expect(contentArg).toContain("
|
|
276
|
+
expect(contentArg).toContain("Test reminder");
|
|
277
277
|
});
|
|
278
278
|
|
|
279
279
|
test("passes skipIndexing option to addMessage", async () => {
|
|
@@ -43,7 +43,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
43
43
|
const mockProviderStub = { name: "mock-provider" };
|
|
44
44
|
mock.module("../providers/registry.js", () => ({
|
|
45
45
|
getProvider: () => mockProviderStub,
|
|
46
|
-
initializeProviders: () => {},
|
|
46
|
+
initializeProviders: async () => {},
|
|
47
47
|
listProviders: () => ["anthropic", "openai", "gemini"],
|
|
48
48
|
resolveProviderFromConnection: async () => mockProviderStub,
|
|
49
49
|
}));
|
|
@@ -18,7 +18,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
18
18
|
|
|
19
19
|
mock.module("../providers/registry.js", () => ({
|
|
20
20
|
getProvider: () => ({ name: "mock-provider" }),
|
|
21
|
-
initializeProviders: () => {},
|
|
21
|
+
initializeProviders: async () => {},
|
|
22
22
|
}));
|
|
23
23
|
|
|
24
24
|
mock.module("../config/loader.js", () => ({
|
|
@@ -41,7 +41,7 @@ mock.module("../memory/guardian-action-store.js", () => ({
|
|
|
41
41
|
|
|
42
42
|
mock.module("../providers/registry.js", () => ({
|
|
43
43
|
getProvider: () => ({ name: "mock-provider" }),
|
|
44
|
-
initializeProviders: () => {},
|
|
44
|
+
initializeProviders: async () => {},
|
|
45
45
|
}));
|
|
46
46
|
|
|
47
47
|
mock.module("../config/loader.js", () => ({
|
|
@@ -12,6 +12,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
12
12
|
return {
|
|
13
13
|
...real,
|
|
14
14
|
memory: { ...real.memory, v2: { ...real.memory.v2, enabled: false } },
|
|
15
|
+
slack: { ...real.slack, botUserId: "U_BOT" },
|
|
15
16
|
};
|
|
16
17
|
},
|
|
17
18
|
}));
|
|
@@ -245,7 +246,7 @@ describe("injectChannelCapabilityContext", () => {
|
|
|
245
246
|
expect(text).toContain("CHANNEL CONSTRAINTS");
|
|
246
247
|
expect(text).toContain("Do NOT reference the dashboard UI");
|
|
247
248
|
expect(text).toContain("Do NOT use ui_show");
|
|
248
|
-
expect(text).toContain("
|
|
249
|
+
expect(text).not.toContain("microphone");
|
|
249
250
|
expect(text).toContain("dashboard_capable: false");
|
|
250
251
|
});
|
|
251
252
|
|
|
@@ -337,6 +338,36 @@ describe("injectChannelCapabilityContext", () => {
|
|
|
337
338
|
expect(text).toContain("emoji reactions");
|
|
338
339
|
});
|
|
339
340
|
|
|
341
|
+
test("allows only task_progress ui_show/ui_update guidance for Slack", () => {
|
|
342
|
+
const caps: ChannelCapabilities = {
|
|
343
|
+
channel: "slack",
|
|
344
|
+
dashboardCapable: false,
|
|
345
|
+
supportsDynamicUi: false,
|
|
346
|
+
supportsVoiceInput: false,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
350
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
351
|
+
expect(text).toContain(
|
|
352
|
+
'Only use ui_show/ui_update for card surfaces with template: "task_progress"',
|
|
353
|
+
);
|
|
354
|
+
expect(text).not.toContain("Do NOT use ui_show, ui_update, or app_create");
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("keeps blanket ui_show/ui_update prohibition for other non-dynamic channels", () => {
|
|
358
|
+
const caps: ChannelCapabilities = {
|
|
359
|
+
channel: "phone",
|
|
360
|
+
dashboardCapable: false,
|
|
361
|
+
supportsDynamicUi: false,
|
|
362
|
+
supportsVoiceInput: false,
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const result = injectChannelCapabilityContext(baseUserMessage, caps);
|
|
366
|
+
const text = (result.content[0] as { type: "text"; text: string }).text;
|
|
367
|
+
expect(text).toContain("Do NOT use ui_show, ui_update, or app_create");
|
|
368
|
+
expect(text).not.toContain("Only use ui_show/ui_update");
|
|
369
|
+
});
|
|
370
|
+
|
|
340
371
|
test("still injects for group chats even when all capabilities are true", () => {
|
|
341
372
|
const caps: ChannelCapabilities = {
|
|
342
373
|
channel: "slack",
|
|
@@ -611,7 +642,7 @@ describe("trust-gating via channel capabilities", () => {
|
|
|
611
642
|
});
|
|
612
643
|
|
|
613
644
|
test("non-dashboard channel adds constraint rules preventing UI references", () => {
|
|
614
|
-
const caps = resolveChannelCapabilities("
|
|
645
|
+
const caps = resolveChannelCapabilities("telegram");
|
|
615
646
|
const message: Message = {
|
|
616
647
|
role: "user",
|
|
617
648
|
content: [{ type: "text", text: "Show me a chart" }],
|
|
@@ -624,7 +655,8 @@ describe("trust-gating via channel capabilities", () => {
|
|
|
624
655
|
expect(injected).toContain("Do NOT reference the dashboard UI");
|
|
625
656
|
expect(injected).toContain("Do NOT use ui_show, ui_update, or app_create");
|
|
626
657
|
expect(injected).toContain("Present information as well-formatted text");
|
|
627
|
-
expect(injected).toContain("
|
|
658
|
+
expect(injected).not.toContain("accent color selection");
|
|
659
|
+
expect(injected).not.toContain("complete those steps");
|
|
628
660
|
});
|
|
629
661
|
|
|
630
662
|
test("vellum web interface allows dynamic UI but constrains dashboard references", () => {
|
|
@@ -930,6 +962,28 @@ describe("stripInjectionsForCompaction with NOW.md", () => {
|
|
|
930
962
|
"Hello",
|
|
931
963
|
);
|
|
932
964
|
});
|
|
965
|
+
|
|
966
|
+
test("strips <background_turn> blocks", () => {
|
|
967
|
+
const messages: Message[] = [
|
|
968
|
+
{
|
|
969
|
+
role: "user",
|
|
970
|
+
content: [
|
|
971
|
+
{
|
|
972
|
+
type: "text",
|
|
973
|
+
text: "<background_turn>\nGuardian isn't watching — notify on anything noteworthy.\n</background_turn>",
|
|
974
|
+
},
|
|
975
|
+
{ type: "text", text: "Hello" },
|
|
976
|
+
],
|
|
977
|
+
},
|
|
978
|
+
];
|
|
979
|
+
|
|
980
|
+
const result = stripInjectionsForCompaction(messages);
|
|
981
|
+
expect(result.length).toBe(1);
|
|
982
|
+
expect(result[0].content.length).toBe(1);
|
|
983
|
+
expect((result[0].content[0] as { type: "text"; text: string }).text).toBe(
|
|
984
|
+
"Hello",
|
|
985
|
+
);
|
|
986
|
+
});
|
|
933
987
|
});
|
|
934
988
|
|
|
935
989
|
// ---------------------------------------------------------------------------
|
|
@@ -1228,6 +1282,24 @@ describe("buildUnifiedTurnContextBlock", () => {
|
|
|
1228
1282
|
expect(telegramText).toContain("<no_response/>");
|
|
1229
1283
|
});
|
|
1230
1284
|
|
|
1285
|
+
test("adds task_progress hint only for Slack turns", () => {
|
|
1286
|
+
const slackText = buildUnifiedTurnContextBlock({
|
|
1287
|
+
timestamp: "2026-04-02T12:00:00Z",
|
|
1288
|
+
interfaceName: "slack",
|
|
1289
|
+
channelName: "slack",
|
|
1290
|
+
});
|
|
1291
|
+
const telegramText = buildUnifiedTurnContextBlock({
|
|
1292
|
+
timestamp: "2026-04-02T12:00:00Z",
|
|
1293
|
+
interfaceName: "telegram",
|
|
1294
|
+
channelName: "telegram",
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
expect(slackText).toContain(
|
|
1298
|
+
"if you are going to do work, use task_progress",
|
|
1299
|
+
);
|
|
1300
|
+
expect(telegramText).not.toContain("use task_progress");
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1231
1303
|
test("dedup logic: fields matching canonical_actor_identity are omitted", () => {
|
|
1232
1304
|
const uuid = "vellum-principal-b77e94f5-67c0-4599-8baa-871b925b3da8";
|
|
1233
1305
|
const options: UnifiedTurnContextOptions = {
|
|
@@ -1880,7 +1952,7 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
1880
1952
|
},
|
|
1881
1953
|
];
|
|
1882
1954
|
|
|
1883
|
-
const FLAT_REMINDER = buildPkbReminder([]
|
|
1955
|
+
const FLAT_REMINDER = buildPkbReminder([]);
|
|
1884
1956
|
|
|
1885
1957
|
// Use a platform-agnostic absolute workspace root so the tests work on
|
|
1886
1958
|
// macOS and Linux runners alike. `pkbRoot` sits under `pkbWorkingDir` to
|
|
@@ -2136,7 +2208,7 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2136
2208
|
role: "user",
|
|
2137
2209
|
content: [
|
|
2138
2210
|
{ type: "text", text: "hello" },
|
|
2139
|
-
{ type: "text", text: buildPkbReminder([]
|
|
2211
|
+
{ type: "text", text: buildPkbReminder([]) },
|
|
2140
2212
|
],
|
|
2141
2213
|
};
|
|
2142
2214
|
const hintedMessage: Message = {
|
|
@@ -2145,7 +2217,7 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2145
2217
|
{ type: "text", text: "hello" },
|
|
2146
2218
|
{
|
|
2147
2219
|
type: "text",
|
|
2148
|
-
text: buildPkbReminder(["topics/alpha.md", "topics/beta.md"]
|
|
2220
|
+
text: buildPkbReminder(["topics/alpha.md", "topics/beta.md"]),
|
|
2149
2221
|
},
|
|
2150
2222
|
],
|
|
2151
2223
|
};
|
|
@@ -2273,7 +2345,6 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2273
2345
|
const T1 = "1700000010.000002"; // top-level message starting thread B
|
|
2274
2346
|
const T2 = "1700000030.000003"; // newer top-level message
|
|
2275
2347
|
const ALIAS_T0 = parentAlias(T0);
|
|
2276
|
-
const ALIAS_T1 = parentAlias(T1);
|
|
2277
2348
|
const ALIAS_T2 = parentAlias(T2);
|
|
2278
2349
|
|
|
2279
2350
|
const SLACK_CHANNEL_ID = "C0123CHANNEL";
|
|
@@ -2468,16 +2539,16 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2468
2539
|
expect(lines[2]).toContain("Reply in thread B");
|
|
2469
2540
|
expect(lines[3]).toContain("Reply in thread A");
|
|
2470
2541
|
// Cross-thread visibility: thread B's reply is in the rendered output
|
|
2471
|
-
// alongside thread A's reply.
|
|
2472
|
-
expect(lines[2]).toContain(
|
|
2473
|
-
expect(lines[3]).toContain(
|
|
2542
|
+
// alongside thread A's reply, without parent-arrow prefixes.
|
|
2543
|
+
expect(lines[2]).not.toContain("→ M");
|
|
2544
|
+
expect(lines[3]).not.toContain("→ M");
|
|
2474
2545
|
// Sender labels appear.
|
|
2475
2546
|
expect(lines[0]).toContain("alice");
|
|
2476
2547
|
expect(lines[1]).toContain("bob");
|
|
2477
2548
|
});
|
|
2478
2549
|
|
|
2479
2550
|
// ── Scenario 2: reply to a top-level (starts new thread) ─────────────
|
|
2480
|
-
test("scenario 2 — reply to top-level renders
|
|
2551
|
+
test("scenario 2 — reply to top-level renders without parent arrow", async () => {
|
|
2481
2552
|
const rows: MessageRow[] = [
|
|
2482
2553
|
userRow({
|
|
2483
2554
|
id: "m1",
|
|
@@ -2501,15 +2572,13 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2501
2572
|
const lines = texts(result);
|
|
2502
2573
|
|
|
2503
2574
|
expect(lines.length).toBe(2);
|
|
2504
|
-
// Top-level has no thread tag.
|
|
2505
2575
|
expect(lines[0]).not.toContain("→ M");
|
|
2506
|
-
|
|
2507
|
-
expect(lines[1]).toContain(`→ ${ALIAS_T0}`);
|
|
2576
|
+
expect(lines[1]).not.toContain("→ M");
|
|
2508
2577
|
expect(lines[1]).toContain("Reply that starts a new thread");
|
|
2509
2578
|
});
|
|
2510
2579
|
|
|
2511
2580
|
// ── Scenario 3: reply to the most-recent top-level message ───────────
|
|
2512
|
-
test("scenario 3 — reply to last top-level still renders
|
|
2581
|
+
test("scenario 3 — reply to last top-level still renders chronologically", async () => {
|
|
2513
2582
|
const rows: MessageRow[] = [
|
|
2514
2583
|
userRow({
|
|
2515
2584
|
id: "m1",
|
|
@@ -2539,13 +2608,12 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2539
2608
|
const lines = texts(result);
|
|
2540
2609
|
|
|
2541
2610
|
expect(lines.length).toBe(3);
|
|
2542
|
-
|
|
2543
|
-
expect(lines[2]).toContain(
|
|
2544
|
-
expect(lines[2]).not.toContain(`→ ${ALIAS_T0}`);
|
|
2611
|
+
expect(lines[2]).toContain("Reply to the newer top-level");
|
|
2612
|
+
expect(lines[2]).not.toContain("→ M");
|
|
2545
2613
|
});
|
|
2546
2614
|
|
|
2547
2615
|
// ── Scenario 4: brand-new top-level message ──────────────────────────
|
|
2548
|
-
test("scenario 4 — new top-level message has no
|
|
2616
|
+
test("scenario 4 — new top-level message has no parent arrow", async () => {
|
|
2549
2617
|
const rows: MessageRow[] = [
|
|
2550
2618
|
userRow({
|
|
2551
2619
|
id: "m1",
|
|
@@ -2565,7 +2633,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2565
2633
|
const lines = texts(result);
|
|
2566
2634
|
|
|
2567
2635
|
expect(lines.length).toBe(2);
|
|
2568
|
-
// Both lines render without a
|
|
2636
|
+
// Both lines render without a parent arrow — they are siblings, not
|
|
2569
2637
|
// members of the same thread.
|
|
2570
2638
|
expect(lines[0]).not.toContain("→ M");
|
|
2571
2639
|
expect(lines[1]).not.toContain("→ M");
|
|
@@ -2580,9 +2648,9 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2580
2648
|
// ── Scenario 5: legacy mixed with post-upgrade rows ──────────────────
|
|
2581
2649
|
// Pre-upgrade rows have no `slackMeta` sub-key. Post-upgrade rows have
|
|
2582
2650
|
// it. Both kinds must appear in the rendered transcript with legacy
|
|
2583
|
-
// rows
|
|
2584
|
-
//
|
|
2585
|
-
//
|
|
2651
|
+
// rows and post-upgrade rows both rendered without parent-arrow prefixes.
|
|
2652
|
+
// The renderer's chronological sort must intermix them on the appropriate
|
|
2653
|
+
// timeline.
|
|
2586
2654
|
test("scenario 5 — legacy rows mixed with post-upgrade rows render chronologically", async () => {
|
|
2587
2655
|
const rows: MessageRow[] = [
|
|
2588
2656
|
// Legacy user row with a displayName hint only — no slackMeta.
|
|
@@ -2599,8 +2667,7 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2599
2667
|
text: "Legacy assistant reply",
|
|
2600
2668
|
}),
|
|
2601
2669
|
// Post-upgrade row anchored to a thread parent that has no record
|
|
2602
|
-
// in storage (legacy parent)
|
|
2603
|
-
// because the metadata is intact.
|
|
2670
|
+
// in storage (legacy parent).
|
|
2604
2671
|
userRow({
|
|
2605
2672
|
id: "m3",
|
|
2606
2673
|
createdAt: 1700000000_000,
|
|
@@ -2623,11 +2690,9 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2623
2690
|
expect(lines[0]).toContain("Legacy user message");
|
|
2624
2691
|
expect(lines[1]).toContain("Legacy assistant reply");
|
|
2625
2692
|
expect(lines[2]).toContain("Post-upgrade thread reply");
|
|
2626
|
-
// Legacy rows render flat — no thread tag arrow.
|
|
2627
2693
|
expect(lines[0]).not.toContain("→ M");
|
|
2628
2694
|
expect(lines[1]).not.toContain("→ M");
|
|
2629
|
-
|
|
2630
|
-
expect(lines[2]).toContain(`→ ${ALIAS_T0}`);
|
|
2695
|
+
expect(lines[2]).not.toContain("→ M");
|
|
2631
2696
|
// Sender labels: legacy rows carry no structured displayName, and the
|
|
2632
2697
|
// role slot already conveys user-vs-assistant identity, so the row
|
|
2633
2698
|
// mapper emits `null` senderLabel and the renderer omits the label
|
|
@@ -2945,37 +3010,6 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2945
3010
|
expect(allText).not.toContain("dm context");
|
|
2946
3011
|
});
|
|
2947
3012
|
|
|
2948
|
-
test("slack late-join notice is model-facing and non-persisted", async () => {
|
|
2949
|
-
const slackChannelCaps: ChannelCapabilities = {
|
|
2950
|
-
channel: "slack",
|
|
2951
|
-
dashboardCapable: false,
|
|
2952
|
-
supportsDynamicUi: false,
|
|
2953
|
-
supportsVoiceInput: false,
|
|
2954
|
-
chatType: "channel",
|
|
2955
|
-
};
|
|
2956
|
-
const notice =
|
|
2957
|
-
"Slack context note: this turn joined an existing thread. 3 earlier thread messages were backfilled before the current message.";
|
|
2958
|
-
|
|
2959
|
-
const { messages: result, blocks } = await applyRuntimeInjections(
|
|
2960
|
-
[{ role: "user", content: [{ type: "text", text: "current turn" }] }],
|
|
2961
|
-
{
|
|
2962
|
-
channelCapabilities: slackChannelCaps,
|
|
2963
|
-
slackRuntimeContextNotice: notice,
|
|
2964
|
-
transportHints: [notice],
|
|
2965
|
-
},
|
|
2966
|
-
);
|
|
2967
|
-
|
|
2968
|
-
const allText = result
|
|
2969
|
-
.flatMap((m) => m.content)
|
|
2970
|
-
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
2971
|
-
.map((b) => b.text)
|
|
2972
|
-
.join("\n");
|
|
2973
|
-
expect(allText).toContain("<slack_context_notice>");
|
|
2974
|
-
expect(allText).toContain(notice);
|
|
2975
|
-
expect(allText).not.toContain("<transport_hints>");
|
|
2976
|
-
expect(JSON.stringify(blocks)).not.toContain(notice);
|
|
2977
|
-
});
|
|
2978
|
-
|
|
2979
3013
|
// ── transport_hints kept for non-slack channels ───────────────────────
|
|
2980
3014
|
test("non-slack conversations still receive <transport_hints>", async () => {
|
|
2981
3015
|
const { messages: result } = await applyRuntimeInjections(
|
|
@@ -3001,10 +3035,9 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3001
3035
|
});
|
|
3002
3036
|
|
|
3003
3037
|
// ── trust-filter regression for loadSlackChronologicalMessages ───────
|
|
3004
|
-
// For untrusted actors,
|
|
3005
|
-
//
|
|
3006
|
-
|
|
3007
|
-
test("loadSlackChronologicalMessages filters guardian-scoped rows for untrusted actors", () => {
|
|
3038
|
+
// For untrusted actors, Slack-sourced rows are still shared channel/thread
|
|
3039
|
+
// context, while non-Slack guardian-scoped rows remain private.
|
|
3040
|
+
test("loadSlackChronologicalMessages keeps Slack-visible guardian rows for untrusted actors", () => {
|
|
3008
3041
|
const caps: ChannelCapabilities = {
|
|
3009
3042
|
channel: "slack",
|
|
3010
3043
|
dashboardCapable: false,
|
|
@@ -3012,14 +3045,15 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3012
3045
|
supportsVoiceInput: false,
|
|
3013
3046
|
chatType: "channel",
|
|
3014
3047
|
};
|
|
3015
|
-
// Row 1 has no provenance → guardian-scoped (filtered out).
|
|
3016
|
-
// Row 2 has provenance.trustClass === "trusted_contact" (kept).
|
|
3017
3048
|
const rows: MessageRow[] = [
|
|
3018
3049
|
userRow({
|
|
3019
3050
|
id: "m1",
|
|
3020
3051
|
createdAt: 1700000000_000,
|
|
3021
|
-
text: "guardian
|
|
3052
|
+
text: "public guardian instruction",
|
|
3022
3053
|
slackMeta: buildSlackMeta({ channelTs: T0, displayName: "alice" }),
|
|
3054
|
+
extraOuterMetadata: {
|
|
3055
|
+
provenanceTrustClass: "guardian",
|
|
3056
|
+
},
|
|
3023
3057
|
}),
|
|
3024
3058
|
userRow({
|
|
3025
3059
|
id: "m2",
|
|
@@ -3030,6 +3064,14 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3030
3064
|
provenanceTrustClass: "trusted_contact",
|
|
3031
3065
|
},
|
|
3032
3066
|
}),
|
|
3067
|
+
userRow({
|
|
3068
|
+
id: "m3",
|
|
3069
|
+
createdAt: 1700000020_000,
|
|
3070
|
+
text: "private guardian-only context",
|
|
3071
|
+
extraOuterMetadata: {
|
|
3072
|
+
provenanceTrustClass: "guardian",
|
|
3073
|
+
},
|
|
3074
|
+
}),
|
|
3033
3075
|
];
|
|
3034
3076
|
const result = loadSlackChronologicalMessages("conv-1", caps, {
|
|
3035
3077
|
loader: () => rows,
|
|
@@ -3041,8 +3083,9 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3041
3083
|
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
3042
3084
|
.map((b) => b.text)
|
|
3043
3085
|
.join("\n");
|
|
3044
|
-
expect(allText).
|
|
3086
|
+
expect(allText).toContain("public guardian instruction");
|
|
3045
3087
|
expect(allText).toContain("from untrusted actor");
|
|
3088
|
+
expect(allText).not.toContain("private guardian-only context");
|
|
3046
3089
|
});
|
|
3047
3090
|
|
|
3048
3091
|
test("loadSlackChronologicalContext preserves summary and filters by Slack watermark", () => {
|
|
@@ -3360,15 +3403,14 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3360
3403
|
expect(focusBlock).not.toBeNull();
|
|
3361
3404
|
expect(focusBlock!).toContain("<active_thread>");
|
|
3362
3405
|
expect(focusBlock!).toContain("</active_thread>");
|
|
3363
|
-
// Parent (T0) is included
|
|
3406
|
+
// Parent (T0) is included by content.
|
|
3364
3407
|
expect(focusBlock!).toContain("Top-level in thread A");
|
|
3365
3408
|
// The new reply is included.
|
|
3366
3409
|
expect(focusBlock!).toContain("New reply in thread A");
|
|
3367
|
-
expect(focusBlock!).toContain(
|
|
3410
|
+
expect(focusBlock!).not.toContain("→ M");
|
|
3368
3411
|
// Thread B's content is NOT in the focus block.
|
|
3369
3412
|
expect(focusBlock!).not.toContain("Top-level in thread B");
|
|
3370
3413
|
expect(focusBlock!).not.toContain("Cross-thread reply in B");
|
|
3371
|
-
expect(focusBlock!).not.toContain(`→ ${ALIAS_T1}`);
|
|
3372
3414
|
|
|
3373
3415
|
// The focus block is appended to the FINAL user message as a tail
|
|
3374
3416
|
// text block — not to any earlier message.
|
|
@@ -3966,13 +4008,97 @@ describe("assembleSlackActiveThreadFocusBlock", () => {
|
|
|
3966
4008
|
expect(result!).toContain("@assistant: Assistant reply");
|
|
3967
4009
|
});
|
|
3968
4010
|
|
|
4011
|
+
test("timezone-aware assistant rows keep renderer attribution in active-thread focus block", () => {
|
|
4012
|
+
const rows: SlackTranscriptInputRow[] = [
|
|
4013
|
+
buildRow(
|
|
4014
|
+
"user",
|
|
4015
|
+
"Parent",
|
|
4016
|
+
1_000,
|
|
4017
|
+
buildMeta({
|
|
4018
|
+
channelTs: PARENT_TS,
|
|
4019
|
+
displayName: "aaron",
|
|
4020
|
+
timestampTimezone: "America/Denver",
|
|
4021
|
+
timestampTimezoneLabel: "MT",
|
|
4022
|
+
}),
|
|
4023
|
+
),
|
|
4024
|
+
buildRow(
|
|
4025
|
+
"assistant",
|
|
4026
|
+
"Assistant reply",
|
|
4027
|
+
2_000,
|
|
4028
|
+
buildMeta({
|
|
4029
|
+
channelTs: "1700000005.000001",
|
|
4030
|
+
threadTs: PARENT_TS,
|
|
4031
|
+
timestampTimezone: "America/Denver",
|
|
4032
|
+
timestampTimezoneLabel: "MT",
|
|
4033
|
+
speakerTimezoneLabel: "ET",
|
|
4034
|
+
}),
|
|
4035
|
+
),
|
|
4036
|
+
buildRow(
|
|
4037
|
+
"user",
|
|
4038
|
+
"Follow-up",
|
|
4039
|
+
3_000,
|
|
4040
|
+
buildMeta({
|
|
4041
|
+
channelTs: REPLY_TS,
|
|
4042
|
+
threadTs: PARENT_TS,
|
|
4043
|
+
displayName: "aaron",
|
|
4044
|
+
timestampTimezone: "America/Denver",
|
|
4045
|
+
timestampTimezoneLabel: "MT",
|
|
4046
|
+
}),
|
|
4047
|
+
),
|
|
4048
|
+
];
|
|
4049
|
+
|
|
4050
|
+
const result = assembleSlackActiveThreadFocusBlock(rows, SLACK_CAPS);
|
|
4051
|
+
expect(result).not.toBeNull();
|
|
4052
|
+
expect(result!).toContain(
|
|
4053
|
+
"[nov 14 2023 3:13 PM MT assistant] Assistant reply",
|
|
4054
|
+
);
|
|
4055
|
+
expect(result!).not.toContain(
|
|
4056
|
+
"@assistant: [nov 14 2023 3:13 PM MT assistant]",
|
|
4057
|
+
);
|
|
4058
|
+
});
|
|
4059
|
+
|
|
4060
|
+
test("assistant content that only looks like a compact tag still gets active-thread attribution", () => {
|
|
4061
|
+
const compactLookingContent =
|
|
4062
|
+
"[nov 14 2023 3:13 PM MT assistant] Assistant reply";
|
|
4063
|
+
const rows: SlackTranscriptInputRow[] = [
|
|
4064
|
+
buildRow(
|
|
4065
|
+
"user",
|
|
4066
|
+
"Parent",
|
|
4067
|
+
1_000,
|
|
4068
|
+
buildMeta({ channelTs: PARENT_TS, displayName: "@alice" }),
|
|
4069
|
+
),
|
|
4070
|
+
buildRow(
|
|
4071
|
+
"assistant",
|
|
4072
|
+
compactLookingContent,
|
|
4073
|
+
2_000,
|
|
4074
|
+
buildMeta({
|
|
4075
|
+
channelTs: "1700000005.000001",
|
|
4076
|
+
threadTs: PARENT_TS,
|
|
4077
|
+
}),
|
|
4078
|
+
),
|
|
4079
|
+
buildRow(
|
|
4080
|
+
"user",
|
|
4081
|
+
"Follow-up",
|
|
4082
|
+
3_000,
|
|
4083
|
+
buildMeta({
|
|
4084
|
+
channelTs: REPLY_TS,
|
|
4085
|
+
threadTs: PARENT_TS,
|
|
4086
|
+
displayName: "@alice",
|
|
4087
|
+
}),
|
|
4088
|
+
),
|
|
4089
|
+
];
|
|
4090
|
+
|
|
4091
|
+
const result = assembleSlackActiveThreadFocusBlock(rows, SLACK_CAPS);
|
|
4092
|
+
expect(result).not.toBeNull();
|
|
4093
|
+
expect(result!).toContain(`@assistant: ${compactLookingContent}`);
|
|
4094
|
+
});
|
|
4095
|
+
|
|
3969
4096
|
test("assistant reaction overflow trailer is not double-attributed", () => {
|
|
3970
4097
|
// When assistant reactions overflow the per-target cap, `renderSlackTranscript`
|
|
3971
4098
|
// emits a trailer line (`[…and N more reactions to Mxxxxxx]`) whose role
|
|
3972
|
-
// is inherited from the first overflowing reaction — i.e. `assistant`.
|
|
3973
|
-
//
|
|
3974
|
-
//
|
|
3975
|
-
// be detected by `isReactionTagLine` and skipped by the prefix step.
|
|
4099
|
+
// is inherited from the first overflowing reaction — i.e. `assistant`.
|
|
4100
|
+
// Renderer provenance marks it as a Slack reaction line so the flattened
|
|
4101
|
+
// active-thread block does not add a content-message prefix.
|
|
3976
4102
|
const PARENT_ALIAS_TS = PARENT_TS;
|
|
3977
4103
|
const buildAssistantReaction = (ts: string, emoji: string) =>
|
|
3978
4104
|
buildRow(
|
|
@@ -4057,6 +4183,7 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
4057
4183
|
// Anchor times mirror the renderer's HH:MM (UTC) output.
|
|
4058
4184
|
// 14:25:00 UTC on 2023-11-14 = epoch second 1699971900.
|
|
4059
4185
|
const TS_14_25 = "1699971900.000100"; // 14:25 UTC
|
|
4186
|
+
const TS_14_26 = "1699971960.000200"; // 14:26 UTC
|
|
4060
4187
|
const TS_14_28 = "1699972080.000300"; // 14:28 UTC
|
|
4061
4188
|
const MS_14_25 = 1699971900_000;
|
|
4062
4189
|
const MS_14_26 = 1699971960_000;
|
|
@@ -4217,6 +4344,85 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
4217
4344
|
}
|
|
4218
4345
|
});
|
|
4219
4346
|
|
|
4347
|
+
test("expanded Slack timezone metadata remains renderable", () => {
|
|
4348
|
+
const userMeta: SlackMessageMetadata = {
|
|
4349
|
+
source: "slack",
|
|
4350
|
+
channelId: DM_CHANNEL_ID,
|
|
4351
|
+
channelTs: TS_14_25,
|
|
4352
|
+
eventKind: "message",
|
|
4353
|
+
displayName: "@alice",
|
|
4354
|
+
actorTimezone: "America/New_York",
|
|
4355
|
+
actorTimezoneLabel: "ET",
|
|
4356
|
+
actorTimezoneOffsetSeconds: -18000,
|
|
4357
|
+
timestampTimezone: "America/New_York",
|
|
4358
|
+
timestampTimezoneLabel: "ET",
|
|
4359
|
+
speakerTimezoneLabel: "ET",
|
|
4360
|
+
};
|
|
4361
|
+
const rows: SlackTranscriptInputRow[] = [
|
|
4362
|
+
row("user", "timezone-aware hello", MS_14_25, metadataEnvelope(userMeta)),
|
|
4363
|
+
];
|
|
4364
|
+
|
|
4365
|
+
const result = assembleSlackChronologicalMessages(rows, DM_CAPS);
|
|
4366
|
+
expect(result).not.toBeNull();
|
|
4367
|
+
expect(result!.map((m) => (m.content[0] as { text: string }).text)).toEqual(
|
|
4368
|
+
[
|
|
4369
|
+
`[nov 14 2023 9:25 AM ET @alice (ET)] ${slackExternal(
|
|
4370
|
+
"timezone-aware hello",
|
|
4371
|
+
"@alice",
|
|
4372
|
+
)}`,
|
|
4373
|
+
],
|
|
4374
|
+
);
|
|
4375
|
+
});
|
|
4376
|
+
|
|
4377
|
+
test("Slack context skips configured assistant new-thread placeholder rows", () => {
|
|
4378
|
+
const placeholderMeta: SlackMessageMetadata = {
|
|
4379
|
+
source: "slack",
|
|
4380
|
+
channelId: DM_CHANNEL_ID,
|
|
4381
|
+
channelTs: TS_14_25,
|
|
4382
|
+
eventKind: "message",
|
|
4383
|
+
displayName: "Ada",
|
|
4384
|
+
actorExternalUserId: "U_BOT",
|
|
4385
|
+
};
|
|
4386
|
+
const otherBotMeta: SlackMessageMetadata = {
|
|
4387
|
+
source: "slack",
|
|
4388
|
+
channelId: DM_CHANNEL_ID,
|
|
4389
|
+
channelTs: TS_14_26,
|
|
4390
|
+
eventKind: "message",
|
|
4391
|
+
displayName: "Build Bot",
|
|
4392
|
+
actorExternalUserId: "B_OTHER",
|
|
4393
|
+
};
|
|
4394
|
+
const realBotMeta: SlackMessageMetadata = {
|
|
4395
|
+
source: "slack",
|
|
4396
|
+
channelId: DM_CHANNEL_ID,
|
|
4397
|
+
channelTs: TS_14_28,
|
|
4398
|
+
eventKind: "message",
|
|
4399
|
+
actorExternalUserId: "B_ASSISTANT",
|
|
4400
|
+
};
|
|
4401
|
+
const rows: SlackTranscriptInputRow[] = [
|
|
4402
|
+
row(
|
|
4403
|
+
"user",
|
|
4404
|
+
"New Assistant Thread",
|
|
4405
|
+
MS_14_25,
|
|
4406
|
+
metadataEnvelope(placeholderMeta),
|
|
4407
|
+
),
|
|
4408
|
+
row(
|
|
4409
|
+
"user",
|
|
4410
|
+
"New Assistant Thread",
|
|
4411
|
+
MS_14_26,
|
|
4412
|
+
metadataEnvelope(otherBotMeta),
|
|
4413
|
+
),
|
|
4414
|
+
row("user", "real bot context", MS_14_28, metadataEnvelope(realBotMeta)),
|
|
4415
|
+
];
|
|
4416
|
+
|
|
4417
|
+
const result = assembleSlackChronologicalMessages(rows, DM_CAPS);
|
|
4418
|
+
expect(result).not.toBeNull();
|
|
4419
|
+
const rendered = JSON.stringify(result);
|
|
4420
|
+
expect(rendered).not.toContain("Ada");
|
|
4421
|
+
expect(rendered.split("New Assistant Thread").length - 1).toBe(1);
|
|
4422
|
+
expect(rendered).toContain("Build Bot");
|
|
4423
|
+
expect(rendered).toContain("real bot context");
|
|
4424
|
+
});
|
|
4425
|
+
|
|
4220
4426
|
test("legacy-DM fixture: pre-upgrade rows (no slackMeta) interleave with post-upgrade rows", () => {
|
|
4221
4427
|
// Mix:
|
|
4222
4428
|
// - Two pre-upgrade rows (created before PR 16 wired slackMeta into
|
|
@@ -4430,17 +4636,16 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
4430
4636
|
});
|
|
4431
4637
|
});
|
|
4432
4638
|
|
|
4433
|
-
test("post-reconciliation: assistant rows with channelTs participate in
|
|
4639
|
+
test("post-reconciliation: assistant rows with channelTs participate in chronological rendering", () => {
|
|
4434
4640
|
// Once `deliverReplyViaCallback` reconciles `channelTs` from the
|
|
4435
4641
|
// gateway's response, assistant rows carry a fully-formed slackMeta
|
|
4436
4642
|
// envelope. They must then render through the Slack chronological
|
|
4437
4643
|
// path (not the legacy fallback) so reply rows pointing at the
|
|
4438
|
-
// assistant's prior message
|
|
4644
|
+
// assistant's prior message appear in Slack timestamp order.
|
|
4439
4645
|
//
|
|
4440
4646
|
// This is the cross-thread visibility that the slack-thread-aware-
|
|
4441
4647
|
// context plan promises: a follow-up user reply to the assistant's
|
|
4442
|
-
// earlier post should render
|
|
4443
|
-
// can use to reason about which prior assistant message it threads off.
|
|
4648
|
+
// earlier post should render alongside the prior assistant row.
|
|
4444
4649
|
const SLACK_CHANNEL_ID_2 = "C0THREAD";
|
|
4445
4650
|
const ASSISTANT_TS = "1700001000.000111";
|
|
4446
4651
|
const REPLY_TS = "1700001020.000222";
|
|
@@ -4485,13 +4690,13 @@ describe("assembleSlackChronologicalMessages", () => {
|
|
|
4485
4690
|
expect(result).not.toBeNull();
|
|
4486
4691
|
expect(result!.length).toBe(2);
|
|
4487
4692
|
|
|
4488
|
-
// The user follow-up
|
|
4489
|
-
//
|
|
4490
|
-
// assistant row was treated as legacy/null-metadata and excluded from
|
|
4491
|
-
// alias issuance — the user reply rendered without the arrow.
|
|
4693
|
+
// The user follow-up keeps timestamp/sender attribution without carrying
|
|
4694
|
+
// the old parent-alias arrow.
|
|
4492
4695
|
const replyText = (result![1].content[0] as { text: string }).text;
|
|
4493
|
-
expect(replyText).
|
|
4494
|
-
|
|
4696
|
+
expect(replyText).toBe(
|
|
4697
|
+
`[11/14/23 22:30 @alice]: ${slackExternal("Following up", "@alice")}`,
|
|
4698
|
+
);
|
|
4699
|
+
expect(replyText).not.toContain("→ M");
|
|
4495
4700
|
});
|
|
4496
4701
|
|
|
4497
4702
|
test("post-reconciliation: assistant row appears in active-thread focus block", () => {
|
|
@@ -4827,7 +5032,7 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
|
|
|
4827
5032
|
mode: "full",
|
|
4828
5033
|
});
|
|
4829
5034
|
|
|
4830
|
-
const expected = buildPkbReminder([]
|
|
5035
|
+
const expected = buildPkbReminder([]);
|
|
4831
5036
|
expect(blocks.pkbSystemReminder).toBe(expected);
|
|
4832
5037
|
});
|
|
4833
5038
|
|