@vellumai/assistant 0.6.5 → 0.6.6
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/AGENTS.md +9 -1
- package/ARCHITECTURE.md +15 -17
- package/Dockerfile +6 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/docs/architecture/integrations.md +32 -39
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +7 -6
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/plugins.md +761 -0
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +212 -68
- package/package.json +1 -1
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +7 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/catalog-cache.test.ts +69 -0
- package/src/__tests__/checker.test.ts +459 -171
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema.test.ts +22 -9
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-window-manager.test.ts +355 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
- package/src/__tests__/conversation-agent-loop.test.ts +30 -141
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
- package/src/__tests__/conversation-process-callsite.test.ts +3 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
- package/src/__tests__/conversation-queue.test.ts +29 -14
- package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
- package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-slash-queue.test.ts +7 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
- package/src/__tests__/conversation-speed-override.test.ts +6 -1
- package/src/__tests__/conversation-title-service.test.ts +116 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +2 -2
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +0 -26
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +5 -68
- package/src/__tests__/model-intents.test.ts +4 -2
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +41 -76
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +44 -12
- package/src/__tests__/proxy-approval-callback.test.ts +69 -8
- package/src/__tests__/reaction-persistence.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/suggestion-routes.test.ts +103 -4
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +141 -0
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-policy.test.ts +21 -3
- package/src/agent/loop.ts +340 -102
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
- package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -9
- package/src/cli/commands/conversations.ts +55 -7
- package/src/cli/commands/image-generation.ts +33 -34
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/program.ts +25 -29
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-tool-registry.ts +0 -15
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +19 -0
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/security.ts +6 -6
- package/src/config/schemas/tts.ts +11 -0
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +94 -5
- package/src/context/__tests__/compact-prompt.test.ts +27 -9
- package/src/context/prompts/compact.md +26 -12
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +190 -16
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/config-watcher.ts +0 -2
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
- package/src/daemon/conversation-agent-loop.ts +984 -683
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +37 -19
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +14 -7
- package/src/daemon/conversation-runtime-assembly.ts +532 -411
- package/src/daemon/conversation-tool-setup.ts +41 -4
- package/src/daemon/conversation.ts +80 -35
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/handlers/config-model.ts +11 -0
- package/src/daemon/handlers/skills.ts +5 -1
- package/src/daemon/lifecycle.ts +33 -68
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +49 -0
- package/src/daemon/message-types/messages.ts +12 -0
- package/src/daemon/server.ts +5 -3
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -56
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +24 -1
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/emit-feed-event.ts +7 -0
- package/src/home/feed-types.ts +41 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/ipc/__tests__/socket-path.test.ts +11 -50
- package/src/ipc/cli-client.ts +1 -1
- package/src/ipc/cli-server.ts +3 -3
- package/src/ipc/gateway-client.ts +4 -1
- package/src/ipc/routes/browser-context.ts +2 -0
- package/src/ipc/routes/browser.ts +1 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +14 -0
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/socket-path.ts +14 -38
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/conversation-crud.ts +48 -18
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +25 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/extraction.ts +10 -2
- package/src/memory/graph/graph-search.test.ts +1 -0
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/retriever.ts +10 -3
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/pkb/pkb-index.test.ts +1 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +65 -4
- package/src/memory/pkb/pkb-search.ts +40 -18
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +25 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
- package/src/messaging/providers/slack/render-transcript.ts +58 -0
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +30 -14
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -108
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +293 -18
- package/src/permissions/approval-policy.ts +110 -58
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +414 -2
- package/src/permissions/bash-risk-classifier.ts +303 -60
- package/src/permissions/checker.ts +157 -29
- package/src/permissions/command-registry.test.ts +239 -0
- package/src/permissions/command-registry.ts +234 -54
- package/src/permissions/defaults.ts +5 -4
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +61 -4
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/types.ts +2 -0
- package/src/permissions/workspace-policy.ts +8 -3
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/providers/model-catalog.ts +52 -29
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
- package/src/providers/speech-to-text/xai-realtime.ts +39 -14
- package/src/runtime/AGENTS.md +25 -16
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/http-server.ts +77 -8
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
- package/src/runtime/routes/approval-routes.ts +17 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/conversation-routes.ts +223 -116
- package/src/runtime/routes/inbound-message-handler.ts +88 -13
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +0 -3
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/skill-route-registry.ts +75 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +48 -8
- package/src/skills/catalog-cache.ts +12 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
- package/src/tools/browser/browser-execution.ts +88 -19
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/executor.ts +126 -74
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/permission-checker.ts +98 -49
- package/src/tools/policy-context.ts +4 -0
- package/src/tools/registry.ts +140 -3
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/types.ts +28 -2
- package/src/util/platform.ts +7 -2
- package/src/util/pricing.ts +26 -3
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route handlers for conversation messages and suggestions.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
existsSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
statSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
5
11
|
import { join, relative } from "node:path";
|
|
6
12
|
|
|
7
13
|
import { z } from "zod";
|
|
@@ -48,10 +54,12 @@ import type {
|
|
|
48
54
|
NonHostProxyTransportMetadata,
|
|
49
55
|
} from "../../daemon/message-types/conversations.js";
|
|
50
56
|
import type { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
|
|
57
|
+
import { emitFeedEvent } from "../../home/emit-feed-event.js";
|
|
51
58
|
import {
|
|
52
59
|
writeOnboardingSidecar,
|
|
53
60
|
writeRelationshipState,
|
|
54
61
|
} from "../../home/relationship-state-writer.js";
|
|
62
|
+
import { rewriteCommandPreview } from "../../home/rewrite-command-preview.js";
|
|
55
63
|
import * as attachmentsStore from "../../memory/attachments-store.js";
|
|
56
64
|
import {
|
|
57
65
|
createCanonicalGuardianRequest,
|
|
@@ -88,6 +96,7 @@ import { buildAssistantEvent } from "../assistant-event.js";
|
|
|
88
96
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
89
97
|
import type { AuthContext } from "../auth/types.js";
|
|
90
98
|
import { getChromeExtensionRegistry } from "../chrome-extension-registry.js";
|
|
99
|
+
import { getClientRegistry } from "../client-registry.js";
|
|
91
100
|
import { bridgeConfirmationRequestToGuardian } from "../confirmation-request-guardian-bridge.js";
|
|
92
101
|
import { routeGuardianReply } from "../guardian-reply-router.js";
|
|
93
102
|
import { healGuardianBindingDrift } from "../guardian-vellum-migration.js";
|
|
@@ -998,6 +1007,54 @@ function makeHubPublisher(
|
|
|
998
1007
|
},
|
|
999
1008
|
});
|
|
1000
1009
|
|
|
1010
|
+
const inputRecord = msg.input as Record<string, unknown>;
|
|
1011
|
+
const commandPreview =
|
|
1012
|
+
redactSecrets(summarizeToolInput(msg.toolName, inputRecord)) ||
|
|
1013
|
+
undefined;
|
|
1014
|
+
const technicalTitle = commandPreview
|
|
1015
|
+
? `Requesting permission: ${commandPreview}`
|
|
1016
|
+
: `Requesting approval to use ${msg.toolName}.`;
|
|
1017
|
+
const dedupKey = `tool-approval:${msg.requestId}`;
|
|
1018
|
+
|
|
1019
|
+
// Emit immediately with the technical preview.
|
|
1020
|
+
void emitFeedEvent({
|
|
1021
|
+
source: "assistant",
|
|
1022
|
+
title: technicalTitle,
|
|
1023
|
+
summary: technicalTitle,
|
|
1024
|
+
dedupKey,
|
|
1025
|
+
urgency: msg.riskLevel === "high" ? "high" : "medium",
|
|
1026
|
+
conversationId,
|
|
1027
|
+
}).catch((err) => {
|
|
1028
|
+
log.warn(
|
|
1029
|
+
{ err, requestId: msg.requestId },
|
|
1030
|
+
"Failed to emit tool approval request feed event",
|
|
1031
|
+
);
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
// Background: rewrite into prose and update the feed item.
|
|
1035
|
+
if (commandPreview) {
|
|
1036
|
+
void rewriteCommandPreview(msg.toolName, commandPreview)
|
|
1037
|
+
.then((prose) => {
|
|
1038
|
+
if (prose) {
|
|
1039
|
+
const proseTitle = `Requesting permission: ${prose}`;
|
|
1040
|
+
return emitFeedEvent({
|
|
1041
|
+
source: "assistant",
|
|
1042
|
+
title: proseTitle,
|
|
1043
|
+
summary: proseTitle,
|
|
1044
|
+
dedupKey,
|
|
1045
|
+
urgency: msg.riskLevel === "high" ? "high" : "medium",
|
|
1046
|
+
conversationId,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
})
|
|
1050
|
+
.catch((err) => {
|
|
1051
|
+
log.warn(
|
|
1052
|
+
{ err, requestId: msg.requestId },
|
|
1053
|
+
"Failed to update feed event with prose rewrite",
|
|
1054
|
+
);
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1001
1058
|
// Create a canonical guardian request so HTTP handlers can find it
|
|
1002
1059
|
// via applyCanonicalGuardianDecision.
|
|
1003
1060
|
try {
|
|
@@ -1271,7 +1328,7 @@ function resolveHostBrowserSender(
|
|
|
1271
1328
|
* failure. The route handler path must never reject because of a
|
|
1272
1329
|
* best-effort persistence step.
|
|
1273
1330
|
*/
|
|
1274
|
-
function persistOnboardingArtifacts(onboarding: {
|
|
1331
|
+
export function persistOnboardingArtifacts(onboarding: {
|
|
1275
1332
|
tools: string[];
|
|
1276
1333
|
tasks: string[];
|
|
1277
1334
|
tone: string;
|
|
@@ -1283,31 +1340,49 @@ function persistOnboardingArtifacts(onboarding: {
|
|
|
1283
1340
|
const assistantName = onboarding.assistantName?.trim();
|
|
1284
1341
|
if (assistantName) {
|
|
1285
1342
|
const identityPath = getWorkspacePromptPath("IDENTITY.md");
|
|
1286
|
-
|
|
1287
|
-
|
|
1343
|
+
try {
|
|
1344
|
+
if (existsSync(identityPath)) {
|
|
1345
|
+
const content = readFileSync(identityPath, "utf-8");
|
|
1346
|
+
const updated = content.replace(
|
|
1347
|
+
/^- (?:\*\*)?Name:(?:\*\*)?\s*.*$/m,
|
|
1348
|
+
() => `- **Name:** ${assistantName}`,
|
|
1349
|
+
);
|
|
1350
|
+
if (updated !== content) {
|
|
1351
|
+
writeFileSync(identityPath, updated, "utf-8");
|
|
1352
|
+
}
|
|
1353
|
+
} else {
|
|
1288
1354
|
writeFileSync(
|
|
1289
1355
|
identityPath,
|
|
1290
|
-
`# Identity\n\n- Name
|
|
1356
|
+
`# Identity\n\n- **Name:** ${assistantName}\n`,
|
|
1291
1357
|
"utf-8",
|
|
1292
1358
|
);
|
|
1293
|
-
} catch (err) {
|
|
1294
|
-
log.warn(
|
|
1295
|
-
{ err, identityPath },
|
|
1296
|
-
"Failed to seed IDENTITY.md from onboarding",
|
|
1297
|
-
);
|
|
1298
1359
|
}
|
|
1360
|
+
} catch (err) {
|
|
1361
|
+
log.warn(
|
|
1362
|
+
{ err, identityPath },
|
|
1363
|
+
"Failed to seed IDENTITY.md from onboarding",
|
|
1364
|
+
);
|
|
1299
1365
|
}
|
|
1300
1366
|
}
|
|
1301
1367
|
|
|
1302
1368
|
const userName = onboarding.userName?.trim();
|
|
1303
1369
|
if (userName) {
|
|
1304
1370
|
const userPath = getWorkspacePromptPath("USER.md");
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1371
|
+
try {
|
|
1372
|
+
if (existsSync(userPath)) {
|
|
1373
|
+
const content = readFileSync(userPath, "utf-8");
|
|
1374
|
+
const updated = content.replace(
|
|
1375
|
+
/^- (?:\*\*)?Name:(?:\*\*)?\s*.*$/m,
|
|
1376
|
+
() => `- **Name:** ${userName}`,
|
|
1377
|
+
);
|
|
1378
|
+
if (updated !== content) {
|
|
1379
|
+
writeFileSync(userPath, updated, "utf-8");
|
|
1380
|
+
}
|
|
1381
|
+
} else {
|
|
1382
|
+
writeFileSync(userPath, `# User\n\n- **Name:** ${userName}\n`, "utf-8");
|
|
1310
1383
|
}
|
|
1384
|
+
} catch (err) {
|
|
1385
|
+
log.warn({ err, userPath }, "Failed to seed USER.md from onboarding");
|
|
1311
1386
|
}
|
|
1312
1387
|
}
|
|
1313
1388
|
|
|
@@ -1339,6 +1414,7 @@ export async function handleSendMessage(
|
|
|
1339
1414
|
bypassSecretCheck?: boolean;
|
|
1340
1415
|
hostHomeDir?: string;
|
|
1341
1416
|
hostUsername?: string;
|
|
1417
|
+
clientId?: string;
|
|
1342
1418
|
clientMessageId?: string;
|
|
1343
1419
|
onboarding?: {
|
|
1344
1420
|
tools: string[];
|
|
@@ -1495,20 +1571,37 @@ export async function handleSendMessage(
|
|
|
1495
1571
|
interfaceId: sourceInterface,
|
|
1496
1572
|
} satisfies NonHostProxyTransportMetadata);
|
|
1497
1573
|
|
|
1574
|
+
// Register/refresh the client in the unified client registry so
|
|
1575
|
+
// `assistant clients list` can discover all connected interfaces.
|
|
1576
|
+
// Uses the client-supplied clientId when available (stable per-install
|
|
1577
|
+
// UUID), falling back to a synthetic key derived from interfaceId so
|
|
1578
|
+
// older clients that don't send clientId still appear in the registry.
|
|
1579
|
+
const effectiveClientId =
|
|
1580
|
+
typeof body.clientId === "string" && body.clientId.length > 0
|
|
1581
|
+
? body.clientId
|
|
1582
|
+
: `synthetic:${sourceInterface}`;
|
|
1583
|
+
getClientRegistry().register({
|
|
1584
|
+
clientId: effectiveClientId,
|
|
1585
|
+
interfaceId: sourceInterface,
|
|
1586
|
+
hostHomeDir: body.hostHomeDir,
|
|
1587
|
+
hostUsername: body.hostUsername,
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1498
1590
|
const conversation = await smDeps.getOrCreateConversation(
|
|
1499
1591
|
mapping.conversationId,
|
|
1500
1592
|
{ transport },
|
|
1501
1593
|
);
|
|
1502
1594
|
|
|
1503
1595
|
// Store pre-chat onboarding context on the conversation when this is the
|
|
1504
|
-
// very first message (no prior messages loaded).
|
|
1505
|
-
//
|
|
1506
|
-
//
|
|
1507
|
-
//
|
|
1508
|
-
//
|
|
1509
|
-
|
|
1510
|
-
conversation.
|
|
1511
|
-
|
|
1596
|
+
// very first message (no prior messages loaded). Artifact persistence
|
|
1597
|
+
// (IDENTITY.md, USER.md, sidecar) is deferred: on the canned greeting
|
|
1598
|
+
// path it runs inside the setTimeout right before warmPromptCache() so
|
|
1599
|
+
// the warmed system prompt includes the identity; on the normal LLM
|
|
1600
|
+
// path it runs immediately before inference starts.
|
|
1601
|
+
const isFirstOnboarding =
|
|
1602
|
+
!!body.onboarding && conversation.messages.length === 0;
|
|
1603
|
+
if (isFirstOnboarding) {
|
|
1604
|
+
conversation.setOnboardingContext(body.onboarding!);
|
|
1512
1605
|
}
|
|
1513
1606
|
|
|
1514
1607
|
// Resolve guardian context from the AuthContext's actorPrincipalId.
|
|
@@ -1618,14 +1711,13 @@ export async function handleSendMessage(
|
|
|
1618
1711
|
conversation.hostBrowserSenderOverride = undefined;
|
|
1619
1712
|
}
|
|
1620
1713
|
|
|
1621
|
-
// Provision the host browser proxy.
|
|
1622
|
-
//
|
|
1623
|
-
//
|
|
1624
|
-
//
|
|
1625
|
-
//
|
|
1626
|
-
//
|
|
1627
|
-
//
|
|
1628
|
-
// provisioning and browser tools fall through to cdp-inspect/local.
|
|
1714
|
+
// Provision the host browser proxy. Both macOS and chrome-extension
|
|
1715
|
+
// natively support host_browser. For macOS, the proxy is wired to the
|
|
1716
|
+
// SSE sender by default so `host_browser_request` frames reach the
|
|
1717
|
+
// desktop client directly. When the guardian also has an active extension
|
|
1718
|
+
// connection (isRegistryRouted), the registry-routed sender is used
|
|
1719
|
+
// instead so browser tools route through the user's real Chrome session.
|
|
1720
|
+
// For chrome-extension, the registry sender is always used.
|
|
1629
1721
|
const shouldProvisionBrowserProxy =
|
|
1630
1722
|
supportsHostProxy(sourceInterface, "host_browser") ||
|
|
1631
1723
|
(canServiceRegistryBrowser(sourceInterface) && isRegistryRouted);
|
|
@@ -1688,9 +1780,10 @@ export async function handleSendMessage(
|
|
|
1688
1780
|
skipProxySenderUpdate: preservingProxies,
|
|
1689
1781
|
});
|
|
1690
1782
|
// Re-enable the browser proxy for turns that provisioned one. This covers:
|
|
1783
|
+
// - macOS: always provisioned (SSE sender or registry-routed when extension
|
|
1784
|
+
// is connected)
|
|
1691
1785
|
// - chrome-extension: natively supports host_browser (non-interactive but
|
|
1692
1786
|
// has a connected client for host_browser_request events)
|
|
1693
|
-
// - macOS with extension: provisioned above when isRegistryRouted is true
|
|
1694
1787
|
//
|
|
1695
1788
|
// The helper bypasses the `hasNoClient` gate so chrome-extension turns can
|
|
1696
1789
|
// drive the browser via CDP without leaking host_bash/host_file tool
|
|
@@ -1703,97 +1796,102 @@ export async function handleSendMessage(
|
|
|
1703
1796
|
|
|
1704
1797
|
// ── Canned first-greeting fast path ──
|
|
1705
1798
|
// On a completely fresh workspace, skip LLM inference for the macOS
|
|
1706
|
-
// wake-up greeting and return a pre-written response.
|
|
1707
|
-
//
|
|
1799
|
+
// wake-up greeting and return a pre-written response. When onboarding
|
|
1800
|
+
// context is present the greeting is personalized using the selections;
|
|
1801
|
+
// otherwise a generic greeting is served. Both paths are instant.
|
|
1708
1802
|
if (isWakeUpGreeting(trimmedContent, conversation.getMessages().length)) {
|
|
1709
|
-
const cannedGreeting = getCannedFirstGreeting();
|
|
1710
|
-
if (cannedGreeting) {
|
|
1711
|
-
conversation.processing = true;
|
|
1712
|
-
let cleanupDeferred = false;
|
|
1713
|
-
try {
|
|
1714
|
-
const provenance = provenanceFromTrustContext(
|
|
1715
|
-
conversation.trustContext,
|
|
1716
|
-
);
|
|
1717
|
-
const channelMeta = {
|
|
1718
|
-
...provenance,
|
|
1719
|
-
userMessageChannel: sourceChannel,
|
|
1720
|
-
assistantMessageChannel: sourceChannel,
|
|
1721
|
-
userMessageInterface: sourceInterface,
|
|
1722
|
-
assistantMessageInterface: sourceInterface,
|
|
1723
|
-
};
|
|
1724
|
-
|
|
1725
|
-
const rawContent = content ?? "";
|
|
1726
|
-
const attachments = hasAttachments
|
|
1727
|
-
? smDeps.resolveAttachments(attachmentIds)
|
|
1728
|
-
: [];
|
|
1729
|
-
const userMsg = createUserMessage(rawContent, attachments);
|
|
1730
|
-
const persisted = await addMessage(
|
|
1731
|
-
mapping.conversationId,
|
|
1732
|
-
"user",
|
|
1733
|
-
JSON.stringify(userMsg.content),
|
|
1734
|
-
channelMeta,
|
|
1735
|
-
);
|
|
1736
|
-
conversation.getMessages().push(userMsg);
|
|
1803
|
+
const cannedGreeting = getCannedFirstGreeting(body.onboarding ?? undefined);
|
|
1737
1804
|
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1805
|
+
conversation.processing = true;
|
|
1806
|
+
let cleanupDeferred = false;
|
|
1807
|
+
try {
|
|
1808
|
+
const provenance = provenanceFromTrustContext(conversation.trustContext);
|
|
1809
|
+
const channelMeta = {
|
|
1810
|
+
...provenance,
|
|
1811
|
+
userMessageChannel: sourceChannel,
|
|
1812
|
+
assistantMessageChannel: sourceChannel,
|
|
1813
|
+
userMessageInterface: sourceInterface,
|
|
1814
|
+
assistantMessageInterface: sourceInterface,
|
|
1815
|
+
};
|
|
1746
1816
|
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1817
|
+
const rawContent = content ?? "";
|
|
1818
|
+
const attachments = hasAttachments
|
|
1819
|
+
? smDeps.resolveAttachments(attachmentIds)
|
|
1820
|
+
: [];
|
|
1821
|
+
const userMsg = createUserMessage(rawContent, attachments);
|
|
1822
|
+
const persisted = await addMessage(
|
|
1823
|
+
mapping.conversationId,
|
|
1824
|
+
"user",
|
|
1825
|
+
JSON.stringify(userMsg.content),
|
|
1826
|
+
channelMeta,
|
|
1827
|
+
);
|
|
1828
|
+
conversation.getMessages().push(userMsg);
|
|
1755
1829
|
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1830
|
+
setConversationOriginChannelIfUnset(
|
|
1831
|
+
mapping.conversationId,
|
|
1832
|
+
sourceChannel,
|
|
1833
|
+
);
|
|
1834
|
+
setConversationOriginInterfaceIfUnset(
|
|
1835
|
+
mapping.conversationId,
|
|
1836
|
+
sourceInterface,
|
|
1837
|
+
);
|
|
1761
1838
|
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
conversation.drainQueue(),
|
|
1778
|
-
"canned-greeting queue drain",
|
|
1779
|
-
);
|
|
1780
|
-
}, 0);
|
|
1839
|
+
const conversationId = mapping.conversationId;
|
|
1840
|
+
|
|
1841
|
+
const assistantMsg = createAssistantMessage(cannedGreeting);
|
|
1842
|
+
await addMessage(
|
|
1843
|
+
mapping.conversationId,
|
|
1844
|
+
"assistant",
|
|
1845
|
+
JSON.stringify(assistantMsg.content),
|
|
1846
|
+
channelMeta,
|
|
1847
|
+
);
|
|
1848
|
+
conversation.getMessages().push(assistantMsg);
|
|
1849
|
+
|
|
1850
|
+
const response = Response.json(
|
|
1851
|
+
{ accepted: true, messageId: persisted.id, conversationId },
|
|
1852
|
+
{ status: 202 },
|
|
1853
|
+
);
|
|
1781
1854
|
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1855
|
+
setTimeout(() => {
|
|
1856
|
+
onEvent({
|
|
1857
|
+
type: "user_message_echo",
|
|
1858
|
+
text: rawContent,
|
|
1859
|
+
conversationId,
|
|
1860
|
+
messageId: persisted.id,
|
|
1861
|
+
clientMessageId,
|
|
1862
|
+
});
|
|
1863
|
+
onEvent({ type: "assistant_text_delta", text: cannedGreeting });
|
|
1864
|
+
onEvent({ type: "message_complete", conversationId });
|
|
1865
|
+
conversation.processing = false;
|
|
1866
|
+
silentlyWithLog(
|
|
1867
|
+
conversation.drainQueue(),
|
|
1868
|
+
"canned-greeting queue drain",
|
|
1785
1869
|
);
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
if (!cleanupDeferred && conversation.processing) {
|
|
1790
|
-
conversation.processing = false;
|
|
1791
|
-
silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
|
|
1870
|
+
|
|
1871
|
+
if (isFirstOnboarding) {
|
|
1872
|
+
persistOnboardingArtifacts(body.onboarding!);
|
|
1792
1873
|
}
|
|
1874
|
+
conversation.warmPromptCache();
|
|
1875
|
+
}, 0);
|
|
1876
|
+
|
|
1877
|
+
log.info(
|
|
1878
|
+
{ conversationId, personalized: !!body.onboarding },
|
|
1879
|
+
"Served canned first greeting — skipped LLM inference",
|
|
1880
|
+
);
|
|
1881
|
+
cleanupDeferred = true;
|
|
1882
|
+
return response;
|
|
1883
|
+
} finally {
|
|
1884
|
+
if (!cleanupDeferred && conversation.processing) {
|
|
1885
|
+
conversation.processing = false;
|
|
1886
|
+
silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
|
|
1793
1887
|
}
|
|
1794
1888
|
}
|
|
1795
1889
|
}
|
|
1796
1890
|
|
|
1891
|
+
if (isFirstOnboarding) {
|
|
1892
|
+
persistOnboardingArtifacts(body.onboarding!);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1797
1895
|
const attachments = hasAttachments
|
|
1798
1896
|
? smDeps.resolveAttachments(attachmentIds)
|
|
1799
1897
|
: [];
|
|
@@ -2251,11 +2349,15 @@ async function generateLlmSuggestion(
|
|
|
2251
2349
|
`Write the user's next reply, focusing on the LAST question or call-to-action in the assistant message. Keep it short (under 15 words), casual, and in the user's voice. Respond in this exact format:\n\n` +
|
|
2252
2350
|
`<reply>YOUR_REPLY_HERE</reply>`;
|
|
2253
2351
|
|
|
2352
|
+
// Single user message only — no assistant-role prefill. Anthropic
|
|
2353
|
+
// rejects assistant prefill whenever the request triggers extended
|
|
2354
|
+
// thinking (e.g. Opus 4.x at `effort: "xhigh"`), and the call-site
|
|
2355
|
+
// config is user-controlled, so we can't statically guarantee a
|
|
2356
|
+
// prefill-safe model. Keep `stop_sequences: ["</reply>"]` as an
|
|
2357
|
+
// early-termination hint; the parser below handles both tagged and
|
|
2358
|
+
// untagged responses so untagged "casual answer" replies still work.
|
|
2254
2359
|
const response = await provider.sendMessage(
|
|
2255
|
-
[
|
|
2256
|
-
{ role: "user", content: [{ type: "text", text: userPrompt }] },
|
|
2257
|
-
{ role: "assistant", content: [{ type: "text", text: "<reply>" }] },
|
|
2258
|
-
],
|
|
2360
|
+
[{ role: "user", content: [{ type: "text", text: userPrompt }] }],
|
|
2259
2361
|
[], // no tools
|
|
2260
2362
|
systemPrompt,
|
|
2261
2363
|
{
|
|
@@ -2270,7 +2372,12 @@ async function generateLlmSuggestion(
|
|
|
2270
2372
|
|
|
2271
2373
|
const textBlock = response.content.find((b) => b.type === "text");
|
|
2272
2374
|
const raw = textBlock && "text" in textBlock ? textBlock.text : "";
|
|
2273
|
-
|
|
2375
|
+
// Prefer the content inside <reply>…</reply> when the model honors the
|
|
2376
|
+
// tag format. If the response has no tags, fall back to the raw text —
|
|
2377
|
+
// a plain "Sure, tomorrow works" without tags is still a valid chip.
|
|
2378
|
+
const tagMatch = raw.match(/<reply>([\s\S]*?)(?:<\/reply>|$)/i);
|
|
2379
|
+
const extracted = tagMatch ? tagMatch[1] : raw;
|
|
2380
|
+
const stripped = extracted
|
|
2274
2381
|
.replace(/<\/?reply>/gi, "")
|
|
2275
2382
|
.replace(/^["'`]+|["'`]+$/g, "")
|
|
2276
2383
|
.trim();
|
|
@@ -21,9 +21,9 @@ import {
|
|
|
21
21
|
} from "../../memory/conversation-attention-store.js";
|
|
22
22
|
import {
|
|
23
23
|
addMessage,
|
|
24
|
-
countMessagesWithSlackMeta,
|
|
25
24
|
getMessageById,
|
|
26
25
|
getMessages,
|
|
26
|
+
selectSlackMetaCandidateMetadata,
|
|
27
27
|
updateMessageMetadata,
|
|
28
28
|
} from "../../memory/conversation-crud.js";
|
|
29
29
|
import * as deliveryChannels from "../../memory/delivery-channels.js";
|
|
@@ -1049,14 +1049,21 @@ export async function handleChannelInbound(
|
|
|
1049
1049
|
// longer trigger backfill. Failures are non-fatal — the new message
|
|
1050
1050
|
// proceeds without backfilled history.
|
|
1051
1051
|
if (sourceChannel === "slack" && sourceChatType === "im") {
|
|
1052
|
+
// Exclude the just-arrived webhook message from the history window —
|
|
1053
|
+
// the normal inbound persistence path writes it separately, so
|
|
1054
|
+
// including it here would produce duplicate user turns. Only pass a
|
|
1055
|
+
// bound when we actually have a Slack ts (`<secs>.<micros>`): the
|
|
1056
|
+
// fallback path writes `externalMessageId` into `channelTs`, but that
|
|
1057
|
+
// identifier is not guaranteed to be a Slack ts, and Slack's
|
|
1058
|
+
// `conversations.history` rejects anything that isn't a ts string.
|
|
1059
|
+
const boundingTs = isSlackTs(sourceMessageId)
|
|
1060
|
+
? sourceMessageId
|
|
1061
|
+
: undefined;
|
|
1052
1062
|
await tryBackfillSlackDmIfCold({
|
|
1053
1063
|
conversationId: result.conversationId,
|
|
1054
1064
|
channelId: conversationExternalId,
|
|
1055
1065
|
account: slackAccount,
|
|
1056
|
-
|
|
1057
|
-
// the normal inbound persistence path writes it separately, so
|
|
1058
|
-
// including it here would produce duplicate user turns.
|
|
1059
|
-
latestTs: slackInbound?.channelTs,
|
|
1066
|
+
latestTs: boundingTs,
|
|
1060
1067
|
});
|
|
1061
1068
|
}
|
|
1062
1069
|
|
|
@@ -1245,16 +1252,84 @@ async function persistSlackReactionAsMessage(params: {
|
|
|
1245
1252
|
const SLACK_DM_BACKFILL_WARM_THRESHOLD = 3;
|
|
1246
1253
|
|
|
1247
1254
|
/**
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1255
|
+
* Shape-check for a Slack `ts` value. Slack IDs messages by `<seconds>.<micros>`
|
|
1256
|
+
* strings (e.g. `"1700000000.000100"`). The daemon also stores an
|
|
1257
|
+
* `externalMessageId` derived from the gateway's dedupe key which follows a
|
|
1258
|
+
* different format, so any path that feeds a ts to Slack's API
|
|
1259
|
+
* (`conversations.history`'s `latest`, etc.) must shape-check first — Slack
|
|
1260
|
+
* rejects non-ts arguments with `invalid_arguments`, and passing a malformed
|
|
1261
|
+
* bound silently disables the intended history window.
|
|
1262
|
+
*/
|
|
1263
|
+
function isSlackTs(value: string | null | undefined): value is string {
|
|
1264
|
+
return typeof value === "string" && /^\d+\.\d+$/.test(value);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
/**
|
|
1268
|
+
* Batch size used when pulling candidate rows from SQL. A bare
|
|
1269
|
+
* `LIKE '%"slackMeta"%'` match can include rows whose metadata JSON is
|
|
1270
|
+
* malformed or carries the literal under an unrelated key, so we fetch in
|
|
1271
|
+
* batches and re-validate each candidate with Zod. The threshold is tiny
|
|
1272
|
+
* (see `SLACK_DM_BACKFILL_WARM_THRESHOLD`), so a 10× batch is a trivial
|
|
1273
|
+
* scan while letting a handful of bad rows not starve the count.
|
|
1274
|
+
*/
|
|
1275
|
+
const SLACK_DM_CANDIDATE_BATCH_SIZE = SLACK_DM_BACKFILL_WARM_THRESHOLD * 10;
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* Absolute cap on candidate rows inspected per webhook to classify a DM as
|
|
1279
|
+
* warm. If this many substring matches have been examined without reaching
|
|
1280
|
+
* the valid-row threshold, treat the conversation as cold — a scan this
|
|
1281
|
+
* deep already dominates the critical-path budget and the cold-start
|
|
1282
|
+
* backfill path is itself idempotent against re-runs.
|
|
1283
|
+
*/
|
|
1284
|
+
const SLACK_DM_CANDIDATE_MAX_SCAN = SLACK_DM_BACKFILL_WARM_THRESHOLD * 20;
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* Count messages in a conversation whose `metadata` carries a well-formed
|
|
1288
|
+
* `slackMeta` envelope, capped at the warm threshold. SQL prefilters with
|
|
1289
|
+
* `LIKE` + `LIMIT`/`OFFSET` so warm DM conversations never scan the full
|
|
1290
|
+
* table on the webhook critical path, and each candidate is re-validated
|
|
1291
|
+
* through `readSlackMetadata` — a bare substring match would otherwise
|
|
1292
|
+
* wrongly count rows whose metadata is truncated, parses but fails schema
|
|
1293
|
+
* validation, or happens to contain the literal `"slackMeta"` under an
|
|
1294
|
+
* unrelated key. Pulls candidates in batches, continuing until either the
|
|
1295
|
+
* threshold of *valid* rows is reached or the per-call scan cap is hit, so
|
|
1296
|
+
* a cluster of malformed rows at the head of the scan cannot starve the
|
|
1297
|
+
* count and misclassify a warm conversation as cold.
|
|
1252
1298
|
*/
|
|
1253
1299
|
function countSlackMetaMessages(conversationId: string): number {
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1300
|
+
let count = 0;
|
|
1301
|
+
let offset = 0;
|
|
1302
|
+
while (offset < SLACK_DM_CANDIDATE_MAX_SCAN) {
|
|
1303
|
+
const remaining = SLACK_DM_CANDIDATE_MAX_SCAN - offset;
|
|
1304
|
+
const batchLimit = Math.min(SLACK_DM_CANDIDATE_BATCH_SIZE, remaining);
|
|
1305
|
+
const candidates = selectSlackMetaCandidateMetadata(
|
|
1306
|
+
conversationId,
|
|
1307
|
+
batchLimit,
|
|
1308
|
+
offset,
|
|
1309
|
+
);
|
|
1310
|
+
if (candidates.length === 0) return count;
|
|
1311
|
+
for (const raw of candidates) {
|
|
1312
|
+
let parent: Record<string, unknown> | null = null;
|
|
1313
|
+
try {
|
|
1314
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
1315
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1316
|
+
parent = parsed as Record<string, unknown>;
|
|
1317
|
+
}
|
|
1318
|
+
} catch {
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
if (!parent) continue;
|
|
1322
|
+
const inner = parent.slackMeta;
|
|
1323
|
+
if (typeof inner !== "string") continue;
|
|
1324
|
+
if (readSlackMetadata(inner)) {
|
|
1325
|
+
count++;
|
|
1326
|
+
if (count >= SLACK_DM_BACKFILL_WARM_THRESHOLD) return count;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
if (candidates.length < batchLimit) return count;
|
|
1330
|
+
offset += candidates.length;
|
|
1331
|
+
}
|
|
1332
|
+
return count;
|
|
1258
1333
|
}
|
|
1259
1334
|
|
|
1260
1335
|
/**
|
|
@@ -56,6 +56,7 @@ mock.module("../../memory/qdrant-client.js", () => ({
|
|
|
56
56
|
searchWithFilter: async () => [...mockHybridSearchResults],
|
|
57
57
|
}),
|
|
58
58
|
initQdrantClient: () => {},
|
|
59
|
+
resolveQdrantUrl: () => "http://127.0.0.1:6333",
|
|
59
60
|
}));
|
|
60
61
|
|
|
61
62
|
mock.module("../../memory/qdrant-circuit-breaker.js", () => ({
|
|
@@ -255,9 +255,6 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
const result = await streamExportVBundle({
|
|
258
|
-
// hooksDir is intentionally omitted — hooks now live under workspace/hooks/
|
|
259
|
-
// and are included in the workspace walk. Passing hooksDir separately would
|
|
260
|
-
// export them twice (once as workspace/hooks/... and again as hooks/...).
|
|
261
258
|
workspaceDir: getWorkspaceDir(),
|
|
262
259
|
source: "runtime-export",
|
|
263
260
|
description,
|