@vellumai/assistant 0.8.3 → 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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- 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__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- 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 +25 -7
- 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-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 +264 -81
- 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__/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__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -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-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-chain.test.ts +2 -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__/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-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- 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.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- 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__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- 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-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- 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/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- 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 +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- 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/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- 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 +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- 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 +135 -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 -5
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- 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-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- 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/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/trust-context.ts +6 -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/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- 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/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -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/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- 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/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 +17 -0
- package/src/memory/migrations/registry.ts +25 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- 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/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/messaging/providers/index.ts +7 -1
- 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__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- package/src/notifications/home-feed-side-effect.ts +60 -30
- 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/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 +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- 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/adapter-factory.ts +15 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -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 +151 -56
- package/src/runtime/auth/route-policy.ts +7 -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-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- 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/chatgpt-subscription-auth-routes.ts +246 -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 +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -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 +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -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/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/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/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/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- 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/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- 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/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,
|
|
@@ -103,10 +99,7 @@ import type { Provider } from "../../providers/types.js";
|
|
|
103
99
|
import { checkIngressForSecrets } from "../../security/secret-ingress.js";
|
|
104
100
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
105
101
|
import { getLogger } from "../../util/logger.js";
|
|
106
|
-
import {
|
|
107
|
-
getInterfacesDir,
|
|
108
|
-
getWorkspacePromptPath,
|
|
109
|
-
} from "../../util/platform.js";
|
|
102
|
+
import { getWorkspacePromptPath } from "../../util/platform.js";
|
|
110
103
|
import { silentlyWithLog } from "../../util/silently.js";
|
|
111
104
|
import { assistantEventHub, broadcastMessage } from "../assistant-event-hub.js";
|
|
112
105
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
@@ -136,6 +129,7 @@ const log = getLogger("conversation-routes");
|
|
|
136
129
|
|
|
137
130
|
/** Matches the `<no_response/>` sentinel used by channel delivery suppression. */
|
|
138
131
|
const NO_RESPONSE_INLINE_RE = /<no_response\s*\/?>/g;
|
|
132
|
+
const ATTACHMENT_ENTRY_RE = /^attachment:(\d+)$/;
|
|
139
133
|
|
|
140
134
|
const SUGGESTION_CACHE_MAX = 100;
|
|
141
135
|
const VALID_RISK_THRESHOLDS = ["none", "low", "medium", "high"] as const;
|
|
@@ -148,31 +142,67 @@ function isValidRiskThreshold(value: unknown): value is RiskThreshold {
|
|
|
148
142
|
);
|
|
149
143
|
}
|
|
150
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
|
+
|
|
151
160
|
function buildSlackHistoryMessage(
|
|
152
161
|
slackMeta: SlackMessageMetadata | null,
|
|
162
|
+
opts?: { role?: string; assistantDisplayName?: string },
|
|
153
163
|
): RuntimeMessagePayload["slackMessage"] | undefined {
|
|
154
164
|
if (!slackMeta) return undefined;
|
|
155
165
|
|
|
156
166
|
const slackConfig = getConfig().slack;
|
|
167
|
+
const replyThreadTs =
|
|
168
|
+
slackMeta.threadTs && slackMeta.threadTs !== slackMeta.channelTs
|
|
169
|
+
? slackMeta.threadTs
|
|
170
|
+
: undefined;
|
|
157
171
|
const messageLink = buildSlackMessageDeepLinks({
|
|
158
172
|
teamId: slackConfig?.teamId,
|
|
159
173
|
teamUrl: slackConfig?.teamUrl,
|
|
160
174
|
channelId: slackMeta.channelId,
|
|
161
175
|
messageTs: slackMeta.channelTs,
|
|
176
|
+
...(replyThreadTs ? { threadTs: replyThreadTs } : {}),
|
|
162
177
|
});
|
|
163
|
-
const threadLink =
|
|
178
|
+
const threadLink = replyThreadTs
|
|
164
179
|
? buildSlackMessageDeepLinks({
|
|
165
180
|
teamId: slackConfig?.teamId,
|
|
166
181
|
teamUrl: slackConfig?.teamUrl,
|
|
167
182
|
channelId: slackMeta.channelId,
|
|
168
|
-
messageTs:
|
|
183
|
+
messageTs: replyThreadTs,
|
|
169
184
|
})
|
|
170
185
|
: undefined;
|
|
186
|
+
const assistantDisplayName =
|
|
187
|
+
opts?.role === "assistant" ? opts.assistantDisplayName : undefined;
|
|
188
|
+
const senderDisplayName =
|
|
189
|
+
slackMeta.displayName?.trim() || assistantDisplayName;
|
|
171
190
|
|
|
172
191
|
return {
|
|
173
192
|
channelId: slackMeta.channelId,
|
|
193
|
+
...(slackMeta.channelName ? { channelName: slackMeta.channelName } : {}),
|
|
174
194
|
channelTs: slackMeta.channelTs,
|
|
175
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
|
+
: {}),
|
|
176
206
|
...(messageLink ? { messageLink } : {}),
|
|
177
207
|
...(threadLink ? { threadLink } : {}),
|
|
178
208
|
};
|
|
@@ -390,32 +420,9 @@ async function tryConsumeCanonicalGuardianReply(params: {
|
|
|
390
420
|
return { consumed: true, messageId };
|
|
391
421
|
}
|
|
392
422
|
|
|
393
|
-
function
|
|
394
|
-
|
|
395
|
-
):
|
|
396
|
-
if (!interfacesDir || !existsSync(interfacesDir)) return [];
|
|
397
|
-
const results: Array<{ path: string; mtimeMs: number }> = [];
|
|
398
|
-
const scan = (dir: string): void => {
|
|
399
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
400
|
-
const fullPath = join(dir, entry.name);
|
|
401
|
-
if (entry.isDirectory()) {
|
|
402
|
-
scan(fullPath);
|
|
403
|
-
} else {
|
|
404
|
-
results.push({
|
|
405
|
-
path: relative(interfacesDir, fullPath),
|
|
406
|
-
mtimeMs: statSync(fullPath).mtimeMs,
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
scan(interfacesDir);
|
|
412
|
-
return results;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
export function handleListMessages(
|
|
416
|
-
{ queryParams }: RouteHandlerArgs,
|
|
417
|
-
interfacesDir: string | null,
|
|
418
|
-
): Record<string, unknown> {
|
|
423
|
+
export function handleListMessages({
|
|
424
|
+
queryParams,
|
|
425
|
+
}: RouteHandlerArgs): Record<string, unknown> {
|
|
419
426
|
const conversationId = queryParams?.conversationId;
|
|
420
427
|
const conversationKey = queryParams?.conversationKey;
|
|
421
428
|
|
|
@@ -423,8 +430,20 @@ export function handleListMessages(
|
|
|
423
430
|
if (conversationId) {
|
|
424
431
|
resolvedConversationId = conversationId;
|
|
425
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).
|
|
426
441
|
const mapping = getConversationByKey(conversationKey);
|
|
427
|
-
|
|
442
|
+
if (mapping) {
|
|
443
|
+
resolvedConversationId = mapping.conversationId;
|
|
444
|
+
} else if (getConversation(conversationKey)) {
|
|
445
|
+
resolvedConversationId = conversationKey;
|
|
446
|
+
}
|
|
428
447
|
} else {
|
|
429
448
|
throw new BadRequestError(
|
|
430
449
|
"conversationKey or conversationId query parameter is required",
|
|
@@ -480,16 +499,30 @@ export function handleListMessages(
|
|
|
480
499
|
let rawMessages: MessageRow[];
|
|
481
500
|
let hasMore = false;
|
|
482
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
|
+
|
|
483
515
|
if (isPaginated) {
|
|
484
516
|
const result = getMessagesPaginated(
|
|
485
517
|
resolvedConversationId,
|
|
486
518
|
limit,
|
|
487
519
|
beforeTimestamp,
|
|
520
|
+
visibleFilter,
|
|
488
521
|
);
|
|
489
522
|
rawMessages = result.messages;
|
|
490
523
|
hasMore = result.hasMore;
|
|
491
524
|
} else {
|
|
492
|
-
rawMessages = getMessages(resolvedConversationId);
|
|
525
|
+
rawMessages = getMessages(resolvedConversationId).filter(visibleFilter);
|
|
493
526
|
}
|
|
494
527
|
|
|
495
528
|
// During streaming, tool_use (assistant) and tool_result (user) events are
|
|
@@ -508,6 +541,7 @@ export function handleListMessages(
|
|
|
508
541
|
// (consecutive tool refs grouped together).
|
|
509
542
|
const { messages: consolidatedMessages, mergedIdMap } =
|
|
510
543
|
mergeConsecutiveAssistantMessages(mergedMessages);
|
|
544
|
+
const assistantSlackDisplayName = getAssistantName()?.trim() || undefined;
|
|
511
545
|
|
|
512
546
|
// Parse content blocks and extract text + tool calls
|
|
513
547
|
const parsed = consolidatedMessages.map((msg) => {
|
|
@@ -559,6 +593,10 @@ export function handleListMessages(
|
|
|
559
593
|
}
|
|
560
594
|
const slackMessage = buildSlackHistoryMessage(
|
|
561
595
|
readSlackMetadataFromMessageMetadata(msg.metadata),
|
|
596
|
+
{
|
|
597
|
+
role: msg.role,
|
|
598
|
+
assistantDisplayName: assistantSlackDisplayName,
|
|
599
|
+
},
|
|
562
600
|
);
|
|
563
601
|
|
|
564
602
|
// Strip <no_response/> markers from assistant messages so web/API
|
|
@@ -599,6 +637,7 @@ export function handleListMessages(
|
|
|
599
637
|
textSegments: filteredSegments,
|
|
600
638
|
contentOrder: filteredContentOrder,
|
|
601
639
|
surfaces: rendered.surfaces,
|
|
640
|
+
attachmentRefs: rendered.attachments,
|
|
602
641
|
slackMessage,
|
|
603
642
|
...(rendered.thinkingSegments.length > 0
|
|
604
643
|
? { thinkingSegments: rendered.thinkingSegments }
|
|
@@ -618,6 +657,7 @@ export function handleListMessages(
|
|
|
618
657
|
textSegments: rendered.textSegments,
|
|
619
658
|
contentOrder: rendered.contentOrder,
|
|
620
659
|
surfaces: rendered.surfaces,
|
|
660
|
+
attachmentRefs: rendered.attachments,
|
|
621
661
|
slackMessage,
|
|
622
662
|
...(rendered.thinkingSegments.length > 0
|
|
623
663
|
? { thinkingSegments: rendered.thinkingSegments }
|
|
@@ -627,15 +667,6 @@ export function handleListMessages(
|
|
|
627
667
|
};
|
|
628
668
|
});
|
|
629
669
|
|
|
630
|
-
const interfaceFiles = getInterfaceFilesWithMtimes(interfacesDir);
|
|
631
|
-
|
|
632
|
-
let prevAssistantTimestamp = 0;
|
|
633
|
-
if (isPaginated && rawMessages.length > 0) {
|
|
634
|
-
prevAssistantTimestamp = getLastAssistantTimestampBefore(
|
|
635
|
-
resolvedConversationId!,
|
|
636
|
-
rawMessages[0].createdAt,
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
670
|
const messages: RuntimeMessagePayload[] = parsed.map((m) => {
|
|
640
671
|
let msgAttachments: RuntimeAttachmentMetadata[] = [];
|
|
641
672
|
if (m.id) {
|
|
@@ -682,19 +713,76 @@ export function handleListMessages(
|
|
|
682
713
|
}
|
|
683
714
|
}
|
|
684
715
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
+
);
|
|
696
779
|
}
|
|
697
|
-
|
|
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
|
+
);
|
|
698
786
|
}
|
|
699
787
|
|
|
700
788
|
// Use sentAt (actual event time) for the display timestamp when
|
|
@@ -717,13 +805,14 @@ export function handleListMessages(
|
|
|
717
805
|
timestamp: new Date(displayTimestamp).toISOString(),
|
|
718
806
|
attachments: msgAttachments,
|
|
719
807
|
...(m.toolCalls.length > 0 ? { toolCalls: m.toolCalls } : {}),
|
|
720
|
-
...(interfaces ? { interfaces } : {}),
|
|
721
808
|
...(m.surfaces.length > 0 ? { surfaces: m.surfaces } : {}),
|
|
722
809
|
...(m.textSegments.length > 0 ? { textSegments: m.textSegments } : {}),
|
|
723
810
|
...(m.thinkingSegments?.length
|
|
724
811
|
? { thinkingSegments: m.thinkingSegments }
|
|
725
812
|
: {}),
|
|
726
|
-
...(
|
|
813
|
+
...(alignedContentOrder.length > 0
|
|
814
|
+
? { contentOrder: alignedContentOrder }
|
|
815
|
+
: {}),
|
|
727
816
|
...(m.subagentNotification
|
|
728
817
|
? { subagentNotification: m.subagentNotification }
|
|
729
818
|
: {}),
|
|
@@ -1092,6 +1181,10 @@ export function persistOnboardingArtifacts(onboarding: {
|
|
|
1092
1181
|
tone: string;
|
|
1093
1182
|
userName?: string;
|
|
1094
1183
|
assistantName?: string;
|
|
1184
|
+
priorAssistants?: string[];
|
|
1185
|
+
cohort?: string;
|
|
1186
|
+
websiteUrl?: string;
|
|
1187
|
+
contentSourceUrl?: string;
|
|
1095
1188
|
}): void {
|
|
1096
1189
|
writeOnboardingSidecar(onboarding);
|
|
1097
1190
|
|
|
@@ -1169,6 +1262,10 @@ export async function handleSendMessage(
|
|
|
1169
1262
|
assistantName?: string;
|
|
1170
1263
|
googleConnected?: boolean;
|
|
1171
1264
|
googleScopes?: string[];
|
|
1265
|
+
priorAssistants?: string[];
|
|
1266
|
+
cohort?: string;
|
|
1267
|
+
websiteUrl?: string;
|
|
1268
|
+
contentSourceUrl?: string;
|
|
1172
1269
|
};
|
|
1173
1270
|
};
|
|
1174
1271
|
|
|
@@ -1500,12 +1597,34 @@ export async function handleSendMessage(
|
|
|
1500
1597
|
);
|
|
1501
1598
|
}
|
|
1502
1599
|
|
|
1503
|
-
// ──
|
|
1504
|
-
//
|
|
1505
|
-
//
|
|
1506
|
-
//
|
|
1507
|
-
//
|
|
1508
|
-
|
|
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) {
|
|
1509
1628
|
const cannedGreeting = getCannedFirstGreeting(body.onboarding ?? undefined);
|
|
1510
1629
|
|
|
1511
1630
|
conversation.processing = true;
|
|
@@ -1569,6 +1688,7 @@ export async function handleSendMessage(
|
|
|
1569
1688
|
tone: body.onboarding!.tone,
|
|
1570
1689
|
googleConnected: body.onboarding!.googleConnected,
|
|
1571
1690
|
googleScopes: body.onboarding!.googleScopes,
|
|
1691
|
+
priorAssistants: body.onboarding!.priorAssistants,
|
|
1572
1692
|
});
|
|
1573
1693
|
} catch (err) {
|
|
1574
1694
|
log.warn({ err }, "Failed to record onboarding telemetry event");
|
|
@@ -1623,12 +1743,18 @@ export async function handleSendMessage(
|
|
|
1623
1743
|
tone: body.onboarding!.tone,
|
|
1624
1744
|
googleConnected: body.onboarding!.googleConnected,
|
|
1625
1745
|
googleScopes: body.onboarding!.googleScopes,
|
|
1746
|
+
priorAssistants: body.onboarding!.priorAssistants,
|
|
1626
1747
|
});
|
|
1627
1748
|
} catch (err) {
|
|
1628
1749
|
log.warn({ err }, "Failed to record onboarding telemetry event");
|
|
1629
1750
|
}
|
|
1630
1751
|
}
|
|
1631
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
|
+
|
|
1632
1758
|
const attachments = hasAttachments
|
|
1633
1759
|
? smDeps.resolveAttachments(attachmentIds)
|
|
1634
1760
|
: [];
|
|
@@ -1648,7 +1774,7 @@ export async function handleSendMessage(
|
|
|
1648
1774
|
conversationId: mapping.conversationId,
|
|
1649
1775
|
sourceChannel,
|
|
1650
1776
|
sourceInterface,
|
|
1651
|
-
content:
|
|
1777
|
+
content: contentAfterScan,
|
|
1652
1778
|
attachments,
|
|
1653
1779
|
conversation,
|
|
1654
1780
|
onEvent: broadcastMessage,
|
|
@@ -1682,7 +1808,7 @@ export async function handleSendMessage(
|
|
|
1682
1808
|
// Queue the message so it's processed when the current turn completes
|
|
1683
1809
|
const requestId = crypto.randomUUID();
|
|
1684
1810
|
const enqueueResult = conversation.enqueueMessage(
|
|
1685
|
-
|
|
1811
|
+
contentAfterScan,
|
|
1686
1812
|
attachments,
|
|
1687
1813
|
broadcastMessage,
|
|
1688
1814
|
requestId,
|
|
@@ -1754,6 +1880,7 @@ export async function handleSendMessage(
|
|
|
1754
1880
|
accepted: true,
|
|
1755
1881
|
queued: true,
|
|
1756
1882
|
conversationId: mapping.conversationId,
|
|
1883
|
+
requestId,
|
|
1757
1884
|
};
|
|
1758
1885
|
}
|
|
1759
1886
|
|
|
@@ -1801,7 +1928,9 @@ export async function handleSendMessage(
|
|
|
1801
1928
|
await conversation.ensureActorScopedHistory();
|
|
1802
1929
|
|
|
1803
1930
|
// Resolve slash commands before persisting or running the agent loop.
|
|
1804
|
-
|
|
1931
|
+
// `contentAfterScan` already carries the scan-rewritten content when
|
|
1932
|
+
// applicable; reuse it here for consistency.
|
|
1933
|
+
const rawContent = contentAfterScan;
|
|
1805
1934
|
const slashContext = buildSlashContextForContent(rawContent, {
|
|
1806
1935
|
conversationId: mapping.conversationId,
|
|
1807
1936
|
messageCount: conversation.getMessages().length,
|
|
@@ -2012,6 +2141,82 @@ export async function handleSendMessage(
|
|
|
2012
2141
|
};
|
|
2013
2142
|
}
|
|
2014
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
|
+
|
|
2015
2220
|
const resolvedContent = slashResult.content;
|
|
2016
2221
|
|
|
2017
2222
|
const requestId = crypto.randomUUID();
|
|
@@ -2369,7 +2574,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
2369
2574
|
.optional()
|
|
2370
2575
|
.describe("ID of the oldest message in this page"),
|
|
2371
2576
|
}),
|
|
2372
|
-
handler: (args) => handleListMessages(args
|
|
2577
|
+
handler: (args) => handleListMessages(args),
|
|
2373
2578
|
},
|
|
2374
2579
|
{
|
|
2375
2580
|
operationId: "messages_post",
|