@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
createAssistantMessage,
|
|
11
11
|
createUserMessage,
|
|
12
12
|
} from "../../agent/message-types.js";
|
|
13
|
+
import { ConversationMessageSchema } from "../../api/responses/conversation-message.js";
|
|
13
14
|
import {
|
|
14
15
|
CHANNEL_IDS,
|
|
15
16
|
INTERFACE_IDS,
|
|
@@ -46,7 +47,11 @@ import {
|
|
|
46
47
|
getCannedFirstGreeting,
|
|
47
48
|
isWakeUpGreeting,
|
|
48
49
|
} from "../../daemon/first-greeting.js";
|
|
49
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
collectAttachmentRefs,
|
|
52
|
+
type HistoryAttachmentRef,
|
|
53
|
+
renderHistoryContent,
|
|
54
|
+
} from "../../daemon/handlers/shared.js";
|
|
50
55
|
import { HostAppControlProxy } from "../../daemon/host-app-control-proxy.js";
|
|
51
56
|
import { HostCuProxy } from "../../daemon/host-cu-proxy.js";
|
|
52
57
|
import {
|
|
@@ -105,10 +110,14 @@ import type { Provider } from "../../providers/types.js";
|
|
|
105
110
|
import { checkIngressForSecrets } from "../../security/secret-ingress.js";
|
|
106
111
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
107
112
|
import { getLogger } from "../../util/logger.js";
|
|
108
|
-
import {
|
|
113
|
+
import {
|
|
114
|
+
getWorkspaceDir,
|
|
115
|
+
getWorkspacePromptPath,
|
|
116
|
+
} from "../../util/platform.js";
|
|
109
117
|
import { silentlyWithLog } from "../../util/silently.js";
|
|
110
118
|
import { assistantEventHub, broadcastMessage } from "../assistant-event-hub.js";
|
|
111
119
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
120
|
+
import { getPersistedSeq } from "../assistant-stream-state.js";
|
|
112
121
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
113
122
|
import { routeGuardianReply } from "../guardian-reply-router.js";
|
|
114
123
|
import { healGuardianBindingDrift } from "../guardian-vellum-migration.js";
|
|
@@ -134,6 +143,10 @@ import {
|
|
|
134
143
|
NotFoundError,
|
|
135
144
|
RouteError,
|
|
136
145
|
} from "./errors.js";
|
|
146
|
+
import {
|
|
147
|
+
collectPendingConfirmations,
|
|
148
|
+
enrichToolCallsWithConfirmation,
|
|
149
|
+
} from "./tool-call-confirmation-enrichment.js";
|
|
137
150
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
138
151
|
import { RouteResponse } from "./types.js";
|
|
139
152
|
|
|
@@ -143,6 +156,125 @@ const log = getLogger("conversation-routes");
|
|
|
143
156
|
const NO_RESPONSE_INLINE_RE = /<no_response\s*\/?>/g;
|
|
144
157
|
const ATTACHMENT_ENTRY_RE = /^attachment:(\d+)$/;
|
|
145
158
|
|
|
159
|
+
/** Rewrites a rendered `contentOrder` to reflect attachment alignment. */
|
|
160
|
+
type ContentOrderRewrite = (contentOrder: string[]) => string[];
|
|
161
|
+
|
|
162
|
+
interface AlignedAttachments {
|
|
163
|
+
/** Hydrated rows, reordered to match the inline file-block order. */
|
|
164
|
+
attachments: RuntimeAttachmentMetadata[];
|
|
165
|
+
/**
|
|
166
|
+
* Resolves a content-walk attachment ref index to its hydrated DB row,
|
|
167
|
+
* mirroring the inline placement `rewriteContentOrder` encodes. Refs with no
|
|
168
|
+
* inline placement (unmatched ids, count mismatch, no DB rows) are absent, so
|
|
169
|
+
* `renderHistoryContent` emits no `attachment` block for them — the row still
|
|
170
|
+
* ships via the flat `attachments` array.
|
|
171
|
+
*/
|
|
172
|
+
refIndexToAttachment: Map<number, RuntimeAttachmentMetadata>;
|
|
173
|
+
rewriteContentOrder: ContentOrderRewrite;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Align DB-hydrated attachment rows with the file-block refs `renderHistoryContent`
|
|
178
|
+
* captured. When a file block was persisted with `_attachmentId` (user-message
|
|
179
|
+
* uploads) we join on that id to position the chip inline; DB rows without a
|
|
180
|
+
* matching ref go to the tail as orphan chips, and unmatched refs drop their
|
|
181
|
+
* `attachment:N` entry. Assistant-authored file blocks carry no `_attachmentId`,
|
|
182
|
+
* so when no ids match we fall back to positional alignment if the ref and row
|
|
183
|
+
* counts agree; otherwise we strip the markers and let chips fall to the tail.
|
|
184
|
+
*/
|
|
185
|
+
function alignAttachments(
|
|
186
|
+
attachmentRefs: HistoryAttachmentRef[],
|
|
187
|
+
attachments: RuntimeAttachmentMetadata[],
|
|
188
|
+
): AlignedAttachments {
|
|
189
|
+
const refIndexToAttachment = new Map<number, RuntimeAttachmentMetadata>();
|
|
190
|
+
const identity: ContentOrderRewrite = (contentOrder) => contentOrder;
|
|
191
|
+
const stripAttachmentEntries: ContentOrderRewrite = (contentOrder) =>
|
|
192
|
+
contentOrder.filter((entry) => !ATTACHMENT_ENTRY_RE.test(entry));
|
|
193
|
+
|
|
194
|
+
if (attachmentRefs.length === 0) {
|
|
195
|
+
return { attachments, refIndexToAttachment, rewriteContentOrder: identity };
|
|
196
|
+
}
|
|
197
|
+
if (attachments.length === 0) {
|
|
198
|
+
// Refs were captured but no DB rows came back — drop the contentOrder
|
|
199
|
+
// entries to avoid out-of-bounds renders.
|
|
200
|
+
return {
|
|
201
|
+
attachments,
|
|
202
|
+
refIndexToAttachment,
|
|
203
|
+
rewriteContentOrder: stripAttachmentEntries,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const byId = new Map<string, number>();
|
|
208
|
+
attachments.forEach((att, idx) => {
|
|
209
|
+
if (att.id) byId.set(att.id, idx);
|
|
210
|
+
});
|
|
211
|
+
const consumed = new Set<number>();
|
|
212
|
+
const orderedRowIdx: Array<number | null> = attachmentRefs.map((ref) => {
|
|
213
|
+
if (!ref.attachmentId) return null;
|
|
214
|
+
const idx = byId.get(ref.attachmentId);
|
|
215
|
+
if (idx === undefined || consumed.has(idx)) return null;
|
|
216
|
+
consumed.add(idx);
|
|
217
|
+
return idx;
|
|
218
|
+
});
|
|
219
|
+
const matchedRows = orderedRowIdx.filter(
|
|
220
|
+
(idx): idx is number => idx !== null,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
if (matchedRows.length > 0) {
|
|
224
|
+
const orphanRows: number[] = [];
|
|
225
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
226
|
+
if (!consumed.has(i)) orphanRows.push(i);
|
|
227
|
+
}
|
|
228
|
+
const reordered = [
|
|
229
|
+
...matchedRows.map((i) => attachments[i]),
|
|
230
|
+
...orphanRows.map((i) => attachments[i]),
|
|
231
|
+
];
|
|
232
|
+
const refToNewIdx = new Map<number, number>();
|
|
233
|
+
let nextIdx = 0;
|
|
234
|
+
orderedRowIdx.forEach((rowIdx, refIdx) => {
|
|
235
|
+
if (rowIdx !== null) {
|
|
236
|
+
refToNewIdx.set(refIdx, nextIdx);
|
|
237
|
+
refIndexToAttachment.set(refIdx, reordered[nextIdx]);
|
|
238
|
+
nextIdx++;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
const rewriteContentOrder: ContentOrderRewrite = (contentOrder) =>
|
|
242
|
+
contentOrder
|
|
243
|
+
.map((entry) => {
|
|
244
|
+
const match = entry.match(ATTACHMENT_ENTRY_RE);
|
|
245
|
+
if (!match) return entry;
|
|
246
|
+
const remapped = refToNewIdx.get(Number(match[1]));
|
|
247
|
+
return remapped !== undefined ? `attachment:${remapped}` : undefined;
|
|
248
|
+
})
|
|
249
|
+
.filter((e): e is string => e !== undefined);
|
|
250
|
+
return {
|
|
251
|
+
attachments: reordered,
|
|
252
|
+
refIndexToAttachment,
|
|
253
|
+
rewriteContentOrder,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (attachmentRefs.length !== attachments.length) {
|
|
258
|
+
// No ref carried an attachmentId we could match and the counts disagree, so
|
|
259
|
+
// positional mapping can't be trusted — strip any attachment:N entries so
|
|
260
|
+
// the client doesn't position attachments inline against a misaligned array
|
|
261
|
+
// (they fall to the tail instead).
|
|
262
|
+
return {
|
|
263
|
+
attachments,
|
|
264
|
+
refIndexToAttachment,
|
|
265
|
+
rewriteContentOrder: stripAttachmentEntries,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// No ref matched an id but the counts agree (the assistant-authored case):
|
|
270
|
+
// the Nth marker maps to the Nth row positionally, so the original
|
|
271
|
+
// contentOrder is left untouched.
|
|
272
|
+
attachmentRefs.forEach((_ref, refIdx) => {
|
|
273
|
+
refIndexToAttachment.set(refIdx, attachments[refIdx]);
|
|
274
|
+
});
|
|
275
|
+
return { attachments, refIndexToAttachment, rewriteContentOrder: identity };
|
|
276
|
+
}
|
|
277
|
+
|
|
146
278
|
/** Feature flag gating the self-intro first message (see first-greeting.ts). */
|
|
147
279
|
const SELF_INTRO_GREETING_FLAG = "self-intro-greeting" as const;
|
|
148
280
|
|
|
@@ -509,6 +641,7 @@ export function handleListMessages({
|
|
|
509
641
|
hasMore: false,
|
|
510
642
|
oldestTimestamp: null,
|
|
511
643
|
oldestMessageId: null,
|
|
644
|
+
seq: null,
|
|
512
645
|
};
|
|
513
646
|
}
|
|
514
647
|
return { messages: [] };
|
|
@@ -547,7 +680,14 @@ export function handleListMessages({
|
|
|
547
680
|
// assistant message has tool_use blocks but its matching user tool_result
|
|
548
681
|
// is left visible, the result will render as a standalone orphan because
|
|
549
682
|
// `mergeToolResultsIntoAssistantMessages` has nothing to merge it into.
|
|
550
|
-
|
|
683
|
+
//
|
|
684
|
+
// Only renderable roles reach this UI-facing transcript. `system` rows (a
|
|
685
|
+
// permitted `MessageRole`, e.g. skill-authored context) are agent-context
|
|
686
|
+
// scaffolding, never a displayed turn, so they are dropped here at the
|
|
687
|
+
// source rather than narrowed away per-client.
|
|
688
|
+
const visibleFilter = (m: MessageRow) =>
|
|
689
|
+
!isHiddenMessage(m.metadata) &&
|
|
690
|
+
(m.role === "user" || m.role === "assistant");
|
|
551
691
|
|
|
552
692
|
if (isPaginated) {
|
|
553
693
|
const result = getMessagesPaginated(
|
|
@@ -581,7 +721,10 @@ export function handleListMessages({
|
|
|
581
721
|
mergeConsecutiveAssistantMessages(mergedMessages);
|
|
582
722
|
const assistantSlackDisplayName = getAssistantName()?.trim() || undefined;
|
|
583
723
|
|
|
584
|
-
// Parse content
|
|
724
|
+
// Parse each row's stored content and per-message metadata. Rendering is
|
|
725
|
+
// deferred to the serializer pass below so it runs after attachment
|
|
726
|
+
// alignment, letting renderHistoryContent inline `attachment` blocks during
|
|
727
|
+
// its single content walk.
|
|
585
728
|
const parsed = consolidatedMessages.map((msg) => {
|
|
586
729
|
let content: unknown;
|
|
587
730
|
try {
|
|
@@ -589,7 +732,6 @@ export function handleListMessages({
|
|
|
589
732
|
} catch {
|
|
590
733
|
content = msg.content;
|
|
591
734
|
}
|
|
592
|
-
const rendered = renderHistoryContent(content);
|
|
593
735
|
|
|
594
736
|
// Extract sentAt from metadata for display timestamps. When a message
|
|
595
737
|
// was queued or its persistence was delayed (long assistant generation),
|
|
@@ -637,95 +779,51 @@ export function handleListMessages({
|
|
|
637
779
|
},
|
|
638
780
|
);
|
|
639
781
|
|
|
640
|
-
//
|
|
641
|
-
//
|
|
642
|
-
//
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
const keepIndices: number[] = [];
|
|
646
|
-
const filteredSegments: string[] = [];
|
|
647
|
-
for (let i = 0; i < originalSegments.length; i++) {
|
|
648
|
-
const cleaned = originalSegments[i]
|
|
649
|
-
.replace(NO_RESPONSE_INLINE_RE, "")
|
|
650
|
-
.trim();
|
|
651
|
-
if (cleaned.length > 0) {
|
|
652
|
-
keepIndices.push(i);
|
|
653
|
-
filteredSegments.push(cleaned);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
// Remap contentOrder text:N indices to account for removed segments
|
|
657
|
-
const indexMap = new Map<number, number>();
|
|
658
|
-
keepIndices.forEach((oldIdx, newIdx) => indexMap.set(oldIdx, newIdx));
|
|
659
|
-
const filteredContentOrder = rendered.contentOrder
|
|
660
|
-
.map((entry) => {
|
|
661
|
-
const m = entry.match(/^text:(\d+)$/);
|
|
662
|
-
if (!m) return entry;
|
|
663
|
-
const newIdx = indexMap.get(Number(m[1]));
|
|
664
|
-
return newIdx !== undefined ? `text:${newIdx}` : undefined;
|
|
665
|
-
})
|
|
666
|
-
.filter((e): e is string => e !== undefined);
|
|
667
|
-
|
|
668
|
-
return {
|
|
669
|
-
role: msg.role,
|
|
670
|
-
text: rendered.text.replace(NO_RESPONSE_INLINE_RE, "").trim(),
|
|
671
|
-
timestamp: msg.createdAt,
|
|
672
|
-
sentAt,
|
|
673
|
-
toolCalls: rendered.toolCalls,
|
|
674
|
-
toolCallsBeforeText: rendered.toolCallsBeforeText,
|
|
675
|
-
textSegments: filteredSegments,
|
|
676
|
-
contentOrder: filteredContentOrder,
|
|
677
|
-
surfaces: rendered.surfaces,
|
|
678
|
-
attachmentRefs: rendered.attachments,
|
|
679
|
-
slackMessage,
|
|
680
|
-
...(rendered.thinkingSegments.length > 0
|
|
681
|
-
? { thinkingSegments: rendered.thinkingSegments }
|
|
682
|
-
: {}),
|
|
683
|
-
id: msg.id,
|
|
684
|
-
subagentNotification,
|
|
685
|
-
};
|
|
686
|
-
}
|
|
782
|
+
// `visibleFilter` has already dropped every non-renderable role, so the
|
|
783
|
+
// only values reaching here are `user` and `assistant`; narrow the raw DB
|
|
784
|
+
// `string` to the wire union.
|
|
785
|
+
const role: "user" | "assistant" =
|
|
786
|
+
msg.role === "assistant" ? "assistant" : "user";
|
|
687
787
|
|
|
688
788
|
return {
|
|
689
|
-
role: msg.role,
|
|
690
|
-
text: rendered.text,
|
|
691
|
-
timestamp: msg.createdAt,
|
|
692
|
-
sentAt,
|
|
693
|
-
toolCalls: rendered.toolCalls,
|
|
694
|
-
toolCallsBeforeText: rendered.toolCallsBeforeText,
|
|
695
|
-
textSegments: rendered.textSegments,
|
|
696
|
-
contentOrder: rendered.contentOrder,
|
|
697
|
-
surfaces: rendered.surfaces,
|
|
698
|
-
attachmentRefs: rendered.attachments,
|
|
699
|
-
slackMessage,
|
|
700
|
-
...(rendered.thinkingSegments.length > 0
|
|
701
|
-
? { thinkingSegments: rendered.thinkingSegments }
|
|
702
|
-
: {}),
|
|
703
789
|
id: msg.id,
|
|
790
|
+
role,
|
|
791
|
+
content,
|
|
792
|
+
createdAt: msg.createdAt,
|
|
793
|
+
sentAt,
|
|
704
794
|
subagentNotification,
|
|
795
|
+
slackMessage,
|
|
705
796
|
};
|
|
706
797
|
});
|
|
707
798
|
|
|
799
|
+
// Confirmation context layered onto rendered tool calls at render time: the
|
|
800
|
+
// derived scope ladder for scope-aware tools, and any in-flight prompt read
|
|
801
|
+
// from the pending-interactions registry. Both are computed once per request
|
|
802
|
+
// and applied per message below.
|
|
803
|
+
const workspaceDir = getWorkspaceDir();
|
|
804
|
+
const pendingConfirmations = collectPendingConfirmations(
|
|
805
|
+
resolvedConversationId,
|
|
806
|
+
);
|
|
807
|
+
|
|
708
808
|
const messages: RuntimeMessagePayload[] = parsed.map((m) => {
|
|
709
809
|
const mergedMessageIds = m.id ? (mergedIdMap.get(m.id) ?? []) : [];
|
|
810
|
+
|
|
811
|
+
// Hydrate the row's attachments from the DB. A metadata-only query avoids
|
|
812
|
+
// loading large base64 blobs for non-image attachments (documents, audio);
|
|
813
|
+
// full data is fetched only for images so the client can generate
|
|
814
|
+
// thumbnails for inline display on history restore. Merged messages
|
|
815
|
+
// (consecutive assistant merge) are queried too so their attachments
|
|
816
|
+
// aren't lost before DB compaction relinks them.
|
|
710
817
|
let msgAttachments: RuntimeAttachmentMetadata[] = [];
|
|
711
818
|
if (m.id) {
|
|
712
|
-
|
|
713
|
-
// blobs for non-image attachments (documents, audio). Then
|
|
714
|
-
// selectively fetch full data only for images so the client can
|
|
715
|
-
// generate thumbnails for inline display on history restore.
|
|
716
|
-
// Also query attachments for any messages that were merged into
|
|
717
|
-
// this one (consecutive assistant merge), so their attachments
|
|
718
|
-
// aren't lost before DB compaction relinks them.
|
|
719
|
-
const idsToQuery = [m.id, ...(mergedIdMap.get(m.id) ?? [])];
|
|
819
|
+
const idsToQuery = [m.id, ...mergedMessageIds];
|
|
720
820
|
const linked = idsToQuery.flatMap((id) =>
|
|
721
821
|
getAttachmentMetadataForMessage(id),
|
|
722
822
|
);
|
|
723
823
|
if (linked.length > 0) {
|
|
724
824
|
msgAttachments = linked.map((a) => {
|
|
725
825
|
if (a.mimeType.startsWith("image/")) {
|
|
726
|
-
const full = getAttachmentById(a.id, {
|
|
727
|
-
hydrateFileData: true,
|
|
728
|
-
});
|
|
826
|
+
const full = getAttachmentById(a.id, { hydrateFileData: true });
|
|
729
827
|
return {
|
|
730
828
|
id: a.id,
|
|
731
829
|
filename: a.originalFilename,
|
|
@@ -752,107 +850,101 @@ export function handleListMessages({
|
|
|
752
850
|
}
|
|
753
851
|
}
|
|
754
852
|
|
|
755
|
-
// Align
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
//
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
853
|
+
// Align the hydrated rows with the file-block refs, then render. Rendering
|
|
854
|
+
// after alignment lets renderHistoryContent inline each `attachment` block
|
|
855
|
+
// during its single content walk, so `contentBlocks` comes back ready to
|
|
856
|
+
// ship with no post-processing. The aligned reorder/rewrite keeps the
|
|
857
|
+
// legacy `attachments` array and `contentOrder` positions consistent.
|
|
858
|
+
const attachmentRefs = collectAttachmentRefs(m.content);
|
|
859
|
+
const aligned = alignAttachments(attachmentRefs, msgAttachments);
|
|
860
|
+
msgAttachments = aligned.attachments;
|
|
861
|
+
const attachmentBlocks = attachmentRefs.map(
|
|
862
|
+
(_ref, refIdx) => aligned.refIndexToAttachment.get(refIdx) ?? null,
|
|
863
|
+
);
|
|
864
|
+
const rendered = renderHistoryContent(
|
|
865
|
+
m.content,
|
|
866
|
+
attachmentBlocks,
|
|
867
|
+
m.id ?? undefined,
|
|
868
|
+
);
|
|
869
|
+
|
|
870
|
+
const toolCalls = enrichToolCallsWithConfirmation(rendered.toolCalls, {
|
|
871
|
+
workspaceDir,
|
|
872
|
+
pendingConfirmations,
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
// Strip <no_response/> markers from assistant messages so web/API clients
|
|
876
|
+
// never see the raw sentinel. Only assistant messages produce it; user
|
|
877
|
+
// messages are untouched. The filter is applied consistently to the flat
|
|
878
|
+
// text, the segments, the contentOrder text refs, and the text blocks of
|
|
879
|
+
// contentBlocks.
|
|
880
|
+
let text = rendered.text;
|
|
881
|
+
let textSegments = rendered.textSegments;
|
|
882
|
+
let contentOrder = rendered.contentOrder;
|
|
883
|
+
let contentBlocks = rendered.contentBlocks;
|
|
884
|
+
if (m.role === "assistant") {
|
|
885
|
+
const keepIndices: number[] = [];
|
|
886
|
+
const filteredSegments: string[] = [];
|
|
887
|
+
for (let i = 0; i < rendered.textSegments.length; i++) {
|
|
888
|
+
const cleaned = rendered.textSegments[i]
|
|
889
|
+
.replace(NO_RESPONSE_INLINE_RE, "")
|
|
890
|
+
.trim();
|
|
891
|
+
if (cleaned.length > 0) {
|
|
892
|
+
keepIndices.push(i);
|
|
893
|
+
filteredSegments.push(cleaned);
|
|
791
894
|
}
|
|
792
|
-
msgAttachments = [
|
|
793
|
-
...matchedRows.map((i) => msgAttachments[i]),
|
|
794
|
-
...orphanRows.map((i) => msgAttachments[i]),
|
|
795
|
-
];
|
|
796
|
-
const refToNewIdx = new Map<number, number>();
|
|
797
|
-
let nextIdx = 0;
|
|
798
|
-
orderedRowIdx.forEach((rowIdx, refIdx) => {
|
|
799
|
-
if (rowIdx !== null) {
|
|
800
|
-
refToNewIdx.set(refIdx, nextIdx);
|
|
801
|
-
nextIdx++;
|
|
802
|
-
}
|
|
803
|
-
});
|
|
804
|
-
alignedContentOrder = m.contentOrder
|
|
805
|
-
.map((entry) => {
|
|
806
|
-
const match = entry.match(ATTACHMENT_ENTRY_RE);
|
|
807
|
-
if (!match) return entry;
|
|
808
|
-
const remapped = refToNewIdx.get(Number(match[1]));
|
|
809
|
-
return remapped !== undefined
|
|
810
|
-
? `attachment:${remapped}`
|
|
811
|
-
: undefined;
|
|
812
|
-
})
|
|
813
|
-
.filter((e): e is string => e !== undefined);
|
|
814
|
-
} else if (m.attachmentRefs.length !== msgAttachments.length) {
|
|
815
|
-
// No ref carried an attachmentId we could match and the counts
|
|
816
|
-
// disagree, so positional mapping can't be trusted — strip any
|
|
817
|
-
// attachment:N entries so the client doesn't position attachments
|
|
818
|
-
// inline against a misaligned array (they fall to the tail instead).
|
|
819
|
-
alignedContentOrder = m.contentOrder.filter(
|
|
820
|
-
(entry) => !ATTACHMENT_ENTRY_RE.test(entry),
|
|
821
|
-
);
|
|
822
895
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
896
|
+
const indexMap = new Map<number, number>();
|
|
897
|
+
keepIndices.forEach((oldIdx, newIdx) => indexMap.set(oldIdx, newIdx));
|
|
898
|
+
contentOrder = rendered.contentOrder
|
|
899
|
+
.map((entry) => {
|
|
900
|
+
const tm = entry.match(/^text:(\d+)$/);
|
|
901
|
+
if (!tm) return entry;
|
|
902
|
+
const newIdx = indexMap.get(Number(tm[1]));
|
|
903
|
+
return newIdx !== undefined ? `text:${newIdx}` : undefined;
|
|
904
|
+
})
|
|
905
|
+
.filter((e): e is string => e !== undefined);
|
|
906
|
+
textSegments = filteredSegments;
|
|
907
|
+
text = rendered.text.replace(NO_RESPONSE_INLINE_RE, "").trim();
|
|
908
|
+
contentBlocks = rendered.contentBlocks
|
|
909
|
+
.map((block) =>
|
|
910
|
+
block.type === "text"
|
|
911
|
+
? {
|
|
912
|
+
type: "text" as const,
|
|
913
|
+
text: block.text.replace(NO_RESPONSE_INLINE_RE, "").trim(),
|
|
914
|
+
}
|
|
915
|
+
: block,
|
|
916
|
+
)
|
|
917
|
+
.filter((block) => block.type !== "text" || block.text.length > 0);
|
|
832
918
|
}
|
|
833
919
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
//
|
|
839
|
-
//
|
|
840
|
-
|
|
920
|
+
const alignedContentOrder = aligned.rewriteContentOrder(contentOrder);
|
|
921
|
+
|
|
922
|
+
// Use sentAt (actual event time) for the display timestamp when available,
|
|
923
|
+
// falling back to createdAt (persistence time). Clients use this display
|
|
924
|
+
// timestamp as their pagination cursor after memory-pressure trimming,
|
|
925
|
+
// while server-side pagination filters on createdAt. The mismatch is
|
|
926
|
+
// benign — it may return slightly extra data on a page boundary but never
|
|
927
|
+
// loses messages.
|
|
928
|
+
const displayTimestamp = m.sentAt ?? m.createdAt;
|
|
841
929
|
return {
|
|
842
930
|
id: m.id ?? "",
|
|
843
931
|
...(mergedMessageIds.length > 0 ? { mergedMessageIds } : {}),
|
|
844
932
|
role: m.role,
|
|
933
|
+
// Flat plain-text body the legacy Swift client reads directly; see the
|
|
934
|
+
// `content` field on ConversationMessageSchema for why this must stay.
|
|
935
|
+
content: text,
|
|
845
936
|
timestamp: new Date(displayTimestamp).toISOString(),
|
|
846
937
|
attachments: msgAttachments,
|
|
847
|
-
...(
|
|
848
|
-
...(
|
|
849
|
-
...(
|
|
850
|
-
...(
|
|
851
|
-
? { thinkingSegments:
|
|
938
|
+
...(toolCalls.length > 0 ? { toolCalls } : {}),
|
|
939
|
+
...(rendered.surfaces.length > 0 ? { surfaces: rendered.surfaces } : {}),
|
|
940
|
+
...(textSegments.length > 0 ? { textSegments } : {}),
|
|
941
|
+
...(rendered.thinkingSegments.length > 0
|
|
942
|
+
? { thinkingSegments: rendered.thinkingSegments }
|
|
852
943
|
: {}),
|
|
853
944
|
...(alignedContentOrder.length > 0
|
|
854
945
|
? { contentOrder: alignedContentOrder }
|
|
855
946
|
: {}),
|
|
947
|
+
...(contentBlocks.length > 0 ? { contentBlocks } : {}),
|
|
856
948
|
...(m.subagentNotification
|
|
857
949
|
? { subagentNotification: m.subagentNotification }
|
|
858
950
|
: {}),
|
|
@@ -860,6 +952,13 @@ export function handleListMessages({
|
|
|
860
952
|
};
|
|
861
953
|
});
|
|
862
954
|
|
|
955
|
+
// Snapshot↔stream alignment token: the `seq` of the last event whose
|
|
956
|
+
// content is durably persisted for this conversation in the current
|
|
957
|
+
// daemon process. Returned on every resolved-conversation response so a
|
|
958
|
+
// client can apply only stream events with a higher `seq`. Null when
|
|
959
|
+
// nothing has been persisted in-process (cold/aged-out/post-restart).
|
|
960
|
+
const persistedSeq = getPersistedSeq(resolvedConversationId);
|
|
961
|
+
|
|
863
962
|
if (isPaginated) {
|
|
864
963
|
// Prefer the page's oldest visible row (the documented cursor semantic).
|
|
865
964
|
// When a scan-cap-truncated page comes back empty there's no visible row
|
|
@@ -881,6 +980,7 @@ export function handleListMessages({
|
|
|
881
980
|
hasMore,
|
|
882
981
|
oldestTimestamp: oldestTimestamp ?? null,
|
|
883
982
|
oldestMessageId: oldestMessageId ?? null,
|
|
983
|
+
seq: persistedSeq,
|
|
884
984
|
};
|
|
885
985
|
}
|
|
886
986
|
|
|
@@ -889,10 +989,11 @@ export function handleListMessages({
|
|
|
889
989
|
hasMore,
|
|
890
990
|
...(oldestTimestamp != null ? { oldestTimestamp } : {}),
|
|
891
991
|
...(oldestMessageId != null ? { oldestMessageId } : {}),
|
|
992
|
+
seq: persistedSeq,
|
|
892
993
|
};
|
|
893
994
|
}
|
|
894
995
|
|
|
895
|
-
return { messages };
|
|
996
|
+
return { messages, seq: persistedSeq };
|
|
896
997
|
}
|
|
897
998
|
|
|
898
999
|
/**
|
|
@@ -1446,7 +1547,7 @@ export async function handleSendMessage(
|
|
|
1446
1547
|
} else if (isWakeUp) {
|
|
1447
1548
|
const cannedGreeting = getCannedFirstGreeting(body.onboarding ?? undefined);
|
|
1448
1549
|
|
|
1449
|
-
conversation.
|
|
1550
|
+
conversation.setProcessing(true);
|
|
1450
1551
|
let cleanupDeferred = false;
|
|
1451
1552
|
try {
|
|
1452
1553
|
const rawContent = content ?? "";
|
|
@@ -1522,7 +1623,7 @@ export async function handleSendMessage(
|
|
|
1522
1623
|
persistedAssistant.id,
|
|
1523
1624
|
);
|
|
1524
1625
|
publishConversationMessagesChanged(conversationId, originClientId);
|
|
1525
|
-
conversation.
|
|
1626
|
+
conversation.setProcessing(false);
|
|
1526
1627
|
silentlyWithLog(
|
|
1527
1628
|
conversation.drainQueue(),
|
|
1528
1629
|
"canned-greeting queue drain",
|
|
@@ -1538,8 +1639,8 @@ export async function handleSendMessage(
|
|
|
1538
1639
|
cleanupDeferred = true;
|
|
1539
1640
|
return response;
|
|
1540
1641
|
} finally {
|
|
1541
|
-
if (!cleanupDeferred && conversation.
|
|
1542
|
-
conversation.
|
|
1642
|
+
if (!cleanupDeferred && conversation.isProcessing()) {
|
|
1643
|
+
conversation.setProcessing(false);
|
|
1543
1644
|
silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
|
|
1544
1645
|
}
|
|
1545
1646
|
}
|
|
@@ -1752,7 +1853,7 @@ export async function handleSendMessage(
|
|
|
1752
1853
|
const slashResult = await resolveSlash(rawContent, slashContext);
|
|
1753
1854
|
|
|
1754
1855
|
if (slashResult.kind === "unknown") {
|
|
1755
|
-
conversation.
|
|
1856
|
+
conversation.setProcessing(true);
|
|
1756
1857
|
let cleanupDeferred = false;
|
|
1757
1858
|
try {
|
|
1758
1859
|
const slashMeta = {
|
|
@@ -1833,7 +1934,7 @@ export async function handleSendMessage(
|
|
|
1833
1934
|
persistedAssistant.id,
|
|
1834
1935
|
);
|
|
1835
1936
|
publishConversationMessagesChanged(conversationId, originClientId);
|
|
1836
|
-
conversation.
|
|
1937
|
+
conversation.setProcessing(false);
|
|
1837
1938
|
silentlyWithLog(conversation.drainQueue(), "slash-command queue drain");
|
|
1838
1939
|
}, 0);
|
|
1839
1940
|
|
|
@@ -1842,15 +1943,15 @@ export async function handleSendMessage(
|
|
|
1842
1943
|
} finally {
|
|
1843
1944
|
// No-op for the slash-command early-return path (handled inside
|
|
1844
1945
|
// setTimeout above), but still needed for error paths.
|
|
1845
|
-
if (!cleanupDeferred && conversation.
|
|
1846
|
-
conversation.
|
|
1946
|
+
if (!cleanupDeferred && conversation.isProcessing()) {
|
|
1947
|
+
conversation.setProcessing(false);
|
|
1847
1948
|
silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
|
|
1848
1949
|
}
|
|
1849
1950
|
}
|
|
1850
1951
|
}
|
|
1851
1952
|
|
|
1852
1953
|
if (slashResult.kind === "compact") {
|
|
1853
|
-
conversation.
|
|
1954
|
+
conversation.setProcessing(true);
|
|
1854
1955
|
const slashMeta = {
|
|
1855
1956
|
userMessageChannel: sourceChannel,
|
|
1856
1957
|
assistantMessageChannel: sourceChannel,
|
|
@@ -1870,12 +1971,12 @@ export async function handleSendMessage(
|
|
|
1870
1971
|
// The fire-and-forget compaction below owns clearing `processing`, but a
|
|
1871
1972
|
// throw from this initial persist never reaches it — reset here so the
|
|
1872
1973
|
// conversation isn't stranded in queued mode.
|
|
1873
|
-
conversation.
|
|
1974
|
+
conversation.setProcessing(false);
|
|
1874
1975
|
silentlyWithLog(conversation.drainQueue(), "compact-command queue drain");
|
|
1875
1976
|
throw err;
|
|
1876
1977
|
}
|
|
1877
1978
|
if (persisted.deduplicated) {
|
|
1878
|
-
conversation.
|
|
1979
|
+
conversation.setProcessing(false);
|
|
1879
1980
|
silentlyWithLog(conversation.drainQueue(), "compact-dedup queue drain");
|
|
1880
1981
|
return {
|
|
1881
1982
|
accepted: true,
|
|
@@ -1904,9 +2005,7 @@ export async function handleSendMessage(
|
|
|
1904
2005
|
});
|
|
1905
2006
|
publishConversationMessagesChanged(conversationId, originClientId);
|
|
1906
2007
|
conversation.emitActivityState("thinking", "context_compacting");
|
|
1907
|
-
const result = await conversation.forceCompact(
|
|
1908
|
-
targetInputTokensOverride: slashResult.targetInputTokensOverride,
|
|
1909
|
-
});
|
|
2008
|
+
const result = await conversation.forceCompact();
|
|
1910
2009
|
const responseText = formatCompactResult(result);
|
|
1911
2010
|
|
|
1912
2011
|
const assistantMsg = createAssistantMessage(responseText);
|
|
@@ -1943,7 +2042,7 @@ export async function handleSendMessage(
|
|
|
1943
2042
|
retryable: true,
|
|
1944
2043
|
});
|
|
1945
2044
|
} finally {
|
|
1946
|
-
conversation.
|
|
2045
|
+
conversation.setProcessing(false);
|
|
1947
2046
|
silentlyWithLog(
|
|
1948
2047
|
conversation.drainQueue(),
|
|
1949
2048
|
"compact-command queue drain",
|
|
@@ -1959,7 +2058,7 @@ export async function handleSendMessage(
|
|
|
1959
2058
|
}
|
|
1960
2059
|
|
|
1961
2060
|
if (slashResult.kind === "clean") {
|
|
1962
|
-
conversation.
|
|
2061
|
+
conversation.setProcessing(true);
|
|
1963
2062
|
const conversationId = mapping.conversationId;
|
|
1964
2063
|
// Outer try/finally guarantees the processing flag is cleared (and the
|
|
1965
2064
|
// queue drained) on every failure path — including a throw from the
|
|
@@ -2045,7 +2144,7 @@ export async function handleSendMessage(
|
|
|
2045
2144
|
conversationId,
|
|
2046
2145
|
};
|
|
2047
2146
|
} finally {
|
|
2048
|
-
conversation.
|
|
2147
|
+
conversation.setProcessing(false);
|
|
2049
2148
|
silentlyWithLog(conversation.drainQueue(), "clean-command queue drain");
|
|
2050
2149
|
}
|
|
2051
2150
|
}
|
|
@@ -2465,8 +2564,46 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
2465
2564
|
description:
|
|
2466
2565
|
"Return messages for a conversation, including attachments and interface file metadata.",
|
|
2467
2566
|
tags: ["messages"],
|
|
2567
|
+
queryParams: [
|
|
2568
|
+
{
|
|
2569
|
+
name: "conversationId",
|
|
2570
|
+
type: "string",
|
|
2571
|
+
required: false,
|
|
2572
|
+
description:
|
|
2573
|
+
"Conversation UUID. One of conversationId or conversationKey is required.",
|
|
2574
|
+
},
|
|
2575
|
+
{
|
|
2576
|
+
name: "conversationKey",
|
|
2577
|
+
type: "string",
|
|
2578
|
+
required: false,
|
|
2579
|
+
description:
|
|
2580
|
+
"Channel/external conversation key. One of conversationId or conversationKey is required.",
|
|
2581
|
+
},
|
|
2582
|
+
{
|
|
2583
|
+
name: "page",
|
|
2584
|
+
type: "string",
|
|
2585
|
+
required: false,
|
|
2586
|
+
description:
|
|
2587
|
+
"When set to 'latest', returns the most recent page of messages with pagination metadata.",
|
|
2588
|
+
},
|
|
2589
|
+
{
|
|
2590
|
+
name: "beforeTimestamp",
|
|
2591
|
+
type: "integer",
|
|
2592
|
+
required: false,
|
|
2593
|
+
description:
|
|
2594
|
+
"Return messages older than this timestamp (ms since epoch). Used for paging older history.",
|
|
2595
|
+
},
|
|
2596
|
+
{
|
|
2597
|
+
name: "limit",
|
|
2598
|
+
type: "integer",
|
|
2599
|
+
required: false,
|
|
2600
|
+
description: "Maximum number of messages to return.",
|
|
2601
|
+
},
|
|
2602
|
+
],
|
|
2468
2603
|
responseBody: z.object({
|
|
2469
|
-
messages: z
|
|
2604
|
+
messages: z
|
|
2605
|
+
.array(ConversationMessageSchema)
|
|
2606
|
+
.describe("Array of message objects"),
|
|
2470
2607
|
hasMore: z
|
|
2471
2608
|
.boolean()
|
|
2472
2609
|
.optional()
|
|
@@ -2483,6 +2620,13 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
2483
2620
|
.nullable()
|
|
2484
2621
|
.optional()
|
|
2485
2622
|
.describe("ID of the oldest message in this page"),
|
|
2623
|
+
seq: z
|
|
2624
|
+
.number()
|
|
2625
|
+
.nullable()
|
|
2626
|
+
.optional()
|
|
2627
|
+
.describe(
|
|
2628
|
+
"Global SSE `seq` of the last event whose content is durably persisted for this conversation in the current daemon process. A client can align this snapshot with the `/events` stream by applying only events with `seq` greater than this value. Null when no events have been persisted in this process (cold conversation, after a daemon restart, or when the conversation has aged out of the in-memory map) — clients should cold-start in that case. Absent on older daemons that predate this field.",
|
|
2629
|
+
),
|
|
2486
2630
|
}),
|
|
2487
2631
|
handler: (args) => handleListMessages(args),
|
|
2488
2632
|
},
|
|
@@ -2500,17 +2644,60 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
2500
2644
|
tags: ["messages"],
|
|
2501
2645
|
responseStatus: "202",
|
|
2502
2646
|
requestBody: z.object({
|
|
2503
|
-
|
|
2647
|
+
conversationId: z
|
|
2648
|
+
.string()
|
|
2649
|
+
.nullable()
|
|
2650
|
+
.optional()
|
|
2651
|
+
.describe(
|
|
2652
|
+
"Internal conversation id (0.8.6+ strict lookup). Omit both id and key to mint a new conversation server-side.",
|
|
2653
|
+
),
|
|
2654
|
+
conversationKey: z.string().nullable().optional(),
|
|
2504
2655
|
content: z.string().describe("Message text content"),
|
|
2505
2656
|
attachments: z
|
|
2506
2657
|
.array(z.unknown())
|
|
2507
|
-
.describe("Optional file attachments")
|
|
2658
|
+
.describe("Optional inline file attachments")
|
|
2659
|
+
.optional(),
|
|
2660
|
+
attachmentIds: z
|
|
2661
|
+
.array(z.string())
|
|
2662
|
+
.describe("Ids of previously uploaded attachments to attach")
|
|
2508
2663
|
.optional(),
|
|
2664
|
+
sourceChannel: z
|
|
2665
|
+
.string()
|
|
2666
|
+
.describe('Originating channel id (e.g. "vellum")'),
|
|
2667
|
+
interface: z
|
|
2668
|
+
.string()
|
|
2669
|
+
.describe('Originating interface id (e.g. "vellum")'),
|
|
2509
2670
|
conversationType: z.string().optional(),
|
|
2510
2671
|
slashCommand: z.string().optional(),
|
|
2511
2672
|
clientTimezone: z.string().optional(),
|
|
2512
2673
|
inferenceProfile: z.string().nullable().optional(),
|
|
2513
2674
|
riskThreshold: z.enum(VALID_RISK_THRESHOLDS).optional(),
|
|
2675
|
+
onboarding: z
|
|
2676
|
+
.object({
|
|
2677
|
+
tools: z.array(z.string()),
|
|
2678
|
+
tasks: z.array(z.string()),
|
|
2679
|
+
tone: z.string(),
|
|
2680
|
+
userName: z.string().optional(),
|
|
2681
|
+
assistantName: z.string().optional(),
|
|
2682
|
+
googleConnected: z.boolean().optional(),
|
|
2683
|
+
googleScopes: z.array(z.string()).optional(),
|
|
2684
|
+
priorAssistants: z.array(z.string()).optional(),
|
|
2685
|
+
cohort: z.string().optional(),
|
|
2686
|
+
websiteUrl: z.string().optional(),
|
|
2687
|
+
contentSourceUrl: z.string().optional(),
|
|
2688
|
+
bootstrapTemplate: z.string().optional(),
|
|
2689
|
+
initialMessage: z.string().optional(),
|
|
2690
|
+
skills: z.array(z.string()).optional(),
|
|
2691
|
+
})
|
|
2692
|
+
.describe("PreChat onboarding context, sent on the first message only")
|
|
2693
|
+
.optional(),
|
|
2694
|
+
}),
|
|
2695
|
+
responseBody: z.object({
|
|
2696
|
+
accepted: z.boolean(),
|
|
2697
|
+
conversationId: z.string().optional(),
|
|
2698
|
+
messageId: z.string().optional(),
|
|
2699
|
+
queued: z.boolean().optional(),
|
|
2700
|
+
requestId: z.string().optional(),
|
|
2514
2701
|
}),
|
|
2515
2702
|
handler: async (args) =>
|
|
2516
2703
|
handleSendMessage(args, {
|