@vellumai/assistant 0.12.1-dev.202609141913.5e16607 → 0.12.1-staging.2
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 +0 -4
- package/ARCHITECTURE.md +12 -11
- package/docs/architecture/turn-actor.md +8 -15
- package/docs/guardian-request-flow.md +0 -16
- package/openapi.yaml +49 -291
- package/package.json +1 -1
- package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
- package/src/__tests__/app-compiler.test.ts +0 -6
- package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
- package/src/__tests__/app-dir-path-guard.test.ts +3 -1
- package/src/__tests__/app-source-watcher.test.ts +218 -0
- package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
- package/src/__tests__/computer-use-tools.test.ts +5 -17
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +29 -28
- package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
- package/src/__tests__/config-watcher.test.ts +8 -23
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +0 -26
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
- package/src/__tests__/conversation-queue.test.ts +207 -48
- package/src/__tests__/conversation-retry-discard.test.ts +1 -54
- package/src/__tests__/conversation-skill-tools.test.ts +0 -27
- package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
- package/src/__tests__/conversation-title-service.test.ts +10 -14
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
- package/src/__tests__/credential-record-retire.test.ts +1 -171
- package/src/__tests__/credential-routes.test.ts +3 -212
- package/src/__tests__/credential-security-invariants.test.ts +2 -4
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/document-sync-tags.test.ts +0 -25
- package/src/__tests__/drain-kick-guard.test.ts +2 -0
- package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/host-bash-proxy.test.ts +45 -26
- package/src/__tests__/host-browser-proxy.test.ts +17 -6
- package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
- package/src/__tests__/host-file-proxy.test.ts +23 -11
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
- package/src/__tests__/llm-catalog-parity.test.ts +0 -25
- package/src/__tests__/mcp-auth-routes.test.ts +9 -6
- package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
- package/src/__tests__/mcp-health-check.test.ts +11 -8
- package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
- package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
- package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
- package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
- package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +548 -355
- package/src/__tests__/model-intents.test.ts +0 -3
- package/src/__tests__/notification-vellum-adapter.test.ts +2 -85
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +16 -8
- package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
- package/src/__tests__/script-proxy-certs.test.ts +0 -35
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/speaker-identification.test.ts +56 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
- package/src/__tests__/subagent-tools.test.ts +2 -3
- package/src/__tests__/telephony-tts-guard.test.ts +3 -38
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
- package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
- package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
- package/src/__tests__/workspace-git-service.test.ts +0 -26
- package/src/acp/__tests__/agent-process.test.ts +4 -258
- package/src/acp/__tests__/auth-required.test.ts +0 -96
- package/src/acp/__tests__/client-handler.test.ts +2 -90
- package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
- package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
- package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
- package/src/acp/__tests__/session-manager.test.ts +4 -895
- package/src/acp/agent-process.test.ts +1 -4
- package/src/acp/agent-process.ts +9 -94
- package/src/acp/auth-required.ts +1 -50
- package/src/acp/auto-install.test.ts +7 -9
- package/src/acp/client-handler.ts +3 -16
- package/src/acp/resolve-agent.test.ts +6 -220
- package/src/acp/resolve-agent.ts +23 -88
- package/src/acp/session-manager.test.ts +3 -196
- package/src/acp/session-manager.ts +18 -441
- package/src/acp/types.ts +5 -50
- package/src/api/events/app-files-changed.ts +3 -4
- package/src/api/events/notification-intent.ts +0 -2
- package/src/api/index.ts +0 -6
- package/src/apps/app-store.ts +1 -1
- package/src/bundler/app-compiler.ts +0 -15
- package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
- package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
- package/src/calls/call-constants.ts +4 -24
- package/src/calls/call-controller.ts +199 -499
- package/src/calls/call-transport.ts +0 -16
- package/src/calls/media-stream-output.ts +0 -32
- package/src/calls/media-stream-server.ts +30 -82
- package/src/calls/media-stream-stt-session.ts +0 -15
- package/src/calls/resolve-call-tts-provider.ts +2 -33
- package/src/calls/speaker-identification.ts +233 -0
- package/src/calls/tts-call-strategy.ts +5 -9
- package/src/calls/types.ts +0 -2
- package/src/calls/voice-control-protocol.ts +21 -53
- package/src/calls/voice-session-bridge.ts +46 -59
- package/src/calls/voice-triage-escalate.ts +56 -175
- package/src/cli/commands/apps.help.ts +13 -67
- package/src/cli/commands/apps.ts +0 -183
- package/src/cli/commands/bash.help.ts +13 -8
- package/src/cli/commands/bash.ts +25 -7
- package/src/cli/commands/channels/index.help.ts +2 -70
- package/src/cli/commands/channels/index.ts +3 -16
- package/src/cli/commands/credentials.help.ts +2 -6
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/roadmap.help.ts +1 -5
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +90 -103
- package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
- package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
- package/src/config/__tests__/platform-rehydration.test.ts +9 -99
- package/src/config/acp-defaults.test.ts +3 -13
- package/src/config/acp-defaults.ts +6 -23
- package/src/config/acp-schema.ts +2 -30
- package/src/config/bundled-skills/acp/SKILL.md +14 -22
- package/src/config/bundled-skills/acp/TOOLS.json +1 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +4 -3
- package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +12 -24
- package/src/config/loader.ts +0 -1
- package/src/config/memory-v3-gate.test.ts +0 -31
- package/src/config/memory-v3-gate.ts +16 -20
- package/src/config/platform-rehydration.ts +27 -11
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
- package/src/config/schemas/calls.ts +28 -1
- package/src/config/schemas/live-voice.ts +173 -1
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +0 -10
- package/src/config/schemas/services.ts +0 -8
- package/src/config/schemas/skills.ts +14 -0
- package/src/credential-execution/process-manager.test.ts +19 -18
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
- package/src/daemon/app-source-watcher.ts +218 -0
- package/src/daemon/config-watcher.ts +194 -13
- package/src/daemon/conversation-process.ts +64 -35
- package/src/daemon/conversation-skill-tools.ts +31 -13
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +8 -10
- package/src/daemon/conversation-tool-setup.ts +43 -37
- package/src/daemon/conversation.ts +3 -0
- package/src/daemon/host-bash-proxy.ts +12 -8
- package/src/daemon/host-browser-proxy.ts +1 -15
- package/src/daemon/host-file-proxy.ts +10 -8
- package/src/daemon/host-transfer-proxy.ts +18 -14
- package/src/daemon/lifecycle.ts +5 -0
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +9 -1
- package/src/daemon/skill-memory-refresh.ts +0 -64
- package/src/daemon/tool-side-effects.ts +18 -7
- package/src/home/__tests__/feed-writer.test.ts +0 -22
- package/src/home/feed-writer.ts +2 -11
- package/src/hooks/types.ts +0 -33
- package/src/inbound/platform-callback-registration.ts +5 -4
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
- package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
- package/src/live-voice/live-voice-session.ts +693 -213
- package/src/{calls → live-voice}/progress-narration.ts +2 -2
- package/src/live-voice/protocol.ts +2 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +45 -29
- package/src/mcp/startup.ts +4 -8
- package/src/messaging/provider.ts +3 -10
- package/src/messaging/providers/callback-routing.ts +0 -18
- package/src/messaging/providers/channel-transport.ts +0 -56
- package/src/messaging/providers/discord/transport.ts +0 -17
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
- package/src/messaging/providers/slack/adapter.ts +44 -7
- package/src/messaging/providers/slack/transport.ts +0 -16
- package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
- package/src/messaging/providers/telegram-bot/client.ts +26 -0
- package/src/messaging/providers/telegram-bot/transport.ts +0 -20
- package/src/messaging/providers/whatsapp/adapter.ts +48 -1
- package/src/messaging/providers/whatsapp/client.ts +29 -0
- package/src/messaging/providers/whatsapp/transport.ts +0 -18
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +2 -158
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/__tests__/schedule-result-producer.test.ts +3 -99
- package/src/notifications/adapters/macos.ts +0 -3
- package/src/notifications/assistant-reply-producer.ts +3 -8
- package/src/notifications/conversation-pairing.ts +16 -36
- package/src/notifications/delivered-post-record.ts +1 -11
- package/src/notifications/guardian-feed-projection.ts +5 -125
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/notifications/schedule-result-producer.ts +22 -66
- package/src/oauth/__tests__/seed-providers-managed.test.ts +0 -54
- package/src/oauth/byo-connection.test.ts +0 -45
- package/src/oauth/byo-connection.ts +6 -36
- package/src/oauth/connection-resolver.test.ts +0 -73
- package/src/oauth/connection-resolver.ts +2 -95
- package/src/oauth/seed-providers.ts +0 -165
- package/src/outbound-proxy/certs.ts +5 -3
- package/src/permissions/checker.test.ts +6 -11
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +63 -71
- package/src/persistence/conversation-lineage.ts +4 -12
- package/src/persistence/conversation-title-service.ts +26 -9
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-reads.ts +0 -30
- package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
- package/src/persistence/schema/memory-core.ts +0 -5
- package/src/platform/client.test.ts +2 -4
- package/src/platform/client.ts +10 -2
- package/src/plugin-api/constants.ts +0 -2
- package/src/plugin-api/index.ts +0 -1
- package/src/plugin-api/types.ts +0 -2
- package/src/plugins/defaults/index.ts +0 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
- package/src/plugins/defaults/memory/hooks/init.ts +0 -10
- package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
- package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
- package/src/plugins/defaults/memory/startup.ts +1 -6
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
- package/src/plugins/defaults/memory/worker.ts +0 -6
- package/src/plugins/mcp-servers.ts +75 -15
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +23 -9
- package/src/providers/inference/auth.ts +3 -2
- package/src/providers/inference/connection-availability.ts +8 -27
- package/src/providers/inference/resolve-auth.ts +4 -22
- package/src/providers/model-catalog.ts +0 -34
- package/src/providers/model-intents.ts +5 -10
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
- package/src/providers/openai/chat-completions-provider.ts +78 -192
- package/src/providers/openai/codex-models.ts +4 -0
- package/src/runtime/AGENTS.md +0 -2
- package/src/runtime/auth/same-actor.ts +3 -38
- package/src/runtime/http-server.ts +151 -160
- package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
- package/src/runtime/routes/acp-routes.test.ts +0 -15
- package/src/runtime/routes/acp-routes.ts +9 -72
- package/src/runtime/routes/app-management-routes.ts +0 -73
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +0 -40
- package/src/runtime/routes/credential-routes.ts +10 -19
- package/src/runtime/routes/debug-bash-routes.ts +173 -0
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +2 -4
- package/src/runtime/routes/log-export-routes.ts +0 -2
- package/src/runtime/routes/mcp-auth-routes.ts +47 -39
- package/src/runtime/routes/migration-routes.ts +20 -6
- package/src/runtime/routes/platform-routes.ts +15 -18
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
- package/src/runtime/services/conversation-serializer.ts +3 -16
- package/src/runtime/sync/resource-sync-events.ts +1 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/ces-rpc-record-backend.ts +0 -7
- package/src/subagent/consult-context.ts +25 -12
- package/src/tools/acp/list-agents.test.ts +1 -3
- package/src/tools/acp/spawn.test.ts +11 -146
- package/src/tools/acp/spawn.ts +3 -21
- package/src/tools/acp/status.ts +3 -40
- package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
- package/src/tools/capability-offer.test.ts +2 -22
- package/src/tools/capability-offer.ts +1 -16
- package/src/tools/computer-use/definitions.ts +2 -4
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +0 -100
- package/src/tools/host-filesystem/edit.test.ts +1 -2
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +1 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +1 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +1 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +0 -16
- package/src/tools/host-terminal/host-shell.ts +20 -4
- package/src/tools/policy-context.ts +7 -6
- package/src/tools/subagent/spawn.ts +8 -5
- package/src/tools/watch/watch-retro-report.ts +8 -0
- package/src/util/__tests__/language-subtag.test.ts +1 -23
- package/src/util/fs-watcher-error.ts +36 -0
- package/src/util/language-subtag.ts +0 -16
- package/src/util/platform.ts +0 -8
- package/src/workspace/git-service.ts +54 -5
- package/src/workspace/migrations/registry.ts +0 -6
- package/scripts/smoke-desktop-window-theme.ts +0 -186
- package/src/__tests__/git-porcelain.test.ts +0 -47
- package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
- package/src/__tests__/resolve-app.test.ts +0 -135
- package/src/__tests__/service-account-token.test.ts +0 -247
- package/src/__tests__/source-fingerprint.test.ts +0 -184
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
- package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
- package/src/acp/model-config.test.ts +0 -166
- package/src/acp/model-config.ts +0 -138
- package/src/api/events/acp-session-model-update.test.ts +0 -88
- package/src/api/events/acp-session-model-update.ts +0 -46
- package/src/apps/resolve-app.ts +0 -81
- package/src/apps/source-fingerprint.ts +0 -266
- package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
- package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
- package/src/calls/barge-in-guard.ts +0 -118
- package/src/calls/voice-leg-coordinator.ts +0 -189
- package/src/calls/voice-progress-cadence.ts +0 -423
- package/src/cli/commands/__tests__/apps.test.ts +0 -245
- package/src/cli/commands/__tests__/bash.test.ts +0 -53
- package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
- package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
- package/src/cli/commands/channels/send.ts +0 -128
- package/src/config/__tests__/platform-identity.test.ts +0 -255
- package/src/config/acp-agent-write.ts +0 -43
- package/src/config/acp-schema.test.ts +0 -81
- package/src/config/platform-identity.ts +0 -201
- package/src/config/schemas/__tests__/voice.test.ts +0 -148
- package/src/config/schemas/voice.ts +0 -150
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
- package/src/daemon/app-change-notify.ts +0 -45
- package/src/desktop/__tests__/fake-desktop.ts +0 -119
- package/src/desktop/desktop-chrome-policy.test.ts +0 -71
- package/src/desktop/desktop-chrome-policy.ts +0 -42
- package/src/desktop/desktop-dependencies.test.ts +0 -102
- package/src/desktop/desktop-dependencies.ts +0 -306
- package/src/desktop/desktop-feature.test.ts +0 -18
- package/src/desktop/desktop-feature.ts +0 -19
- package/src/desktop/desktop-panel-config.test.ts +0 -135
- package/src/desktop/desktop-panel-config.ts +0 -149
- package/src/desktop/desktop-session-manager.test.ts +0 -627
- package/src/desktop/desktop-session-manager.ts +0 -814
- package/src/desktop/desktop-stream-bridge.test.ts +0 -395
- package/src/desktop/desktop-stream-bridge.ts +0 -214
- package/src/desktop/desktop-wallpaper.test.ts +0 -191
- package/src/desktop/desktop-wallpaper.ts +0 -113
- package/src/desktop/desktop-window-theme.ts +0 -170
- package/src/i18n/AGENTS.md +0 -22
- package/src/i18n/__tests__/i18n.test.ts +0 -108
- package/src/i18n/index.ts +0 -16
- package/src/i18n/locales.ts +0 -93
- package/src/i18n/messages.ts +0 -61
- package/src/i18n/title.ts +0 -25
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
- package/src/mcp/spec-schema.ts +0 -111
- package/src/mcp/workspace-mcp-config.ts +0 -188
- package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
- package/src/persistence/conversation-title-placeholders.ts +0 -23
- package/src/persistence/message-cursor.ts +0 -78
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
- package/src/providers/gemini-thought-signature.test.ts +0 -63
- package/src/providers/gemini-thought-signature.ts +0 -44
- package/src/providers/inference/service-account-token.ts +0 -248
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
- package/src/providers/openai/google-thought-signature.ts +0 -320
- package/src/runtime/__tests__/channel-send.test.ts +0 -443
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
- package/src/runtime/channel-send.ts +0 -316
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
- package/src/runtime/routes/channel-send-routes.ts +0 -85
- package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
- package/src/runtime/routes/desktop-setup-routes.ts +0 -35
- package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
- package/src/security/ces-http-record-backend.ts +0 -120
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
- package/src/skills/workspace-skill-md-signature.ts +0 -50
- package/src/tools/acp/status.test.ts +0 -94
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -57
- package/src/tools/terminal/sanitized-bash.ts +0 -105
- package/src/util/ca-bundle.ts +0 -13
- package/src/workspace/git-porcelain.ts +0 -66
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
- /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
|
@@ -55,13 +55,6 @@ const SCHEDULE_ID = "sched-1";
|
|
|
55
55
|
const RUN_ID = "run-1";
|
|
56
56
|
const RUN_STARTED_AT = 1700000000000;
|
|
57
57
|
|
|
58
|
-
/**
|
|
59
|
-
* The synthetic user rows carrying each tool call's result. A delivery counts
|
|
60
|
-
* only when its call reported success, so a case that wants suppression has to
|
|
61
|
-
* supply the matching successful result.
|
|
62
|
-
*/
|
|
63
|
-
let toolResultRows: MessageRow[] = [];
|
|
64
|
-
|
|
65
58
|
const realCrud = await import("../../persistence/conversation-crud.js");
|
|
66
59
|
mock.module("../../persistence/conversation-crud.js", () => ({
|
|
67
60
|
...realCrud,
|
|
@@ -73,7 +66,6 @@ mock.module("../../persistence/conversation-crud.js", () => ({
|
|
|
73
66
|
messageId === ASSISTANT_MESSAGE_ID
|
|
74
67
|
? [...turnRows.map((row) => row.id), ASSISTANT_MESSAGE_ID]
|
|
75
68
|
: [messageId],
|
|
76
|
-
getMessagesAfter: () => toolResultRows,
|
|
77
69
|
}));
|
|
78
70
|
|
|
79
71
|
const realAttentionStore =
|
|
@@ -122,25 +114,6 @@ function makeToolCallRow(
|
|
|
122
114
|
);
|
|
123
115
|
}
|
|
124
116
|
|
|
125
|
-
/** The executor's verdict on the call `makeToolCallRow(name, ...)` made. */
|
|
126
|
-
function makeToolResultRow(name: string, isError = false): MessageRow {
|
|
127
|
-
return makeAssistantRow(
|
|
128
|
-
[
|
|
129
|
-
{
|
|
130
|
-
type: "tool_result",
|
|
131
|
-
tool_use_id: `tu-${name}`,
|
|
132
|
-
content: isError ? "failed" : "ok",
|
|
133
|
-
...(isError ? { is_error: true } : {}),
|
|
134
|
-
},
|
|
135
|
-
] as ContentBlock[],
|
|
136
|
-
{
|
|
137
|
-
id: `res-${name}`,
|
|
138
|
-
role: "user",
|
|
139
|
-
createdAt: RUN_STARTED_AT + 150,
|
|
140
|
-
},
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
117
|
function makeAttentionState(
|
|
145
118
|
overrides: Partial<AttentionState> = {},
|
|
146
119
|
): AttentionState {
|
|
@@ -216,7 +189,6 @@ beforeEach(() => {
|
|
|
216
189
|
notifiedProbeArgs.length = 0;
|
|
217
190
|
alreadyNotified = false;
|
|
218
191
|
turnRows = [];
|
|
219
|
-
toolResultRows = [];
|
|
220
192
|
attentionState = makeAttentionState();
|
|
221
193
|
assistantRow = makeAssistantRow([
|
|
222
194
|
{ type: "text", text: "**3 new emails** and one calendar change." },
|
|
@@ -273,8 +245,7 @@ describe("emitScheduleResultNotification", () => {
|
|
|
273
245
|
|
|
274
246
|
test("stays silent when the run delivered by email through messaging_send", async () => {
|
|
275
247
|
// The schedule skill's prescribed route for rich content. It writes no
|
|
276
|
-
// notification event, so the
|
|
277
|
-
// has the result.
|
|
248
|
+
// notification event, so only the tool call shows the user has the result.
|
|
278
249
|
turnRows = [
|
|
279
250
|
makeToolCallRow("messaging_send", {
|
|
280
251
|
platform: "gmail",
|
|
@@ -282,7 +253,6 @@ describe("emitScheduleResultNotification", () => {
|
|
|
282
253
|
message: "Inbox digest…",
|
|
283
254
|
}),
|
|
284
255
|
];
|
|
285
|
-
toolResultRows = [makeToolResultRow("messaging_send")];
|
|
286
256
|
assistantRow = makeAssistantRow([
|
|
287
257
|
{ type: "text", text: "Sent the digest to your inbox." },
|
|
288
258
|
] as ContentBlock[]);
|
|
@@ -292,86 +262,20 @@ describe("emitScheduleResultNotification", () => {
|
|
|
292
262
|
expect(emitCalls).toHaveLength(0);
|
|
293
263
|
});
|
|
294
264
|
|
|
295
|
-
test("stays silent when the run posted to
|
|
296
|
-
turnRows = [
|
|
297
|
-
makeToolCallRow("messaging_send", {
|
|
298
|
-
platform: "slack",
|
|
299
|
-
conversation_id: "C0123456789",
|
|
300
|
-
text: "the digest",
|
|
301
|
-
}),
|
|
302
|
-
];
|
|
303
|
-
toolResultRows = [makeToolResultRow("messaging_send")];
|
|
304
|
-
assistantRow = makeAssistantRow([
|
|
305
|
-
{ type: "text", text: "Posted the digest to #general." },
|
|
306
|
-
] as ContentBlock[]);
|
|
307
|
-
|
|
308
|
-
await run();
|
|
309
|
-
|
|
310
|
-
expect(emitCalls).toHaveLength(0);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
test("notifies when the channel send failed, so the run is not silent", async () => {
|
|
314
|
-
// The failure this guard exists for: the post never landed, the run said
|
|
315
|
-
// so in a conversation nobody has open, and suppressing on the attempt
|
|
316
|
-
// would cost the user the digest and the explanation both.
|
|
317
|
-
turnRows = [
|
|
318
|
-
makeToolCallRow("messaging_send", {
|
|
319
|
-
platform: "slack",
|
|
320
|
-
conversation_id: "C0123456789",
|
|
321
|
-
text: "the digest",
|
|
322
|
-
}),
|
|
323
|
-
];
|
|
324
|
-
toolResultRows = [makeToolResultRow("messaging_send", true)];
|
|
325
|
-
assistantRow = makeAssistantRow([
|
|
326
|
-
{ type: "text", text: "I could not post the digest to #general." },
|
|
327
|
-
] as ContentBlock[]);
|
|
328
|
-
|
|
329
|
-
await run();
|
|
330
|
-
|
|
331
|
-
expect(emitCalls).toHaveLength(1);
|
|
332
|
-
expect(emitCalls[0].contextPayload.requestedMessage).toBe(
|
|
333
|
-
"I could not post the digest to #general.",
|
|
334
|
-
);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
test("notifies when a delivery call left no result at all", async () => {
|
|
338
|
-
// An interrupted turn persists the call without its verdict. Unproven is
|
|
339
|
-
// not delivered.
|
|
340
|
-
turnRows = [
|
|
341
|
-
makeToolCallRow("messaging_send", {
|
|
342
|
-
platform: "slack",
|
|
343
|
-
conversation_id: "C0123456789",
|
|
344
|
-
text: "the digest",
|
|
345
|
-
}),
|
|
346
|
-
];
|
|
347
|
-
toolResultRows = [];
|
|
348
|
-
assistantRow = makeAssistantRow([
|
|
349
|
-
{ type: "text", text: "Here is the digest." },
|
|
350
|
-
] as ContentBlock[]);
|
|
351
|
-
|
|
352
|
-
await run();
|
|
353
|
-
|
|
354
|
-
expect(emitCalls).toHaveLength(1);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test("notifies after a Web API post, whose success cannot prove Slack accepted it", async () => {
|
|
358
|
-
// Slack refuses a call with HTTP 200 and `ok: false`, so a successful bash
|
|
359
|
-
// result is no evidence the post landed. This is the best case the route
|
|
360
|
-
// can produce, and it still gets the fallback.
|
|
265
|
+
test("stays silent when the run posted to Slack through chat.postMessage", async () => {
|
|
361
266
|
turnRows = [
|
|
362
267
|
makeToolCallRow("bash", {
|
|
363
268
|
command:
|
|
364
269
|
'assistant oauth request --provider slack_channel /chat.postMessage --json \'{"channel":"C1","text":"digest"}\'',
|
|
365
270
|
}),
|
|
366
271
|
];
|
|
367
|
-
toolResultRows = [makeToolResultRow("bash")];
|
|
368
272
|
assistantRow = makeAssistantRow([
|
|
369
273
|
{ type: "text", text: "Posted the digest to #general." },
|
|
370
274
|
] as ContentBlock[]);
|
|
371
275
|
|
|
372
276
|
await run();
|
|
373
277
|
|
|
374
|
-
expect(emitCalls).toHaveLength(
|
|
278
|
+
expect(emitCalls).toHaveLength(0);
|
|
375
279
|
});
|
|
376
280
|
|
|
377
281
|
test("still notifies when the run's tool calls were not deliveries", async () => {
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
|
|
21
21
|
import type { AssistantEvent } from "../../api/index.js";
|
|
22
22
|
import type { InterfaceId } from "../../channels/types.js";
|
|
23
|
-
import { getAssistantName } from "../../daemon/identity-helpers.js";
|
|
24
23
|
import { updateMessageContent } from "../../persistence/conversation-crud.js";
|
|
25
24
|
import { publishConversationMessagesChanged } from "../../runtime/sync/resource-sync-events.js";
|
|
26
25
|
import { getLogger } from "../../util/logger.js";
|
|
@@ -102,14 +101,12 @@ export class VellumAdapter implements ChannelAdapter {
|
|
|
102
101
|
|
|
103
102
|
const silent =
|
|
104
103
|
payload.urgency !== "high" && payload.urgency !== "critical";
|
|
105
|
-
const assistantName = getAssistantName()?.trim() || undefined;
|
|
106
104
|
|
|
107
105
|
this.broadcast({
|
|
108
106
|
type: "notification_intent",
|
|
109
107
|
deliveryId: payload.deliveryId,
|
|
110
108
|
correlationId: payload.correlationId,
|
|
111
109
|
sourceEventName: payload.sourceEventName,
|
|
112
|
-
...(assistantName ? { assistantName } : {}),
|
|
113
110
|
title: payload.copy.title,
|
|
114
111
|
body: payload.copy.body,
|
|
115
112
|
deepLinkMetadata: payload.deepLinkTarget,
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
getMessageById,
|
|
23
23
|
parseMessageMetadata,
|
|
24
24
|
} from "../persistence/conversation-crud.js";
|
|
25
|
-
import { isReplaceableTitle } from "../persistence/conversation-title-placeholders.js";
|
|
26
25
|
import {
|
|
27
26
|
isDesktopOriginatedUserMessage,
|
|
28
27
|
isReplyPushIneligibleUserMessage,
|
|
@@ -248,13 +247,9 @@ export async function emitAssistantReplyNotification(params: {
|
|
|
248
247
|
// lock screen. Absent `requestedTitle` lets the decision branch derive a
|
|
249
248
|
// title from the body, which reads better than an empty or placeholder
|
|
250
249
|
// conversation title.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const storedTitle = conversation.title?.trim() ?? "";
|
|
255
|
-
const requestedTitle = isReplaceableTitle(storedTitle)
|
|
256
|
-
? ""
|
|
257
|
-
: sanitizeNotificationTitle(flattenTitleWhitespace(storedTitle));
|
|
250
|
+
const requestedTitle = sanitizeNotificationTitle(
|
|
251
|
+
flattenTitleWhitespace(conversation.title ?? ""),
|
|
252
|
+
);
|
|
258
253
|
|
|
259
254
|
// Read as close to the emit as possible: nothing short-circuits on it.
|
|
260
255
|
// Presence only speaks for a turn the desktop itself opened, on that row's
|
|
@@ -130,7 +130,6 @@ export interface PairingOptions {
|
|
|
130
130
|
* transactional request or a system alert.
|
|
131
131
|
*/
|
|
132
132
|
const ASSISTANT_SHARE_EVENT = "assistant.share";
|
|
133
|
-
const ASSISTANT_REPLY_EVENT = "chat.assistant_reply";
|
|
134
133
|
|
|
135
134
|
/**
|
|
136
135
|
* Promote a background share into an assistant-initiated thread, under the
|
|
@@ -264,25 +263,7 @@ export async function pairDeliveryWithConversation(
|
|
|
264
263
|
// notification can appear. The home feed aims its "Go to Conversation"
|
|
265
264
|
// button at the same row whenever it mirrors the signal.
|
|
266
265
|
//
|
|
267
|
-
//
|
|
268
|
-
// Its notification body is a lock-screen preview, so appending it would
|
|
269
|
-
// create a second, truncated assistant row. Keep the conversation target
|
|
270
|
-
// for deep links without writing the preview into the transcript.
|
|
271
|
-
if (
|
|
272
|
-
strategy === "start_new_conversation" &&
|
|
273
|
-
!signal.requiresConversation &&
|
|
274
|
-
signal.sourceEventName === ASSISTANT_REPLY_EVENT
|
|
275
|
-
) {
|
|
276
|
-
return {
|
|
277
|
-
conversationId: resolveSourceConversationId(signal),
|
|
278
|
-
messageId: null,
|
|
279
|
-
strategy,
|
|
280
|
-
createdNewConversation: false,
|
|
281
|
-
conversationFallbackUsed: false,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// This is deliberately not gated on home-feed eligibility: a signal the
|
|
266
|
+
// So this is deliberately not gated on home-feed eligibility: a signal the
|
|
286
267
|
// feed declines to mirror still reaches the user through the banner, and
|
|
287
268
|
// this row is what makes that landing honest.
|
|
288
269
|
if (strategy === "start_new_conversation" && !signal.requiresConversation) {
|
|
@@ -767,45 +748,44 @@ async function resolveChannelDeliveryHome(params: {
|
|
|
767
748
|
* Indexing is skipped for parity with the other notification write paths:
|
|
768
749
|
* notification copy is delivery audit, not conversational memory.
|
|
769
750
|
*/
|
|
770
|
-
function
|
|
771
|
-
|
|
751
|
+
async function appendBodyToSourceConversation(
|
|
752
|
+
signal: NotificationSignal,
|
|
753
|
+
channel: NotificationChannel,
|
|
754
|
+
messageContent: string,
|
|
755
|
+
): Promise<{ conversationId: string; messageId: string } | null> {
|
|
756
|
+
const sourceContextId = signal.sourceContextId;
|
|
757
|
+
if (!sourceContextId) {
|
|
772
758
|
return null;
|
|
773
759
|
}
|
|
760
|
+
|
|
761
|
+
let existing: ReturnType<typeof getConversation>;
|
|
774
762
|
try {
|
|
775
|
-
|
|
763
|
+
existing = getConversation(sourceContextId);
|
|
776
764
|
} catch {
|
|
777
765
|
return null;
|
|
778
766
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
async function appendBodyToSourceConversation(
|
|
782
|
-
signal: NotificationSignal,
|
|
783
|
-
channel: NotificationChannel,
|
|
784
|
-
messageContent: string,
|
|
785
|
-
): Promise<{ conversationId: string; messageId: string } | null> {
|
|
786
|
-
const conversationId = resolveSourceConversationId(signal);
|
|
787
|
-
if (!conversationId) {
|
|
767
|
+
if (!existing) {
|
|
788
768
|
return null;
|
|
789
769
|
}
|
|
790
770
|
|
|
791
|
-
const message = await addMessage(
|
|
771
|
+
const message = await addMessage(existing.id, "assistant", messageContent, {
|
|
792
772
|
skipIndexing: true,
|
|
793
773
|
});
|
|
794
774
|
// `addMessage` projects attention metadata alone, so a client with this
|
|
795
775
|
// conversation open needs the messages tag to refetch the transcript. A
|
|
796
776
|
// notification the user taps through to has every chance of landing on an
|
|
797
777
|
// already-open conversation.
|
|
798
|
-
publishConversationMessagesChanged(
|
|
778
|
+
publishConversationMessagesChanged(existing.id);
|
|
799
779
|
|
|
800
780
|
log.info(
|
|
801
781
|
{
|
|
802
782
|
signalId: signal.signalId,
|
|
803
783
|
channel,
|
|
804
|
-
conversationId,
|
|
784
|
+
conversationId: existing.id,
|
|
805
785
|
messageId: message.id,
|
|
806
786
|
},
|
|
807
787
|
"Appended notification body to producing conversation",
|
|
808
788
|
);
|
|
809
789
|
|
|
810
|
-
return { conversationId, messageId: message.id };
|
|
790
|
+
return { conversationId: existing.id, messageId: message.id };
|
|
811
791
|
}
|
|
@@ -39,12 +39,6 @@ export interface DeliveredChannelPost {
|
|
|
39
39
|
text: string;
|
|
40
40
|
/** The message id the channel assigned when it acknowledged the post. */
|
|
41
41
|
providerMessageId: string;
|
|
42
|
-
/**
|
|
43
|
-
* The ids of the further posts the same text became when the channel
|
|
44
|
-
* split it, in send order. Each is reconciled onto the row like the first,
|
|
45
|
-
* so a reaction, edit, or delete naming any chunk resolves to this row.
|
|
46
|
-
*/
|
|
47
|
-
additionalProviderMessageIds?: readonly string[];
|
|
48
42
|
/**
|
|
49
43
|
* The conversation whose turn made the post, when a different one from
|
|
50
44
|
* the home it is recorded in (the messaging tool sending from a scheduled
|
|
@@ -81,11 +75,7 @@ export async function recordDeliveredChannelPost(
|
|
|
81
75
|
: {}),
|
|
82
76
|
},
|
|
83
77
|
});
|
|
84
|
-
|
|
85
|
-
await reconcile(post.providerMessageId);
|
|
86
|
-
for (const id of post.additionalProviderMessageIds ?? []) {
|
|
87
|
-
await reconcile(id);
|
|
88
|
-
}
|
|
78
|
+
await makeSentMessageIdReconciler(row.id)(post.providerMessageId);
|
|
89
79
|
// A resident conversation reloads its history on the next turn so the
|
|
90
80
|
// post is in context; the client refetches the transcript.
|
|
91
81
|
findConversation(post.conversationId)?.markHistoryStale();
|
|
@@ -31,10 +31,6 @@ import {
|
|
|
31
31
|
patchFeedItemContent,
|
|
32
32
|
readHomeFeed,
|
|
33
33
|
} from "../home/feed-writer.js";
|
|
34
|
-
import {
|
|
35
|
-
getMemoryCheckpoint,
|
|
36
|
-
setMemoryCheckpoint,
|
|
37
|
-
} from "../persistence/checkpoints.js";
|
|
38
34
|
import { getLogger } from "../util/logger.js";
|
|
39
35
|
import {
|
|
40
36
|
buildToolApprovalSourceView,
|
|
@@ -170,17 +166,11 @@ export interface GuardianFeedReceiptParams {
|
|
|
170
166
|
*
|
|
171
167
|
* The item keeps its title and summary; `guardianRequest` flips to the
|
|
172
168
|
* terminal status (which is what removes the client's action buttons),
|
|
173
|
-
* urgency drops to `medium` so the item leaves the protected
|
|
174
|
-
* attention" treatment and becomes an ordinary clearable
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* edge into terminal, so a status the user set later (including marking
|
|
179
|
-
* the receipt unread again) survives a caller that receipts twice.
|
|
180
|
-
*
|
|
181
|
-
* Returns false only when the write itself failed, so the withdrawal
|
|
182
|
-
* fan-out can hold its per-request receipt back and retry (same
|
|
183
|
-
* contract as the other surfaces it settles). A request with no
|
|
169
|
+
* and urgency drops to `medium` so the item leaves the protected
|
|
170
|
+
* "Needs attention" treatment and becomes an ordinary clearable
|
|
171
|
+
* notification. Returns false only when the write itself failed, so the
|
|
172
|
+
* withdrawal fan-out can hold its per-request receipt back and retry
|
|
173
|
+
* (same contract as the other surfaces it settles). A request with no
|
|
184
174
|
* projection item resolves true: the pending writer converges the
|
|
185
175
|
* write-vs-resolve race by re-checking canonical status after its
|
|
186
176
|
* append, so there is nothing here for a retry to fix.
|
|
@@ -207,15 +197,6 @@ export async function writeGuardianFeedReceipt(
|
|
|
207
197
|
}
|
|
208
198
|
const updated = await patchFeedItemContent(itemId, {
|
|
209
199
|
urgency: "medium",
|
|
210
|
-
// Only on the pending-to-terminal edge. A later receipt for the
|
|
211
|
-
// same request (the fan-out retries per surface, reconciliation
|
|
212
|
-
// heals drift) must not undo a user who marked the receipt unread
|
|
213
|
-
// again; from `new` specifically, because a pending item the user
|
|
214
|
-
// dismissed outright stays dismissed.
|
|
215
|
-
status: (existing) =>
|
|
216
|
-
existing.status === "new" && isPendingGuardianFeedItem(existing)
|
|
217
|
-
? "seen"
|
|
218
|
-
: existing.status,
|
|
219
200
|
guardianRequest: (existing) => ({
|
|
220
201
|
...existing,
|
|
221
202
|
status: params.status,
|
|
@@ -287,103 +268,6 @@ export async function receiptGuardianFeedItemIfRequestTerminal(
|
|
|
287
268
|
*/
|
|
288
269
|
const RECONCILE_BACKFILL_LIMIT = 50;
|
|
289
270
|
|
|
290
|
-
/** Ceiling on rows healed per round, mirroring the backfill bound. */
|
|
291
|
-
const LEGACY_UNREAD_HEAL_LIMIT = 50;
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Completion sentinel for the one-time heal below, in the daemon's
|
|
295
|
-
* checkpoint ledger (the same shape as the lexical backfill's
|
|
296
|
-
* `lexical:messages:backfill_complete`). It is the only thing that
|
|
297
|
-
* separates the two kinds of terminal receipt stored at `status: "new"`:
|
|
298
|
-
* one that never passed the pending-to-terminal edge, and one the user
|
|
299
|
-
* marked unread after it did. The rows are byte-identical (terminal
|
|
300
|
-
* projection, urgency `medium`), so the boundary is recorded rather
|
|
301
|
-
* than inferred.
|
|
302
|
-
*/
|
|
303
|
-
const LEGACY_UNREAD_HEAL_COMPLETE_KEY =
|
|
304
|
-
"guardian_feed:receipt_unread_heal_complete";
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Clear unread on terminal guardian receipts that never passed the
|
|
308
|
-
* pending-to-terminal edge in `writeGuardianFeedReceipt`.
|
|
309
|
-
*
|
|
310
|
-
* Nothing else reaches those rows: the edge runs only while a
|
|
311
|
-
* projection still reads pending, reconciliation skips items whose
|
|
312
|
-
* projection is already terminal, and guardian items carry no
|
|
313
|
-
* `expiresAt`. Without this pass they read unread indefinitely.
|
|
314
|
-
*
|
|
315
|
-
* Runs once per assistant, not once per boot. A recurring pass would
|
|
316
|
-
* take back a deliberate "mark unread" every minute, and a per-boot pass
|
|
317
|
-
* would take it back on the next restart; the sentinel is what makes a
|
|
318
|
-
* later `new` unambiguously the user's, so it is never touched.
|
|
319
|
-
*
|
|
320
|
-
* Same user-choice semantics as the edge, enforced the same way: the
|
|
321
|
-
* transition is re-evaluated inside the writer's coalescing queue, moves
|
|
322
|
-
* only `new`, and leaves `seen`, `acted_on` and `dismissed` alone.
|
|
323
|
-
* Bounded per round, and the sentinel is written only once a round drains
|
|
324
|
-
* with nothing left and nothing failed, so an interrupted pass resumes.
|
|
325
|
-
*/
|
|
326
|
-
export async function healLegacyGuardianReceiptUnread(): Promise<void> {
|
|
327
|
-
try {
|
|
328
|
-
if (getMemoryCheckpoint(LEGACY_UNREAD_HEAL_COMPLETE_KEY) === "1") {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
} catch (err) {
|
|
332
|
-
// A DB that cannot answer skips the round rather than risk re-healing
|
|
333
|
-
// a row the user has since marked unread.
|
|
334
|
-
log.warn({ err }, "Guardian receipt unread heal: checkpoint unreadable");
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const stale = readHomeFeed().items.filter(
|
|
339
|
-
(item) =>
|
|
340
|
-
item.guardianRequest !== undefined &&
|
|
341
|
-
!isPendingGuardianFeedItem(item) &&
|
|
342
|
-
item.status === "new",
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
const batch = stale.slice(0, LEGACY_UNREAD_HEAL_LIMIT);
|
|
346
|
-
let failed = 0;
|
|
347
|
-
for (const item of batch) {
|
|
348
|
-
// Re-tested at write time rather than trusted from the read above,
|
|
349
|
-
// so a row the edge transition or the user changed in between keeps
|
|
350
|
-
// whatever it was given.
|
|
351
|
-
const updated = await patchFeedItemContent(item.id, {
|
|
352
|
-
status: (existing) =>
|
|
353
|
-
existing.status === "new" && !isPendingGuardianFeedItem(existing)
|
|
354
|
-
? "seen"
|
|
355
|
-
: existing.status,
|
|
356
|
-
});
|
|
357
|
-
if (!updated) {
|
|
358
|
-
failed++;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (batch.length > 0) {
|
|
363
|
-
log.info(
|
|
364
|
-
{
|
|
365
|
-
healed: batch.length - failed,
|
|
366
|
-
failed,
|
|
367
|
-
remaining: stale.length - batch.length,
|
|
368
|
-
},
|
|
369
|
-
"Cleared unread on terminal guardian receipts that never passed the edge",
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (failed > 0 || stale.length > batch.length) {
|
|
374
|
-
// Not finished: leave the sentinel unset so the next round resumes.
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
try {
|
|
379
|
-
setMemoryCheckpoint(LEGACY_UNREAD_HEAL_COMPLETE_KEY, "1");
|
|
380
|
-
} catch (err) {
|
|
381
|
-
// The heal itself landed; only the sentinel is missing, so the next
|
|
382
|
-
// round repeats a pass that now has nothing to do.
|
|
383
|
-
log.warn({ err }, "Guardian receipt unread heal: checkpoint not persisted");
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
271
|
/**
|
|
388
272
|
* Converge the feed against canonical guardian state. Run from the
|
|
389
273
|
* guardian expiry sweep, so drift heals on the same cadence expiry does:
|
|
@@ -401,10 +285,6 @@ export async function healLegacyGuardianReceiptUnread(): Promise<void> {
|
|
|
401
285
|
* not be mistaken for "everything resolved".
|
|
402
286
|
*/
|
|
403
287
|
export async function reconcileGuardianFeedProjections(): Promise<void> {
|
|
404
|
-
// Ahead of the gateway read: the heal heads off stored rows alone and
|
|
405
|
-
// must not be held up by an unreachable gateway.
|
|
406
|
-
await healLegacyGuardianReceiptUnread();
|
|
407
|
-
|
|
408
288
|
let pending: GuardianRequestWire[];
|
|
409
289
|
try {
|
|
410
290
|
pending = await listGuardianRequests({ status: "pending" });
|
|
@@ -23,10 +23,7 @@ import {
|
|
|
23
23
|
getMessageById,
|
|
24
24
|
updateMessageContent,
|
|
25
25
|
} from "../persistence/conversation-crud.js";
|
|
26
|
-
import {
|
|
27
|
-
ASSISTANT_INITIATED_SOURCE,
|
|
28
|
-
isBackgroundConversationType,
|
|
29
|
-
} from "../persistence/conversation-types.js";
|
|
26
|
+
import { isBackgroundConversationType } from "../persistence/conversation-types.js";
|
|
30
27
|
import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
31
28
|
import { getLogger } from "../util/logger.js";
|
|
32
29
|
import { normalizeTitle, stripMarkdown } from "../util/short-title.js";
|
|
@@ -275,11 +272,6 @@ async function resolveOwnedConversationMessageId(
|
|
|
275
272
|
sourceConversationId: string | undefined,
|
|
276
273
|
summary: string,
|
|
277
274
|
): Promise<string | undefined> {
|
|
278
|
-
// The completed reply is already the source conversation's canonical row.
|
|
279
|
-
// This signal's body is a compact push preview, not conversation content.
|
|
280
|
-
if (signal.sourceEventName === "chat.assistant_reply") {
|
|
281
|
-
return undefined;
|
|
282
|
-
}
|
|
283
275
|
if (vellumDelivery?.conversationId) {
|
|
284
276
|
return vellumDelivery.conversationId === sourceConversationId
|
|
285
277
|
? vellumDelivery.messageId
|
|
@@ -503,18 +495,6 @@ function deriveDetailPanelKind(
|
|
|
503
495
|
* `isAsyncBackground` hint. `chat.assistant_reply` also mirrors: it is the
|
|
504
496
|
* durable in-app record for the push sent after a user leaves a chat, while
|
|
505
497
|
* retaining the normal interactive conversation as its navigation target.
|
|
506
|
-
*
|
|
507
|
-
* A delivery that materialized an assistant-initiated thread never mirrors.
|
|
508
|
-
* Under the `assistant-initiated-threads` flag, `conversation-pairing.ts`
|
|
509
|
-
* promotes a background `assistant.share` into a standard conversation
|
|
510
|
-
* stamped {@link ASSISTANT_INITIATED_SOURCE}, and that thread's row in the
|
|
511
|
-
* sidebar's assistant section is the share's one surface. Mirroring it here
|
|
512
|
-
* as well put the same share in the bell a second time, labeled by its
|
|
513
|
-
* source channel ("Heartbeat"). The check reads the paired conversation's
|
|
514
|
-
* `source` rather than re-deriving the promotion rule, so it holds however
|
|
515
|
-
* that rule narrows or widens. Guardian requests are checked first on
|
|
516
|
-
* purpose: the bell is their canonical home whatever conversation they pair
|
|
517
|
-
* with.
|
|
518
498
|
*/
|
|
519
499
|
function resolveHomeFeedMirror(
|
|
520
500
|
signal: NotificationSignal,
|
|
@@ -545,15 +525,6 @@ function resolveHomeFeedMirror(
|
|
|
545
525
|
: fallbackConversationId;
|
|
546
526
|
const sourceScheduleJobId = sourceRow?.scheduleJobId ?? undefined;
|
|
547
527
|
|
|
548
|
-
// Guardian requests always project into the feed: the "Needs
|
|
549
|
-
// attention" item is the request's canonical home, and the request
|
|
550
|
-
// blocks on the guardian whatever kind of conversation raised it.
|
|
551
|
-
if (isGuardianRequestSignalEvent(signal.sourceEventName)) {
|
|
552
|
-
return { mirror: true, sourceConversationId, sourceScheduleJobId };
|
|
553
|
-
}
|
|
554
|
-
if (isAssistantInitiatedThreadDelivery(fallbackConversationId)) {
|
|
555
|
-
return { mirror: false };
|
|
556
|
-
}
|
|
557
528
|
if (
|
|
558
529
|
signal.sourceChannel === "assistant_tool" ||
|
|
559
530
|
signal.sourceEventName === "chat.assistant_reply"
|
|
@@ -563,37 +534,18 @@ function resolveHomeFeedMirror(
|
|
|
563
534
|
if (signal.attentionHints.isAsyncBackground) {
|
|
564
535
|
return { mirror: true, sourceConversationId, sourceScheduleJobId };
|
|
565
536
|
}
|
|
537
|
+
// Guardian requests always project into the feed: the "Needs
|
|
538
|
+
// attention" item is the request's canonical home, and the request
|
|
539
|
+
// blocks on the guardian whatever kind of conversation raised it.
|
|
540
|
+
if (isGuardianRequestSignalEvent(signal.sourceEventName)) {
|
|
541
|
+
return { mirror: true, sourceConversationId, sourceScheduleJobId };
|
|
542
|
+
}
|
|
566
543
|
if (isBackgroundConversationType(sourceRow?.conversationType)) {
|
|
567
544
|
return { mirror: true, sourceConversationId, sourceScheduleJobId };
|
|
568
545
|
}
|
|
569
546
|
return { mirror: false };
|
|
570
547
|
}
|
|
571
548
|
|
|
572
|
-
/**
|
|
573
|
-
* Whether the vellum delivery this signal produced landed in a thread the
|
|
574
|
-
* sidebar's assistant section already shows.
|
|
575
|
-
*
|
|
576
|
-
* Best-effort like the source lookup above: a missing id, a missing row, or
|
|
577
|
-
* a lookup failure all read as "not an assistant thread", so a storage
|
|
578
|
-
* hiccup degrades to the pre-existing behavior (a bell row) rather than
|
|
579
|
-
* dropping the notification everywhere.
|
|
580
|
-
*/
|
|
581
|
-
function isAssistantInitiatedThreadDelivery(
|
|
582
|
-
deliveryConversationId: string | undefined,
|
|
583
|
-
): boolean {
|
|
584
|
-
if (!deliveryConversationId) {
|
|
585
|
-
return false;
|
|
586
|
-
}
|
|
587
|
-
try {
|
|
588
|
-
return (
|
|
589
|
-
getConversation(deliveryConversationId)?.source ===
|
|
590
|
-
ASSISTANT_INITIATED_SOURCE
|
|
591
|
-
);
|
|
592
|
-
} catch {
|
|
593
|
-
return false;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
549
|
/**
|
|
598
550
|
* Routing-intent enforcement can prune `selectedChannels` without also
|
|
599
551
|
* pruning `renderedCopy`, so iterating `renderedCopy` directly risks
|