@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
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route handlers for conversation messages and suggestions.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
existsSync,
|
|
6
|
-
readdirSync,
|
|
7
|
-
readFileSync,
|
|
8
|
-
statSync,
|
|
9
|
-
writeFileSync,
|
|
10
|
-
} from "node:fs";
|
|
11
|
-
import { join, relative } from "node:path";
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
5
|
|
|
13
6
|
import { z } from "zod";
|
|
14
7
|
|
|
@@ -31,6 +24,7 @@ import { createApprovalConversationGenerator } from "../../daemon/approval-gener
|
|
|
31
24
|
import type { Conversation } from "../../daemon/conversation.js";
|
|
32
25
|
import {
|
|
33
26
|
buildModelInfoEvent,
|
|
27
|
+
formatCleanResult,
|
|
34
28
|
formatCompactResult,
|
|
35
29
|
isModelSlashCommand,
|
|
36
30
|
} from "../../daemon/conversation-process.js";
|
|
@@ -41,6 +35,7 @@ import {
|
|
|
41
35
|
import { getOrCreateConversation as getOrCreateConversationInstance } from "../../daemon/conversation-store.js";
|
|
42
36
|
import { canonicalizeTimeZone } from "../../daemon/date-context.js";
|
|
43
37
|
import {
|
|
38
|
+
buildScanFirstMessage,
|
|
44
39
|
getCannedFirstGreeting,
|
|
45
40
|
isWakeUpGreeting,
|
|
46
41
|
} from "../../daemon/first-greeting.js";
|
|
@@ -51,6 +46,7 @@ import {
|
|
|
51
46
|
preactivateHostProxySkills,
|
|
52
47
|
shouldAttachHostProxyForCapability,
|
|
53
48
|
} from "../../daemon/host-proxy-preactivation.js";
|
|
49
|
+
import { getAssistantName } from "../../daemon/identity-helpers.js";
|
|
54
50
|
import type { ServerMessage } from "../../daemon/message-protocol.js";
|
|
55
51
|
import type {
|
|
56
52
|
HostProxyTransportMetadata,
|
|
@@ -75,7 +71,7 @@ import {
|
|
|
75
71
|
} from "../../memory/canonical-guardian-store.js";
|
|
76
72
|
import {
|
|
77
73
|
addMessage,
|
|
78
|
-
|
|
74
|
+
getConversation,
|
|
79
75
|
getMessages,
|
|
80
76
|
getMessagesPaginated,
|
|
81
77
|
hasMessages,
|
|
@@ -101,11 +97,9 @@ import { writeOnboardingSection } from "../../prompts/persona-resolver.js";
|
|
|
101
97
|
import { getConfiguredProvider } from "../../providers/provider-send-message.js";
|
|
102
98
|
import type { Provider } from "../../providers/types.js";
|
|
103
99
|
import { checkIngressForSecrets } from "../../security/secret-ingress.js";
|
|
100
|
+
import { getSubagentManager } from "../../subagent/index.js";
|
|
104
101
|
import { getLogger } from "../../util/logger.js";
|
|
105
|
-
import {
|
|
106
|
-
getInterfacesDir,
|
|
107
|
-
getWorkspacePromptPath,
|
|
108
|
-
} from "../../util/platform.js";
|
|
102
|
+
import { getWorkspacePromptPath } from "../../util/platform.js";
|
|
109
103
|
import { silentlyWithLog } from "../../util/silently.js";
|
|
110
104
|
import { assistantEventHub, broadcastMessage } from "../assistant-event-hub.js";
|
|
111
105
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
@@ -135,6 +129,7 @@ const log = getLogger("conversation-routes");
|
|
|
135
129
|
|
|
136
130
|
/** Matches the `<no_response/>` sentinel used by channel delivery suppression. */
|
|
137
131
|
const NO_RESPONSE_INLINE_RE = /<no_response\s*\/?>/g;
|
|
132
|
+
const ATTACHMENT_ENTRY_RE = /^attachment:(\d+)$/;
|
|
138
133
|
|
|
139
134
|
const SUGGESTION_CACHE_MAX = 100;
|
|
140
135
|
const VALID_RISK_THRESHOLDS = ["none", "low", "medium", "high"] as const;
|
|
@@ -147,31 +142,67 @@ function isValidRiskThreshold(value: unknown): value is RiskThreshold {
|
|
|
147
142
|
);
|
|
148
143
|
}
|
|
149
144
|
|
|
145
|
+
/**
|
|
146
|
+
* True when a message's persisted metadata explicitly flags it as hidden.
|
|
147
|
+
* Used to suppress internal scaffolding messages from UI history while
|
|
148
|
+
* leaving them in the LLM-side context.
|
|
149
|
+
*/
|
|
150
|
+
function isHiddenMessage(metadata: string | null): boolean {
|
|
151
|
+
if (!metadata) return false;
|
|
152
|
+
try {
|
|
153
|
+
const meta = JSON.parse(metadata) as { hidden?: unknown };
|
|
154
|
+
return meta?.hidden === true;
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
150
160
|
function buildSlackHistoryMessage(
|
|
151
161
|
slackMeta: SlackMessageMetadata | null,
|
|
162
|
+
opts?: { role?: string; assistantDisplayName?: string },
|
|
152
163
|
): RuntimeMessagePayload["slackMessage"] | undefined {
|
|
153
164
|
if (!slackMeta) return undefined;
|
|
154
165
|
|
|
155
166
|
const slackConfig = getConfig().slack;
|
|
167
|
+
const replyThreadTs =
|
|
168
|
+
slackMeta.threadTs && slackMeta.threadTs !== slackMeta.channelTs
|
|
169
|
+
? slackMeta.threadTs
|
|
170
|
+
: undefined;
|
|
156
171
|
const messageLink = buildSlackMessageDeepLinks({
|
|
157
172
|
teamId: slackConfig?.teamId,
|
|
158
173
|
teamUrl: slackConfig?.teamUrl,
|
|
159
174
|
channelId: slackMeta.channelId,
|
|
160
175
|
messageTs: slackMeta.channelTs,
|
|
176
|
+
...(replyThreadTs ? { threadTs: replyThreadTs } : {}),
|
|
161
177
|
});
|
|
162
|
-
const threadLink =
|
|
178
|
+
const threadLink = replyThreadTs
|
|
163
179
|
? buildSlackMessageDeepLinks({
|
|
164
180
|
teamId: slackConfig?.teamId,
|
|
165
181
|
teamUrl: slackConfig?.teamUrl,
|
|
166
182
|
channelId: slackMeta.channelId,
|
|
167
|
-
messageTs:
|
|
183
|
+
messageTs: replyThreadTs,
|
|
168
184
|
})
|
|
169
185
|
: undefined;
|
|
186
|
+
const assistantDisplayName =
|
|
187
|
+
opts?.role === "assistant" ? opts.assistantDisplayName : undefined;
|
|
188
|
+
const senderDisplayName =
|
|
189
|
+
slackMeta.displayName?.trim() || assistantDisplayName;
|
|
170
190
|
|
|
171
191
|
return {
|
|
172
192
|
channelId: slackMeta.channelId,
|
|
193
|
+
...(slackMeta.channelName ? { channelName: slackMeta.channelName } : {}),
|
|
173
194
|
channelTs: slackMeta.channelTs,
|
|
174
195
|
...(slackMeta.threadTs ? { threadTs: slackMeta.threadTs } : {}),
|
|
196
|
+
...(senderDisplayName || slackMeta.actorExternalUserId
|
|
197
|
+
? {
|
|
198
|
+
sender: {
|
|
199
|
+
...(senderDisplayName ? { displayName: senderDisplayName } : {}),
|
|
200
|
+
...(slackMeta.actorExternalUserId
|
|
201
|
+
? { externalUserId: slackMeta.actorExternalUserId }
|
|
202
|
+
: {}),
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
: {}),
|
|
175
206
|
...(messageLink ? { messageLink } : {}),
|
|
176
207
|
...(threadLink ? { threadLink } : {}),
|
|
177
208
|
};
|
|
@@ -389,32 +420,9 @@ async function tryConsumeCanonicalGuardianReply(params: {
|
|
|
389
420
|
return { consumed: true, messageId };
|
|
390
421
|
}
|
|
391
422
|
|
|
392
|
-
function
|
|
393
|
-
|
|
394
|
-
):
|
|
395
|
-
if (!interfacesDir || !existsSync(interfacesDir)) return [];
|
|
396
|
-
const results: Array<{ path: string; mtimeMs: number }> = [];
|
|
397
|
-
const scan = (dir: string): void => {
|
|
398
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
399
|
-
const fullPath = join(dir, entry.name);
|
|
400
|
-
if (entry.isDirectory()) {
|
|
401
|
-
scan(fullPath);
|
|
402
|
-
} else {
|
|
403
|
-
results.push({
|
|
404
|
-
path: relative(interfacesDir, fullPath),
|
|
405
|
-
mtimeMs: statSync(fullPath).mtimeMs,
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
};
|
|
410
|
-
scan(interfacesDir);
|
|
411
|
-
return results;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
export function handleListMessages(
|
|
415
|
-
{ queryParams }: RouteHandlerArgs,
|
|
416
|
-
interfacesDir: string | null,
|
|
417
|
-
): Record<string, unknown> {
|
|
423
|
+
export function handleListMessages({
|
|
424
|
+
queryParams,
|
|
425
|
+
}: RouteHandlerArgs): Record<string, unknown> {
|
|
418
426
|
const conversationId = queryParams?.conversationId;
|
|
419
427
|
const conversationKey = queryParams?.conversationKey;
|
|
420
428
|
|
|
@@ -422,8 +430,20 @@ export function handleListMessages(
|
|
|
422
430
|
if (conversationId) {
|
|
423
431
|
resolvedConversationId = conversationId;
|
|
424
432
|
} else if (conversationKey) {
|
|
433
|
+
// Dual lookup, key-first: prefer the `conversation_keys` table — the
|
|
434
|
+
// canonical channel/external → internal-id mapping — so legacy or
|
|
435
|
+
// externally-sourced keys keep their explicit mapping precedence and
|
|
436
|
+
// never collide with an unrelated `conversations.id`. Fall back to a
|
|
437
|
+
// direct id lookup only when no mapping exists, which covers
|
|
438
|
+
// background/scheduled conversations bootstrapped without a
|
|
439
|
+
// `conversation_keys` row (web clients use the conversation list's
|
|
440
|
+
// `id` as `conversationKey` for those).
|
|
425
441
|
const mapping = getConversationByKey(conversationKey);
|
|
426
|
-
|
|
442
|
+
if (mapping) {
|
|
443
|
+
resolvedConversationId = mapping.conversationId;
|
|
444
|
+
} else if (getConversation(conversationKey)) {
|
|
445
|
+
resolvedConversationId = conversationKey;
|
|
446
|
+
}
|
|
427
447
|
} else {
|
|
428
448
|
throw new BadRequestError(
|
|
429
449
|
"conversationKey or conversationId query parameter is required",
|
|
@@ -479,16 +499,30 @@ export function handleListMessages(
|
|
|
479
499
|
let rawMessages: MessageRow[];
|
|
480
500
|
let hasMore = false;
|
|
481
501
|
|
|
502
|
+
// Drop messages flagged as hidden in metadata (e.g. internal scaffolding
|
|
503
|
+
// like retrospective instructions). The LLM-side history loader
|
|
504
|
+
// (`getMessages` in memory/conversation-crud.ts) intentionally does not
|
|
505
|
+
// filter — hidden messages remain in agent context but are suppressed from
|
|
506
|
+
// the UI list. Filtering is pushed into the paginated query so `hasMore`
|
|
507
|
+
// and the cursor reflect visible rows; otherwise a fully-hidden page would
|
|
508
|
+
// return `hasMore: true` with no cursor and stall the web client.
|
|
509
|
+
// Hidden tool_use/tool_result pairs must be hidden together — if a hidden
|
|
510
|
+
// assistant message has tool_use blocks but its matching user tool_result
|
|
511
|
+
// is left visible, the result will render as a standalone orphan because
|
|
512
|
+
// `mergeToolResultsIntoAssistantMessages` has nothing to merge it into.
|
|
513
|
+
const visibleFilter = (m: MessageRow) => !isHiddenMessage(m.metadata);
|
|
514
|
+
|
|
482
515
|
if (isPaginated) {
|
|
483
516
|
const result = getMessagesPaginated(
|
|
484
517
|
resolvedConversationId,
|
|
485
518
|
limit,
|
|
486
519
|
beforeTimestamp,
|
|
520
|
+
visibleFilter,
|
|
487
521
|
);
|
|
488
522
|
rawMessages = result.messages;
|
|
489
523
|
hasMore = result.hasMore;
|
|
490
524
|
} else {
|
|
491
|
-
rawMessages = getMessages(resolvedConversationId);
|
|
525
|
+
rawMessages = getMessages(resolvedConversationId).filter(visibleFilter);
|
|
492
526
|
}
|
|
493
527
|
|
|
494
528
|
// During streaming, tool_use (assistant) and tool_result (user) events are
|
|
@@ -507,6 +541,7 @@ export function handleListMessages(
|
|
|
507
541
|
// (consecutive tool refs grouped together).
|
|
508
542
|
const { messages: consolidatedMessages, mergedIdMap } =
|
|
509
543
|
mergeConsecutiveAssistantMessages(mergedMessages);
|
|
544
|
+
const assistantSlackDisplayName = getAssistantName()?.trim() || undefined;
|
|
510
545
|
|
|
511
546
|
// Parse content blocks and extract text + tool calls
|
|
512
547
|
const parsed = consolidatedMessages.map((msg) => {
|
|
@@ -558,6 +593,10 @@ export function handleListMessages(
|
|
|
558
593
|
}
|
|
559
594
|
const slackMessage = buildSlackHistoryMessage(
|
|
560
595
|
readSlackMetadataFromMessageMetadata(msg.metadata),
|
|
596
|
+
{
|
|
597
|
+
role: msg.role,
|
|
598
|
+
assistantDisplayName: assistantSlackDisplayName,
|
|
599
|
+
},
|
|
561
600
|
);
|
|
562
601
|
|
|
563
602
|
// Strip <no_response/> markers from assistant messages so web/API
|
|
@@ -598,6 +637,7 @@ export function handleListMessages(
|
|
|
598
637
|
textSegments: filteredSegments,
|
|
599
638
|
contentOrder: filteredContentOrder,
|
|
600
639
|
surfaces: rendered.surfaces,
|
|
640
|
+
attachmentRefs: rendered.attachments,
|
|
601
641
|
slackMessage,
|
|
602
642
|
...(rendered.thinkingSegments.length > 0
|
|
603
643
|
? { thinkingSegments: rendered.thinkingSegments }
|
|
@@ -617,6 +657,7 @@ export function handleListMessages(
|
|
|
617
657
|
textSegments: rendered.textSegments,
|
|
618
658
|
contentOrder: rendered.contentOrder,
|
|
619
659
|
surfaces: rendered.surfaces,
|
|
660
|
+
attachmentRefs: rendered.attachments,
|
|
620
661
|
slackMessage,
|
|
621
662
|
...(rendered.thinkingSegments.length > 0
|
|
622
663
|
? { thinkingSegments: rendered.thinkingSegments }
|
|
@@ -626,15 +667,6 @@ export function handleListMessages(
|
|
|
626
667
|
};
|
|
627
668
|
});
|
|
628
669
|
|
|
629
|
-
const interfaceFiles = getInterfaceFilesWithMtimes(interfacesDir);
|
|
630
|
-
|
|
631
|
-
let prevAssistantTimestamp = 0;
|
|
632
|
-
if (isPaginated && rawMessages.length > 0) {
|
|
633
|
-
prevAssistantTimestamp = getLastAssistantTimestampBefore(
|
|
634
|
-
resolvedConversationId!,
|
|
635
|
-
rawMessages[0].createdAt,
|
|
636
|
-
);
|
|
637
|
-
}
|
|
638
670
|
const messages: RuntimeMessagePayload[] = parsed.map((m) => {
|
|
639
671
|
let msgAttachments: RuntimeAttachmentMetadata[] = [];
|
|
640
672
|
if (m.id) {
|
|
@@ -681,19 +713,76 @@ export function handleListMessages(
|
|
|
681
713
|
}
|
|
682
714
|
}
|
|
683
715
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
716
|
+
// Align msgAttachments order with the file-block order captured by
|
|
717
|
+
// renderHistoryContent. When a file block was persisted with
|
|
718
|
+
// `_attachmentId`, we can join on that id to position the chip inline
|
|
719
|
+
// (the `attachment:N` entries in contentOrder index into msgAttachments).
|
|
720
|
+
// DB rows without a matching ref go to the tail as orphan chips;
|
|
721
|
+
// unmatched refs drop their contentOrder entry and trigger a remap.
|
|
722
|
+
let alignedContentOrder = m.contentOrder;
|
|
723
|
+
if (
|
|
724
|
+
m.attachmentRefs.length > 0 &&
|
|
725
|
+
msgAttachments.length > 0 &&
|
|
726
|
+
m.contentOrder.length > 0
|
|
727
|
+
) {
|
|
728
|
+
const byId = new Map<string, number>();
|
|
729
|
+
msgAttachments.forEach((att, idx) => {
|
|
730
|
+
if (att.id) byId.set(att.id, idx);
|
|
731
|
+
});
|
|
732
|
+
const consumed = new Set<number>();
|
|
733
|
+
const orderedRowIdx: Array<number | null> = m.attachmentRefs.map(
|
|
734
|
+
(ref) => {
|
|
735
|
+
if (!ref.attachmentId) return null;
|
|
736
|
+
const idx = byId.get(ref.attachmentId);
|
|
737
|
+
if (idx === undefined || consumed.has(idx)) return null;
|
|
738
|
+
consumed.add(idx);
|
|
739
|
+
return idx;
|
|
740
|
+
},
|
|
741
|
+
);
|
|
742
|
+
const matchedRows = orderedRowIdx.filter(
|
|
743
|
+
(idx): idx is number => idx !== null,
|
|
744
|
+
);
|
|
745
|
+
if (matchedRows.length > 0) {
|
|
746
|
+
const orphanRows: number[] = [];
|
|
747
|
+
for (let i = 0; i < msgAttachments.length; i++) {
|
|
748
|
+
if (!consumed.has(i)) orphanRows.push(i);
|
|
749
|
+
}
|
|
750
|
+
msgAttachments = [
|
|
751
|
+
...matchedRows.map((i) => msgAttachments[i]),
|
|
752
|
+
...orphanRows.map((i) => msgAttachments[i]),
|
|
753
|
+
];
|
|
754
|
+
const refToNewIdx = new Map<number, number>();
|
|
755
|
+
let nextIdx = 0;
|
|
756
|
+
orderedRowIdx.forEach((rowIdx, refIdx) => {
|
|
757
|
+
if (rowIdx !== null) {
|
|
758
|
+
refToNewIdx.set(refIdx, nextIdx);
|
|
759
|
+
nextIdx++;
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
alignedContentOrder = m.contentOrder
|
|
763
|
+
.map((entry) => {
|
|
764
|
+
const match = entry.match(ATTACHMENT_ENTRY_RE);
|
|
765
|
+
if (!match) return entry;
|
|
766
|
+
const remapped = refToNewIdx.get(Number(match[1]));
|
|
767
|
+
return remapped !== undefined
|
|
768
|
+
? `attachment:${remapped}`
|
|
769
|
+
: undefined;
|
|
770
|
+
})
|
|
771
|
+
.filter((e): e is string => e !== undefined);
|
|
772
|
+
} else {
|
|
773
|
+
// No refs carried an attachmentId we could match — strip any
|
|
774
|
+
// attachment:N entries so the client doesn't try to position
|
|
775
|
+
// attachments inline against a misaligned array.
|
|
776
|
+
alignedContentOrder = m.contentOrder.filter(
|
|
777
|
+
(entry) => !ATTACHMENT_ENTRY_RE.test(entry),
|
|
778
|
+
);
|
|
695
779
|
}
|
|
696
|
-
|
|
780
|
+
} else if (m.attachmentRefs.length > 0 && msgAttachments.length === 0) {
|
|
781
|
+
// Refs were captured but no DB rows came back — drop the
|
|
782
|
+
// contentOrder entries to avoid out-of-bounds renders.
|
|
783
|
+
alignedContentOrder = m.contentOrder.filter(
|
|
784
|
+
(entry) => !ATTACHMENT_ENTRY_RE.test(entry),
|
|
785
|
+
);
|
|
697
786
|
}
|
|
698
787
|
|
|
699
788
|
// Use sentAt (actual event time) for the display timestamp when
|
|
@@ -716,13 +805,14 @@ export function handleListMessages(
|
|
|
716
805
|
timestamp: new Date(displayTimestamp).toISOString(),
|
|
717
806
|
attachments: msgAttachments,
|
|
718
807
|
...(m.toolCalls.length > 0 ? { toolCalls: m.toolCalls } : {}),
|
|
719
|
-
...(interfaces ? { interfaces } : {}),
|
|
720
808
|
...(m.surfaces.length > 0 ? { surfaces: m.surfaces } : {}),
|
|
721
809
|
...(m.textSegments.length > 0 ? { textSegments: m.textSegments } : {}),
|
|
722
810
|
...(m.thinkingSegments?.length
|
|
723
811
|
? { thinkingSegments: m.thinkingSegments }
|
|
724
812
|
: {}),
|
|
725
|
-
...(
|
|
813
|
+
...(alignedContentOrder.length > 0
|
|
814
|
+
? { contentOrder: alignedContentOrder }
|
|
815
|
+
: {}),
|
|
726
816
|
...(m.subagentNotification
|
|
727
817
|
? { subagentNotification: m.subagentNotification }
|
|
728
818
|
: {}),
|
|
@@ -1091,6 +1181,10 @@ export function persistOnboardingArtifacts(onboarding: {
|
|
|
1091
1181
|
tone: string;
|
|
1092
1182
|
userName?: string;
|
|
1093
1183
|
assistantName?: string;
|
|
1184
|
+
priorAssistants?: string[];
|
|
1185
|
+
cohort?: string;
|
|
1186
|
+
websiteUrl?: string;
|
|
1187
|
+
contentSourceUrl?: string;
|
|
1094
1188
|
}): void {
|
|
1095
1189
|
writeOnboardingSidecar(onboarding);
|
|
1096
1190
|
|
|
@@ -1168,6 +1262,10 @@ export async function handleSendMessage(
|
|
|
1168
1262
|
assistantName?: string;
|
|
1169
1263
|
googleConnected?: boolean;
|
|
1170
1264
|
googleScopes?: string[];
|
|
1265
|
+
priorAssistants?: string[];
|
|
1266
|
+
cohort?: string;
|
|
1267
|
+
websiteUrl?: string;
|
|
1268
|
+
contentSourceUrl?: string;
|
|
1171
1269
|
};
|
|
1172
1270
|
};
|
|
1173
1271
|
|
|
@@ -1492,13 +1590,41 @@ export async function handleSendMessage(
|
|
|
1492
1590
|
// that can service host_browser_request events; we restore that single
|
|
1493
1591
|
// proxy explicitly below without relaxing `hasNoClient`.
|
|
1494
1592
|
conversation.updateClient(broadcastMessage, !isInteractive);
|
|
1593
|
+
if (isInteractive) {
|
|
1594
|
+
getSubagentManager().updateParentSender(
|
|
1595
|
+
mapping.conversationId,
|
|
1596
|
+
broadcastMessage,
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1495
1599
|
|
|
1496
|
-
// ──
|
|
1497
|
-
//
|
|
1498
|
-
//
|
|
1499
|
-
//
|
|
1500
|
-
//
|
|
1501
|
-
|
|
1600
|
+
// ── URL scan path: rewrite first message for scan onboarding ──
|
|
1601
|
+
// When onboarding provides a websiteUrl or contentSourceUrl and the
|
|
1602
|
+
// first message is the macOS wake-up greeting, bypass the canned
|
|
1603
|
+
// greeting and rewrite the user message to a scan instruction so real
|
|
1604
|
+
// LLM inference runs against the URL.
|
|
1605
|
+
const sanitizeUrl = (u?: string) =>
|
|
1606
|
+
u?.trim().replace(/[\r\n\t]/g, "") || undefined;
|
|
1607
|
+
const websiteUrl = sanitizeUrl(body.onboarding?.websiteUrl);
|
|
1608
|
+
const contentSourceUrl = sanitizeUrl(body.onboarding?.contentSourceUrl);
|
|
1609
|
+
const scanUrl = websiteUrl || contentSourceUrl;
|
|
1610
|
+
const isWakeUp = isWakeUpGreeting(
|
|
1611
|
+
trimmedContent,
|
|
1612
|
+
conversation.getMessages().length,
|
|
1613
|
+
);
|
|
1614
|
+
const isScanPath = !!scanUrl && isWakeUp;
|
|
1615
|
+
|
|
1616
|
+
let effectiveContent: string | undefined;
|
|
1617
|
+
if (isScanPath) {
|
|
1618
|
+
const scanVariant = websiteUrl
|
|
1619
|
+
? ("website" as const)
|
|
1620
|
+
: ("content-source" as const);
|
|
1621
|
+
effectiveContent = buildScanFirstMessage(scanUrl, scanVariant);
|
|
1622
|
+
// Fall through to normal inference path below
|
|
1623
|
+
} else if (isWakeUp && body.onboarding?.cohort === "content-automation") {
|
|
1624
|
+
effectiveContent = "I want to write articles that rank better in GEO";
|
|
1625
|
+
// Fall through to normal inference path — the bootstrap template
|
|
1626
|
+
// and geo-writing skill handle this message.
|
|
1627
|
+
} else if (isWakeUp) {
|
|
1502
1628
|
const cannedGreeting = getCannedFirstGreeting(body.onboarding ?? undefined);
|
|
1503
1629
|
|
|
1504
1630
|
conversation.processing = true;
|
|
@@ -1562,6 +1688,7 @@ export async function handleSendMessage(
|
|
|
1562
1688
|
tone: body.onboarding!.tone,
|
|
1563
1689
|
googleConnected: body.onboarding!.googleConnected,
|
|
1564
1690
|
googleScopes: body.onboarding!.googleScopes,
|
|
1691
|
+
priorAssistants: body.onboarding!.priorAssistants,
|
|
1565
1692
|
});
|
|
1566
1693
|
} catch (err) {
|
|
1567
1694
|
log.warn({ err }, "Failed to record onboarding telemetry event");
|
|
@@ -1616,12 +1743,18 @@ export async function handleSendMessage(
|
|
|
1616
1743
|
tone: body.onboarding!.tone,
|
|
1617
1744
|
googleConnected: body.onboarding!.googleConnected,
|
|
1618
1745
|
googleScopes: body.onboarding!.googleScopes,
|
|
1746
|
+
priorAssistants: body.onboarding!.priorAssistants,
|
|
1619
1747
|
});
|
|
1620
1748
|
} catch (err) {
|
|
1621
1749
|
log.warn({ err }, "Failed to record onboarding telemetry event");
|
|
1622
1750
|
}
|
|
1623
1751
|
}
|
|
1624
1752
|
|
|
1753
|
+
// When the scan path rewrote the first message, prefer the rewritten
|
|
1754
|
+
// content for all downstream consumers (guardian reply, enqueue, agent
|
|
1755
|
+
// loop) so they see the scan instruction rather than the wake-up greeting.
|
|
1756
|
+
const contentAfterScan = effectiveContent ?? content ?? "";
|
|
1757
|
+
|
|
1625
1758
|
const attachments = hasAttachments
|
|
1626
1759
|
? smDeps.resolveAttachments(attachmentIds)
|
|
1627
1760
|
: [];
|
|
@@ -1641,7 +1774,7 @@ export async function handleSendMessage(
|
|
|
1641
1774
|
conversationId: mapping.conversationId,
|
|
1642
1775
|
sourceChannel,
|
|
1643
1776
|
sourceInterface,
|
|
1644
|
-
content:
|
|
1777
|
+
content: contentAfterScan,
|
|
1645
1778
|
attachments,
|
|
1646
1779
|
conversation,
|
|
1647
1780
|
onEvent: broadcastMessage,
|
|
@@ -1675,7 +1808,7 @@ export async function handleSendMessage(
|
|
|
1675
1808
|
// Queue the message so it's processed when the current turn completes
|
|
1676
1809
|
const requestId = crypto.randomUUID();
|
|
1677
1810
|
const enqueueResult = conversation.enqueueMessage(
|
|
1678
|
-
|
|
1811
|
+
contentAfterScan,
|
|
1679
1812
|
attachments,
|
|
1680
1813
|
broadcastMessage,
|
|
1681
1814
|
requestId,
|
|
@@ -1747,6 +1880,7 @@ export async function handleSendMessage(
|
|
|
1747
1880
|
accepted: true,
|
|
1748
1881
|
queued: true,
|
|
1749
1882
|
conversationId: mapping.conversationId,
|
|
1883
|
+
requestId,
|
|
1750
1884
|
};
|
|
1751
1885
|
}
|
|
1752
1886
|
|
|
@@ -1794,7 +1928,9 @@ export async function handleSendMessage(
|
|
|
1794
1928
|
await conversation.ensureActorScopedHistory();
|
|
1795
1929
|
|
|
1796
1930
|
// Resolve slash commands before persisting or running the agent loop.
|
|
1797
|
-
|
|
1931
|
+
// `contentAfterScan` already carries the scan-rewritten content when
|
|
1932
|
+
// applicable; reuse it here for consistency.
|
|
1933
|
+
const rawContent = contentAfterScan;
|
|
1798
1934
|
const slashContext = buildSlashContextForContent(rawContent, {
|
|
1799
1935
|
conversationId: mapping.conversationId,
|
|
1800
1936
|
messageCount: conversation.getMessages().length,
|
|
@@ -2005,6 +2141,82 @@ export async function handleSendMessage(
|
|
|
2005
2141
|
};
|
|
2006
2142
|
}
|
|
2007
2143
|
|
|
2144
|
+
if (slashResult.kind === "clean") {
|
|
2145
|
+
conversation.processing = true;
|
|
2146
|
+
const provenance = provenanceFromTrustContext(conversation.trustContext);
|
|
2147
|
+
const channelMeta = {
|
|
2148
|
+
...provenance,
|
|
2149
|
+
userMessageChannel: sourceChannel,
|
|
2150
|
+
assistantMessageChannel: sourceChannel,
|
|
2151
|
+
userMessageInterface: sourceInterface,
|
|
2152
|
+
assistantMessageInterface: sourceInterface,
|
|
2153
|
+
};
|
|
2154
|
+
const cleanMsg = createUserMessage(rawContent, attachments);
|
|
2155
|
+
const persisted = await addMessage(
|
|
2156
|
+
mapping.conversationId,
|
|
2157
|
+
"user",
|
|
2158
|
+
JSON.stringify(cleanMsg.content),
|
|
2159
|
+
channelMeta,
|
|
2160
|
+
);
|
|
2161
|
+
conversation.getMessages().push(cleanMsg);
|
|
2162
|
+
|
|
2163
|
+
const conversationId = mapping.conversationId;
|
|
2164
|
+
|
|
2165
|
+
let assistantMessagePersisted = false;
|
|
2166
|
+
try {
|
|
2167
|
+
broadcastMessage({
|
|
2168
|
+
type: "user_message_echo",
|
|
2169
|
+
text: rawContent,
|
|
2170
|
+
conversationId,
|
|
2171
|
+
messageId: persisted.id,
|
|
2172
|
+
clientMessageId,
|
|
2173
|
+
});
|
|
2174
|
+
publishConversationMessagesChanged(conversationId);
|
|
2175
|
+
|
|
2176
|
+
const result = await conversation.forceClean();
|
|
2177
|
+
const responseText = formatCleanResult(result);
|
|
2178
|
+
|
|
2179
|
+
const assistantMsg = createAssistantMessage(responseText);
|
|
2180
|
+
await addMessage(
|
|
2181
|
+
conversationId,
|
|
2182
|
+
"assistant",
|
|
2183
|
+
JSON.stringify(assistantMsg.content),
|
|
2184
|
+
channelMeta,
|
|
2185
|
+
);
|
|
2186
|
+
assistantMessagePersisted = true;
|
|
2187
|
+
conversation.getMessages().push(assistantMsg);
|
|
2188
|
+
|
|
2189
|
+
broadcastMessage({
|
|
2190
|
+
type: "assistant_text_delta",
|
|
2191
|
+
text: responseText,
|
|
2192
|
+
conversationId,
|
|
2193
|
+
});
|
|
2194
|
+
broadcastMessage({ type: "message_complete", conversationId });
|
|
2195
|
+
publishConversationMessagesChanged(conversationId);
|
|
2196
|
+
} catch (err) {
|
|
2197
|
+
if (assistantMessagePersisted) {
|
|
2198
|
+
publishConversationMessagesChanged(conversationId);
|
|
2199
|
+
}
|
|
2200
|
+
log.error({ err, conversationId }, "Clean command failed");
|
|
2201
|
+
broadcastMessage({
|
|
2202
|
+
type: "conversation_error",
|
|
2203
|
+
conversationId,
|
|
2204
|
+
code: "UNKNOWN",
|
|
2205
|
+
userMessage: `Clean failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
2206
|
+
retryable: true,
|
|
2207
|
+
});
|
|
2208
|
+
} finally {
|
|
2209
|
+
conversation.processing = false;
|
|
2210
|
+
silentlyWithLog(conversation.drainQueue(), "clean-command queue drain");
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
return {
|
|
2214
|
+
accepted: true,
|
|
2215
|
+
messageId: persisted.id,
|
|
2216
|
+
conversationId,
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2008
2220
|
const resolvedContent = slashResult.content;
|
|
2009
2221
|
|
|
2010
2222
|
const requestId = crypto.randomUUID();
|
|
@@ -2362,7 +2574,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
2362
2574
|
.optional()
|
|
2363
2575
|
.describe("ID of the oldest message in this page"),
|
|
2364
2576
|
}),
|
|
2365
|
-
handler: (args) => handleListMessages(args
|
|
2577
|
+
handler: (args) => handleListMessages(args),
|
|
2366
2578
|
},
|
|
2367
2579
|
{
|
|
2368
2580
|
operationId: "messages_post",
|