@vellumai/assistant 0.11.2 → 0.11.3-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/ARCHITECTURE.md +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -229,6 +229,13 @@ export interface BroadcastDecisionOptions {
|
|
|
229
229
|
onConversationCreated?: OnConversationCreatedFn;
|
|
230
230
|
/** Deadline override for tests; defaults to PLATFORM_OUTCOME_DEADLINE_MS. */
|
|
231
231
|
platformOutcomeDeadlineMs?: number;
|
|
232
|
+
/**
|
|
233
|
+
* Array each channel's result is appended to as it settles, and the same
|
|
234
|
+
* array `broadcastDecision` returns. Pass one when a broadcast that throws
|
|
235
|
+
* partway still has to be told apart from one that touched no channel: the
|
|
236
|
+
* return value is lost to the throw, the sink is not.
|
|
237
|
+
*/
|
|
238
|
+
resultsSink?: NotificationDeliveryResult[];
|
|
232
239
|
}
|
|
233
240
|
|
|
234
241
|
// Cap on how long the deferred vellum send waits for the platform dispatch
|
|
@@ -348,7 +355,7 @@ export class NotificationBroadcaster {
|
|
|
348
355
|
signal.contextPayload
|
|
349
356
|
? parseAccessRequestPayload(signal.contextPayload)
|
|
350
357
|
: undefined;
|
|
351
|
-
const results: NotificationDeliveryResult[] = [];
|
|
358
|
+
const results: NotificationDeliveryResult[] = options?.resultsSink ?? [];
|
|
352
359
|
|
|
353
360
|
// Vellum pairing carried forward for the platform channel's deep link.
|
|
354
361
|
// A single-pass carry is safe because orderedChannels sorts vellum first.
|
|
@@ -794,7 +801,9 @@ export class NotificationBroadcaster {
|
|
|
794
801
|
/**
|
|
795
802
|
* Dispatch a prepared payload through its channel adapter and record the
|
|
796
803
|
* outcome (delivery row status + results entry). Returns the adapter's
|
|
797
|
-
* result, or null when the send threw.
|
|
804
|
+
* result, or null when the send threw. The recorded result tracks what the
|
|
805
|
+
* adapter actually did, so a status write that fails after a successful
|
|
806
|
+
* send is logged and swallowed rather than downgrading the result.
|
|
798
807
|
*/
|
|
799
808
|
private async sendAndRecord(
|
|
800
809
|
dispatch: PendingChannelDispatch,
|
|
@@ -822,14 +831,25 @@ export class NotificationBroadcaster {
|
|
|
822
831
|
const resolvedMessageId =
|
|
823
832
|
adapterResult.messageId ?? pairing.messageId ?? undefined;
|
|
824
833
|
if (hasPersistedDecision) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
834
|
+
try {
|
|
835
|
+
updateDeliveryStatus(
|
|
836
|
+
deliveryId,
|
|
837
|
+
"sent",
|
|
838
|
+
undefined,
|
|
839
|
+
adapterResult.messageId
|
|
840
|
+
? { messageId: adapterResult.messageId }
|
|
841
|
+
: undefined,
|
|
842
|
+
);
|
|
843
|
+
} catch (statusErr) {
|
|
844
|
+
// The message is already out; a lost status write must not turn
|
|
845
|
+
// this into a `failed` result. Callers read the results to decide
|
|
846
|
+
// whether a retry would re-deliver, so reporting a real send as
|
|
847
|
+
// failed releases the signal's dedupe claim and double-sends.
|
|
848
|
+
log.warn(
|
|
849
|
+
{ err: statusErr, channel, signalId: signal.signalId },
|
|
850
|
+
"Failed to record a sent delivery; the send itself succeeded",
|
|
851
|
+
);
|
|
852
|
+
}
|
|
833
853
|
}
|
|
834
854
|
results.push(
|
|
835
855
|
buildDeliveryResult(dispatch, "sent", {
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
NOTIFICATION_TITLE_MAX_LENGTH,
|
|
31
31
|
readPayloadString,
|
|
32
32
|
sanitizeIdentityField,
|
|
33
|
+
sanitizeMessagePreview,
|
|
33
34
|
} from "./notification-utils.js";
|
|
34
35
|
import type {
|
|
35
36
|
NotificationSignal,
|
|
@@ -47,6 +48,36 @@ function str(value: unknown, fallback: string): string {
|
|
|
47
48
|
return fallback;
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Read an untrusted payload string for interpolation into copy: sanitized
|
|
53
|
+
* like an identity field, undefined when the value is absent, not a string,
|
|
54
|
+
* or sanitizes down to nothing.
|
|
55
|
+
*/
|
|
56
|
+
function optionalSanitizedPayloadField(value: unknown): string | undefined {
|
|
57
|
+
return typeof value === "string"
|
|
58
|
+
? nonEmpty(sanitizeIdentityField(value))
|
|
59
|
+
: undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** {@link optionalSanitizedPayloadField} with a fallback for the empty case. */
|
|
63
|
+
function sanitizedPayloadField(value: unknown, fallback: string): string {
|
|
64
|
+
return optionalSanitizedPayloadField(value) ?? fallback;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Plugin and schedule names from a `schedule.*` payload, with the fallbacks
|
|
69
|
+
* every schedule template shares.
|
|
70
|
+
*/
|
|
71
|
+
function schedulePayloadNames(payload: Record<string, unknown>): {
|
|
72
|
+
scheduleName: string;
|
|
73
|
+
pluginName: string;
|
|
74
|
+
} {
|
|
75
|
+
return {
|
|
76
|
+
scheduleName: sanitizedPayloadField(payload.scheduleName, "a schedule"),
|
|
77
|
+
pluginName: sanitizedPayloadField(payload.pluginName, "A plugin"),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
50
81
|
/**
|
|
51
82
|
* Derive a short notification title from a message body. Trims to the
|
|
52
83
|
* first sentence terminator when present, then caps the result at
|
|
@@ -115,6 +146,44 @@ const TEMPLATES: Partial<Record<NotificationSourceEventName, CopyTemplate>> = {
|
|
|
115
146
|
body: str(payload.message, "A reminder has fired"),
|
|
116
147
|
}),
|
|
117
148
|
|
|
149
|
+
// The schedule.* fields below (names, cadence, error reason) originate in
|
|
150
|
+
// plugin-authored declaration files, so they are sanitized like any other
|
|
151
|
+
// untrusted actor-controlled string before interpolation.
|
|
152
|
+
"schedule.declared": (payload) => {
|
|
153
|
+
const { scheduleName, pluginName } = schedulePayloadNames(payload);
|
|
154
|
+
const cadence = optionalSanitizedPayloadField(payload.cadence);
|
|
155
|
+
const cadenceSuffix = cadence ? ` (${cadence})` : "";
|
|
156
|
+
return {
|
|
157
|
+
title: `New plugin schedule: ${scheduleName}`,
|
|
158
|
+
body: `Plugin "${pluginName}" armed the schedule "${scheduleName}"${cadenceSuffix}. It will run automatically; you can disable it from your schedules.`,
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
"schedule.definition_changed": (payload) => {
|
|
163
|
+
const { scheduleName, pluginName } = schedulePayloadNames(payload);
|
|
164
|
+
return {
|
|
165
|
+
title: `Plugin schedule changed: ${scheduleName}`,
|
|
166
|
+
body: `Plugin "${pluginName}" changed the definition of its schedule "${scheduleName}".`,
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
"schedule.definition_error": (payload) => {
|
|
171
|
+
const { scheduleName, pluginName } = schedulePayloadNames(payload);
|
|
172
|
+
const reason = sanitizeMessagePreview(
|
|
173
|
+
str(payload.reason, "the declaration is invalid"),
|
|
174
|
+
);
|
|
175
|
+
// `paused` says the schedule stopped running over this error rather than
|
|
176
|
+
// carrying on with what it last loaded, which is the part the user acts on.
|
|
177
|
+
const pausedSuffix =
|
|
178
|
+
payload.paused === true
|
|
179
|
+
? " It is paused until the declaration loads again."
|
|
180
|
+
: "";
|
|
181
|
+
return {
|
|
182
|
+
title: `Plugin schedule error: ${scheduleName}`,
|
|
183
|
+
body: `Plugin "${pluginName}" has a schedule "${scheduleName}" that could not be loaded: ${reason}${pausedSuffix}`,
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
|
|
118
187
|
"guardian.question": (payload) => {
|
|
119
188
|
const question = str(
|
|
120
189
|
payload.questionText,
|
|
@@ -21,6 +21,7 @@ import type { ConversationCreateType } from "../persistence/conversation-types.j
|
|
|
21
21
|
import { isPlatformClientConfigured } from "../platform/client.js";
|
|
22
22
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
23
23
|
import { getLogger } from "../util/logger.js";
|
|
24
|
+
import { withSqliteRetry } from "../util/sqlite-retry.js";
|
|
24
25
|
import { VellumAdapter } from "./adapters/macos.js";
|
|
25
26
|
import { PlatformPushAdapter } from "./adapters/platform.js";
|
|
26
27
|
import { SlackAdapter } from "./adapters/slack.js";
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
type DeterministicCheckContext,
|
|
37
38
|
runDeterministicChecks,
|
|
38
39
|
} from "./deterministic-checks.js";
|
|
39
|
-
import { createEvent,
|
|
40
|
+
import { createEvent, setEventDedupeKey } from "./events-store.js";
|
|
40
41
|
import { writeHomeFeedItemForSignal } from "./home-feed-side-effect.js";
|
|
41
42
|
import { dispatchDecision } from "./runtime-dispatch.js";
|
|
42
43
|
import type {
|
|
@@ -226,6 +227,25 @@ export interface EmitSignalResult {
|
|
|
226
227
|
dispatched: boolean;
|
|
227
228
|
reason: string;
|
|
228
229
|
deliveryResults: NotificationDeliveryResult[];
|
|
230
|
+
/**
|
|
231
|
+
* True when the pipeline threw before reaching a verdict. Dispatch, dedupe,
|
|
232
|
+
* suppression, and a blocked deterministic check are all verdicts. Callers
|
|
233
|
+
* that latch their own guard on a completed emit branch on this flag;
|
|
234
|
+
* `reason` is a human-facing log string and never a control signal.
|
|
235
|
+
*/
|
|
236
|
+
pipelineFailed: boolean;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* True when at least one channel adapter has already been handed the signal.
|
|
241
|
+
* `sent` is a completed delivery; `pending` is the platform push that passed
|
|
242
|
+
* its outcome deadline and is still in flight. Both mean a retry would
|
|
243
|
+
* re-deliver.
|
|
244
|
+
*/
|
|
245
|
+
function hasChannelSideEffect(
|
|
246
|
+
results: readonly NotificationDeliveryResult[],
|
|
247
|
+
): boolean {
|
|
248
|
+
return results.some((r) => r.status === "sent" || r.status === "pending");
|
|
229
249
|
}
|
|
230
250
|
|
|
231
251
|
/**
|
|
@@ -245,6 +265,15 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
245
265
|
): Promise<EmitSignalResult> {
|
|
246
266
|
const signalId = uuid();
|
|
247
267
|
|
|
268
|
+
// The event row claims `params.dedupeKey` when it lands, and the failure
|
|
269
|
+
// path below releases that claim.
|
|
270
|
+
let eventPersisted = false;
|
|
271
|
+
|
|
272
|
+
// Every channel result the broadcast produces, appended as each channel
|
|
273
|
+
// settles. A throw discards the dispatch return value, so this is how the
|
|
274
|
+
// failure path learns which channels already ran.
|
|
275
|
+
const channelResults: NotificationDeliveryResult[] = [];
|
|
276
|
+
|
|
248
277
|
const signal: NotificationSignal<TEventName> = {
|
|
249
278
|
signalId,
|
|
250
279
|
createdAt: Date.now(),
|
|
@@ -263,16 +292,27 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
263
292
|
};
|
|
264
293
|
|
|
265
294
|
try {
|
|
266
|
-
// Step 1: Persist the event
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
295
|
+
// Step 1: Persist the event. The insert contends with other writers on
|
|
296
|
+
// the shared database (notably the memory worker's bulk writes), and a
|
|
297
|
+
// lost signal is unrecoverable (the producer has already returned by
|
|
298
|
+
// the time contention surfaces), so transient `SQLITE_BUSY`/`SQLITE_IOERR`
|
|
299
|
+
// rides the shared retry helper instead of failing the pipeline.
|
|
300
|
+
const eventRow = await withSqliteRetry(
|
|
301
|
+
() =>
|
|
302
|
+
createEvent({
|
|
303
|
+
id: signalId,
|
|
304
|
+
sourceEventName: params.sourceEventName,
|
|
305
|
+
sourceChannel: params.sourceChannel,
|
|
306
|
+
sourceContextId: params.sourceContextId,
|
|
307
|
+
attentionHints: params.attentionHints,
|
|
308
|
+
payload: (params.contextPayload ?? {}) as Record<string, unknown>,
|
|
309
|
+
dedupeKey: params.dedupeKey,
|
|
310
|
+
}),
|
|
311
|
+
{
|
|
312
|
+
op: "notification-create-event",
|
|
313
|
+
context: { signalId, sourceEventName: params.sourceEventName },
|
|
314
|
+
},
|
|
315
|
+
);
|
|
276
316
|
|
|
277
317
|
if (!eventRow) {
|
|
278
318
|
log.info(
|
|
@@ -285,8 +325,10 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
285
325
|
dispatched: false,
|
|
286
326
|
reason: "Signal deduplicated at event store level",
|
|
287
327
|
deliveryResults: [],
|
|
328
|
+
pipelineFailed: false,
|
|
288
329
|
};
|
|
289
330
|
}
|
|
331
|
+
eventPersisted = true;
|
|
290
332
|
|
|
291
333
|
// Step 1.5: Source-active pre-gate. visibleInSourceNow is a hard invariant
|
|
292
334
|
// the decision engine cannot override, and it depends only on the signal —
|
|
@@ -307,6 +349,7 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
307
349
|
dispatched: false,
|
|
308
350
|
reason: `Signal suppressed: ${sourceActiveCheck.reason}`,
|
|
309
351
|
deliveryResults: [],
|
|
352
|
+
pipelineFailed: false,
|
|
310
353
|
};
|
|
311
354
|
}
|
|
312
355
|
|
|
@@ -427,7 +470,7 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
427
470
|
// only the producer's dedupeKey, which may be null).
|
|
428
471
|
if (decision.dedupeKey && !params.dedupeKey) {
|
|
429
472
|
try {
|
|
430
|
-
|
|
473
|
+
setEventDedupeKey(signalId, decision.dedupeKey);
|
|
431
474
|
} catch (err) {
|
|
432
475
|
log.warn(
|
|
433
476
|
{ err, signalId },
|
|
@@ -458,6 +501,7 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
458
501
|
dispatched: false,
|
|
459
502
|
reason: `Signal blocked by deterministic checks: ${checkResult.reason}`,
|
|
460
503
|
deliveryResults: [],
|
|
504
|
+
pipelineFailed: false,
|
|
461
505
|
};
|
|
462
506
|
}
|
|
463
507
|
}
|
|
@@ -472,9 +516,10 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
472
516
|
signal,
|
|
473
517
|
decision,
|
|
474
518
|
broadcaster,
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
:
|
|
519
|
+
{
|
|
520
|
+
onConversationCreated: params.onConversationCreated,
|
|
521
|
+
resultsSink: channelResults,
|
|
522
|
+
},
|
|
478
523
|
);
|
|
479
524
|
|
|
480
525
|
// Step 5: Mirror background-origin signals into the home activity feed.
|
|
@@ -511,6 +556,7 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
511
556
|
dispatched: dispatchResult.dispatched,
|
|
512
557
|
reason: dispatchResult.reason,
|
|
513
558
|
deliveryResults: dispatchResult.deliveryResults,
|
|
559
|
+
pipelineFailed: false,
|
|
514
560
|
};
|
|
515
561
|
} catch (err) {
|
|
516
562
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -518,6 +564,26 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
518
564
|
{ err: errMsg, signalId, sourceEventName: params.sourceEventName },
|
|
519
565
|
"Signal pipeline failed",
|
|
520
566
|
);
|
|
567
|
+
// The persisted event claimed this signal's dedupe key before the
|
|
568
|
+
// pipeline threw. Leaving the claim in place makes every retry
|
|
569
|
+
// short-circuit as a duplicate of an emit that never reached a verdict,
|
|
570
|
+
// so release it. The row itself stays as the audit trail of the attempt.
|
|
571
|
+
//
|
|
572
|
+
// A partially dispatched broadcast is the exception. Its retry gets a
|
|
573
|
+
// fresh decision id, and delivery dedupe is keyed to the decision, so
|
|
574
|
+
// the channels that already went out would go out again. Keeping the
|
|
575
|
+
// claim costs the channels that did not get the signal; releasing it
|
|
576
|
+
// double-sends to the ones that did.
|
|
577
|
+
if (eventPersisted && !hasChannelSideEffect(channelResults)) {
|
|
578
|
+
try {
|
|
579
|
+
setEventDedupeKey(signalId, null);
|
|
580
|
+
} catch (releaseErr) {
|
|
581
|
+
log.warn(
|
|
582
|
+
{ err: releaseErr, signalId },
|
|
583
|
+
"Failed to release the dedupe key of a failed signal",
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
521
587
|
if (params.throwOnError) {
|
|
522
588
|
throw err instanceof Error ? err : new Error(errMsg);
|
|
523
589
|
}
|
|
@@ -527,6 +593,7 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
527
593
|
dispatched: false,
|
|
528
594
|
reason: `Signal pipeline failed: ${errMsg}`,
|
|
529
595
|
deliveryResults: [],
|
|
596
|
+
pipelineFailed: true,
|
|
530
597
|
};
|
|
531
598
|
}
|
|
532
599
|
}
|
|
@@ -91,8 +91,16 @@ export function createEvent(
|
|
|
91
91
|
return row;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
/**
|
|
95
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Write an existing event's dedupe key. A string claims that key, which is
|
|
96
|
+
* how a decision-engine-generated key lands on the row. `null` releases the
|
|
97
|
+
* claim so a later event may take it while this row stays for the audit
|
|
98
|
+
* trail.
|
|
99
|
+
*/
|
|
100
|
+
export function setEventDedupeKey(
|
|
101
|
+
eventId: string,
|
|
102
|
+
dedupeKey: string | null,
|
|
103
|
+
): void {
|
|
96
104
|
const db = getDb();
|
|
97
105
|
db.update(notificationEvents)
|
|
98
106
|
.set({ dedupeKey, updatedAt: Date.now() })
|
|
@@ -170,9 +170,12 @@ function deriveFallbackTitle(summary: string): string {
|
|
|
170
170
|
// fence into a stray backtick). Ordered markers matter most: the seed prompt
|
|
171
171
|
// asks the model for numbered lists, and a leading `1.` also reads as a
|
|
172
172
|
// sentence terminator, which would truncate the title to the first digit.
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
173
|
+
//
|
|
174
|
+
// Deliberately regex-based, unlike the remark-backed
|
|
175
|
+
// `stripMarkdownForPreview`. The leading-space allowance and rule set mirror
|
|
176
|
+
// `flattenToPlainText` in workspace migration 138, which must stay
|
|
177
|
+
// self-contained and so cannot share code with either: a backfilled title and
|
|
178
|
+
// a freshly written one have to match, which a parser swap would break.
|
|
176
179
|
const deblocked = summary
|
|
177
180
|
.replace(/^\s{0,3}(?:```|~~~).*$/gm, "")
|
|
178
181
|
.replace(/^\s{0,3}#{1,6}\s+/gm, "")
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* circular dependencies.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import type { Root, RootContent } from "mdast";
|
|
10
|
+
|
|
11
|
+
import { parseMarkdown } from "../messaging/content/parse.js";
|
|
12
|
+
import { stripAnsiAndControlChars } from "../util/ansi.js";
|
|
9
13
|
import { isPlainObject } from "../util/object.js";
|
|
10
14
|
|
|
11
15
|
// ── String helpers ──────────────────────────────────────────────────────────
|
|
@@ -59,14 +63,178 @@ export function truncate(text: string, maxLength: number): string {
|
|
|
59
63
|
return text.slice(0, maxLength - 1) + "…";
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
// ── Markdown flattening ─────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Node types whose children are blocks rather than an inline run, so their text
|
|
70
|
+
* needs a paragraph break between children instead of being butted together.
|
|
71
|
+
*/
|
|
72
|
+
const BLOCK_CONTAINER_TYPES = new Set([
|
|
73
|
+
"blockquote",
|
|
74
|
+
"listItem",
|
|
75
|
+
"footnoteDefinition",
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
/** Plain text of one mdast node, markers and media dropped. */
|
|
79
|
+
function nodeText(node: RootContent): string {
|
|
80
|
+
switch (node.type) {
|
|
81
|
+
// Literal runs. Code keeps its value: a fenced block previews as its code,
|
|
82
|
+
// and the parser has already decided what is code, so markdown-looking
|
|
83
|
+
// lines inside it are safe.
|
|
84
|
+
case "text":
|
|
85
|
+
case "inlineCode":
|
|
86
|
+
case "code":
|
|
87
|
+
return node.value;
|
|
88
|
+
// Raw HTML, which the parser splits into tag nodes around the text between
|
|
89
|
+
// them. The in-app renderer registers no `rehype-raw`, so tags and comments
|
|
90
|
+
// display as nothing there; dropping the tag nodes keeps the preview
|
|
91
|
+
// matching what the reply actually looks like, and keeps the contents of an
|
|
92
|
+
// HTML comment off the lock screen. The visible text between a tag pair is
|
|
93
|
+
// its own `text` node and survives.
|
|
94
|
+
case "html":
|
|
95
|
+
return "";
|
|
96
|
+
// Media carries no prose. A caller names it separately when the flattened
|
|
97
|
+
// text comes up empty; see {@link mediaEmbedAltTexts}.
|
|
98
|
+
//
|
|
99
|
+
// A space rather than nothing: an embed needs no surrounding whitespace, so
|
|
100
|
+
// `beforeafter` would otherwise flatten to one run-on word. The
|
|
101
|
+
// callers collapse and trim, so an embed-only reply still reduces to empty
|
|
102
|
+
// and still reaches its media fallback.
|
|
103
|
+
case "image":
|
|
104
|
+
case "imageReference":
|
|
105
|
+
return " ";
|
|
106
|
+
case "break":
|
|
107
|
+
return "\n";
|
|
108
|
+
case "thematicBreak":
|
|
109
|
+
return "";
|
|
110
|
+
case "table":
|
|
111
|
+
case "list":
|
|
112
|
+
return node.children.map(nodeText).join("\n");
|
|
113
|
+
case "tableRow":
|
|
114
|
+
return node.children.map(nodeText).join(" ");
|
|
115
|
+
default:
|
|
116
|
+
return "children" in node
|
|
117
|
+
? node.children
|
|
118
|
+
.map(nodeText)
|
|
119
|
+
.join(BLOCK_CONTAINER_TYPES.has(node.type) ? "\n\n" : "")
|
|
120
|
+
: "";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Flatten markdown to the plain text a notification surface renders, where
|
|
126
|
+
* markers would otherwise arrive as literal punctuation.
|
|
127
|
+
*
|
|
128
|
+
* Parses with the same remark/GFM processor the web client and the channel
|
|
129
|
+
* renderers use, rather than matching markers by hand. That is what keeps a
|
|
130
|
+
* balanced-paren URL, a tilde fence nested in a backtick one, a pipeless GFM
|
|
131
|
+
* table, and image syntax quoted inside a code span from each needing their own
|
|
132
|
+
* special case.
|
|
133
|
+
*
|
|
134
|
+
* Media embeds are dropped whole, alt text included. The alt describes the
|
|
135
|
+
* attachment rather than adding prose, so keeping it previews a video as its
|
|
136
|
+
* own caption. Copy that is nothing but embeds therefore flattens to empty,
|
|
137
|
+
* which is a caller's signal to describe the media instead.
|
|
138
|
+
*
|
|
139
|
+
* Line structure survives, so callers needing multi-line copy keep it; collapse
|
|
140
|
+
* whitespace separately for a single line.
|
|
141
|
+
*/
|
|
142
|
+
export function stripMarkdownForPreview(value: string): string {
|
|
143
|
+
return parseMarkdown(value).children.map(nodeText).join("\n\n");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Destinations the assistant resolves into attachment rows. Mirrors the link
|
|
148
|
+
* forms `daemon/assistant-attachments.ts` extracts.
|
|
149
|
+
*/
|
|
150
|
+
const TRACKED_MEDIA_URL_RE = /^vellum:\/\/(?:workspace|host)\//i;
|
|
151
|
+
|
|
152
|
+
/** A media embed the flattener dropped, kept so a caller can name it. */
|
|
153
|
+
export interface MediaEmbed {
|
|
154
|
+
/**
|
|
155
|
+
* Image description, already plain text: the parser resolves a formatted
|
|
156
|
+
* description rather than handing back its markers.
|
|
157
|
+
*/
|
|
158
|
+
alt: string;
|
|
159
|
+
/**
|
|
160
|
+
* Whether the destination resolves through the attachment store. A
|
|
161
|
+
* `vellum://` embed becomes an attachment row, so a caller holding
|
|
162
|
+
* attachment metadata has already counted it; a remote one leaves no row
|
|
163
|
+
* there and its alt is the only label it has.
|
|
164
|
+
*/
|
|
165
|
+
tracked: boolean;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Collect every image node, depth first. */
|
|
169
|
+
function collectMediaEmbeds(
|
|
170
|
+
node: Root | RootContent,
|
|
171
|
+
into: MediaEmbed[],
|
|
172
|
+
): void {
|
|
173
|
+
if (node.type === "image") {
|
|
174
|
+
into.push({
|
|
175
|
+
alt: node.alt ?? "",
|
|
176
|
+
tracked: TRACKED_MEDIA_URL_RE.test(node.url),
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
// A reference embed names a definition rather than a destination, so it
|
|
181
|
+
// cannot be matched against the attachment store.
|
|
182
|
+
if (node.type === "imageReference") {
|
|
183
|
+
into.push({ alt: node.alt ?? "", tracked: false });
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if ("children" in node) {
|
|
187
|
+
for (const child of node.children) {
|
|
188
|
+
collectMediaEmbeds(child, into);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The media embeds in `value`, in order, empty alts included.
|
|
195
|
+
*
|
|
196
|
+
* Lets a caller describe copy whose entire content was embeds. Remote
|
|
197
|
+
* `https://` embeds are as valid as `vellum://` ones and register nothing in
|
|
198
|
+
* the attachment store, so without this a reply of one remote image would go
|
|
199
|
+
* out with no copy at all rather than merely an unhelpful one.
|
|
200
|
+
*/
|
|
201
|
+
export function mediaEmbeds(value: string): MediaEmbed[] {
|
|
202
|
+
const embeds: MediaEmbed[] = [];
|
|
203
|
+
collectMediaEmbeds(parseMarkdown(value), embeds);
|
|
204
|
+
return embeds;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Copy naming media, from whatever labels a caller could recover: attachment
|
|
209
|
+
* filenames, or the alt texts {@link mediaEmbedAltTexts} pulled back out of
|
|
210
|
+
* copy that flattened away.
|
|
211
|
+
*
|
|
212
|
+
* Labels are model- and tool-authored, so a single one is sanitized before it
|
|
213
|
+
* can reach the lock screen; one that sanitizes away leaves generic copy rather
|
|
214
|
+
* than a dangling "Sent ".
|
|
215
|
+
*
|
|
216
|
+
* Returns an empty string for no labels, leaving the caller to decide whether
|
|
217
|
+
* that means suppress the notification or fall back further.
|
|
218
|
+
*/
|
|
219
|
+
export function describeMedia(labels: string[]): string {
|
|
220
|
+
if (labels.length === 0) {
|
|
221
|
+
return "";
|
|
222
|
+
}
|
|
223
|
+
if (labels.length > 1) {
|
|
224
|
+
return `Sent ${labels.length} attachments`;
|
|
225
|
+
}
|
|
226
|
+
const label = sanitizeMessagePreview(labels[0].replace(/\s+/g, " ").trim());
|
|
227
|
+
return label ? `Sent ${label}` : "Sent an attachment";
|
|
228
|
+
}
|
|
229
|
+
|
|
62
230
|
// ── Sanitization ────────────────────────────────────────────────────────────
|
|
63
231
|
|
|
64
|
-
/**
|
|
232
|
+
/**
|
|
233
|
+
* Strip escape sequences, flatten control characters and newlines to spaces,
|
|
234
|
+
* then truncate to `maxLength`.
|
|
235
|
+
*/
|
|
65
236
|
function sanitize(value: string, maxLength: number): string {
|
|
66
|
-
return truncate(
|
|
67
|
-
value.replace(/[\x00-\x1f\x7f-\x9f\r\n]+/g, " ").trim(),
|
|
68
|
-
maxLength,
|
|
69
|
-
);
|
|
237
|
+
return truncate(stripAnsiAndControlChars(value, " ").trim(), maxLength);
|
|
70
238
|
}
|
|
71
239
|
|
|
72
240
|
/**
|
|
@@ -65,6 +65,18 @@ export const NOTIFICATION_SOURCE_EVENT_NAMES = [
|
|
|
65
65
|
id: "schedule.notify",
|
|
66
66
|
description: "Scheduled notification triggered (one-shot or recurring)",
|
|
67
67
|
},
|
|
68
|
+
{
|
|
69
|
+
id: "schedule.definition_error",
|
|
70
|
+
description: "Plugin schedule declaration failed to parse or validate",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "schedule.declared",
|
|
74
|
+
description: "Plugin-declared schedule armed for the first time",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "schedule.definition_changed",
|
|
78
|
+
description: "Plugin upgrade changed an armed schedule's definition",
|
|
79
|
+
},
|
|
68
80
|
{
|
|
69
81
|
id: "guardian.question",
|
|
70
82
|
description: "Guardian approval question requiring response",
|
package/src/oauth/AGENTS.md
CHANGED
|
@@ -35,9 +35,11 @@ Managed-sign-in users should get the integration pre-enabled by setting `service
|
|
|
35
35
|
|
|
36
36
|
### 4. Set the logo URL — `seed-providers.ts`
|
|
37
37
|
|
|
38
|
-
The `logoUrl` field in `seed-providers.ts` is the source of truth for a provider's logo. Most providers use a [Simple Icons](https://simpleicons.org) (CC0-licensed) CDN URL like `https://cdn.simpleicons.org/acme`.
|
|
38
|
+
The `logoUrl` field in `seed-providers.ts` is the source of truth for a provider's logo. Most providers use a [Simple Icons](https://simpleicons.org) (CC0-licensed) CDN URL like `https://cdn.simpleicons.org/acme`.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
**Verify the URL actually resolves before you commit it.** `oauth-provider-seed-logos.test.ts` only checks the prefix, so a slug that Simple Icons has never hosted (or has since dropped) passes CI and then renders as an initials avatar in the client. Simple Icons removes brands on trademark request: Salesforce is gone, every Microsoft product went in v13, and Slack is currently absent pending permission from the trademark owner. For those, use the `glincker/thesvg` source via jsDelivr: `https://cdn.jsdelivr.net/gh/glincker/thesvg@main/public/icons/<key>/default.svg`. The recognised `logoUrl` prefixes are enforced by `oauth-provider-seed-logos.test.ts`; if you need a third source, extend that allowlist.
|
|
41
|
+
|
|
42
|
+
`logoUrl` is a fallback, not the last word. The web client prefers a logo bundled in `clients/web/public/images/integrations/` when one exists for the provider key, uses `logoUrl` when it doesn't, and only then falls back to an initials avatar (see `BUNDLED_LOGO_URLS` in `clients/web/src/components/integrations/integration-icon.tsx`). Bundling an asset for a new first-class provider is optional but preferred: it survives a CDN removal, works offline, and keeps the integrations list from telling a third party which providers a user is looking at.
|
|
41
43
|
|
|
42
44
|
### 5. Secret patterns (if applicable) — `packages/service-contracts/src/secret-detection.ts`
|
|
43
45
|
|
|
@@ -47,6 +47,11 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
47
47
|
async (token) => {
|
|
48
48
|
const effectiveBaseUrl = req.baseUrl ?? this.baseUrl;
|
|
49
49
|
const isTelegram = this.provider === "telegram";
|
|
50
|
+
// Discord bot tokens authenticate with the `Bot ` scheme, not
|
|
51
|
+
// `Bearer`. Sending Bearer here reaches Discord as an unusable
|
|
52
|
+
// credential and comes back 401, which reads as a revoked token.
|
|
53
|
+
const authScheme =
|
|
54
|
+
this.provider === "discord_channel" ? "Bot" : "Bearer";
|
|
50
55
|
const requestPath = isTelegram
|
|
51
56
|
? buildTelegramBotApiPath(req.path, token)
|
|
52
57
|
: req.path;
|
|
@@ -87,7 +92,7 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
if (!isTelegram) {
|
|
90
|
-
headers.set("Authorization",
|
|
95
|
+
headers.set("Authorization", `${authScheme} ${token}`);
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
const resp = await fetch(fullUrl, {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getSecureKeyResultAsync,
|
|
20
20
|
type SecureKeyResult,
|
|
21
21
|
} from "../security/secure-keys.js";
|
|
22
|
+
import { manualTokenProvider } from "./manual-token-providers.js";
|
|
22
23
|
|
|
23
24
|
// ── Types ─────────────────────────────────────────────────────────────
|
|
24
25
|
|
|
@@ -44,16 +45,8 @@ export interface ConnectionAccessTokenResult {
|
|
|
44
45
|
* rather than assuming the OAuth access-token path.
|
|
45
46
|
*/
|
|
46
47
|
function manualTokenAccessCredentialKey(provider: string): string | null {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return credentialKey("slack_channel", "bot_token");
|
|
50
|
-
case "telegram":
|
|
51
|
-
return credentialKey("telegram", "bot_token");
|
|
52
|
-
case "sanity":
|
|
53
|
-
return credentialKey("sanity", "api_token");
|
|
54
|
-
default:
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
48
|
+
const spec = manualTokenProvider(provider);
|
|
49
|
+
return spec ? credentialKey(provider, spec.accessField) : null;
|
|
57
50
|
}
|
|
58
51
|
|
|
59
52
|
/**
|