@vellumai/assistant 0.10.12 → 0.11.0-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 +1 -1
- package/ARCHITECTURE.md +4 -5
- package/README.md +1 -1
- package/docs/architecture/memory.md +60 -23
- package/docs/error-handling.md +3 -3
- package/docs/guardian-request-flow.md +112 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/app.d.ts +28 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/app.d.ts.map +1 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.d.ts +2 -2
- package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.d.ts.map +1 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/index.js.map +1 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/message.d.ts +2 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/dist/events/message.d.ts.map +1 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@slack/types/package.json +1 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
- package/node_modules/@vellumai/gateway-client/package.json +1 -1
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +34 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +9 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
- package/openapi.yaml +264 -14
- package/package.json +2 -2
- package/src/__tests__/acp-session.test.ts +3 -3
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +3 -0
- package/src/__tests__/anthropic-provider.test.ts +40 -0
- package/src/__tests__/app-conversation-ids-backfill.test.ts +33 -2
- package/src/__tests__/app-conversation-ids.test.ts +204 -0
- package/src/__tests__/approval-cascade.test.ts +5 -5
- package/src/__tests__/approval-routes-http.test.ts +3 -3
- package/src/__tests__/ask-question-resolver.test.ts +207 -0
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +16 -16
- package/src/__tests__/assistant-event-hub-targeted.test.ts +13 -11
- package/src/__tests__/assistant-event-hub.test.ts +13 -11
- package/src/__tests__/assistant-event.test.ts +12 -9
- package/src/__tests__/assistant-feature-flag-guard.test.ts +3 -3
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +1 -1
- package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -58
- package/src/__tests__/assistant-stream-state.test.ts +17 -7
- package/src/__tests__/avatar-identity-sync.test.ts +3 -3
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +134 -18
- package/src/__tests__/call-start-guardian-guard.test.ts +27 -0
- package/src/__tests__/channel-approvals.test.ts +0 -27
- package/src/__tests__/channel-guardian.test.ts +85 -512
- package/src/__tests__/channel-readiness-slack-remote.test.ts +151 -3
- package/src/__tests__/channel-retry-sweep.test.ts +88 -1
- package/src/__tests__/channel-setup-panel-ack.test.ts +12 -9
- package/src/__tests__/chat-reveal-guard-priming.test.ts +31 -34
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +2 -2
- package/src/__tests__/compaction-events.test.ts +8 -8
- package/src/__tests__/config-loader-platform-defaults.test.ts +54 -28
- package/src/__tests__/config-model-image-provider.test.ts +20 -0
- package/src/__tests__/config-schema-memory-substrate.test.ts +306 -0
- package/src/__tests__/config-schema.test.ts +14 -2
- package/src/__tests__/config-set-platform-guard.test.ts +11 -12
- package/src/__tests__/config-sounds-sync.test.ts +4 -4
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +5 -0
- package/src/__tests__/context-search-memory-source.test.ts +1 -1
- package/src/__tests__/context-search-memory-v2-source.test.ts +6 -6
- package/src/__tests__/context-token-estimator.test.ts +54 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +12 -3
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +13 -4
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +4 -4
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +22 -13
- package/src/__tests__/conversation-agent-loop.test.ts +39 -30
- package/src/__tests__/conversation-confirmation-signals.test.ts +14 -14
- package/src/__tests__/conversation-evictor.test.ts +49 -0
- package/src/__tests__/conversation-fork-crud.test.ts +1 -1
- package/src/__tests__/conversation-group-tools.test.ts +279 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +5 -5
- package/src/__tests__/conversation-lineage.test.ts +235 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +5 -5
- package/src/__tests__/conversation-message-sync-tags.test.ts +4 -4
- package/src/__tests__/conversation-queue.test.ts +81 -81
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +8 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +8 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +10 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +29 -2
- package/src/__tests__/conversation-slash-queue.test.ts +11 -11
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -2
- package/src/__tests__/conversation-speed-override.test.ts +2 -2
- package/src/__tests__/conversation-summarize-route.test.ts +9 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +18 -16
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +17 -17
- package/src/__tests__/conversation-surfaces-app-open.test.ts +7 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +7 -7
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +9 -9
- package/src/__tests__/conversation-surfaces-standalone.test.ts +12 -12
- package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
- package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +46 -46
- package/src/__tests__/conversation-sync-tags.test.ts +5 -5
- package/src/__tests__/conversation-wait-for-idle.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -3
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -3
- package/src/__tests__/copy-composer-tc-templates.test.ts +3 -131
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/daemon-assistant-events.test.ts +14 -15
- package/src/__tests__/db-test-helpers.ts +13 -27
- package/src/__tests__/disk-pressure-routes.test.ts +12 -3
- package/src/__tests__/document-tool-security.test.ts +95 -0
- package/src/__tests__/drain-kick-guard.test.ts +232 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +19 -6
- package/src/__tests__/emit-event-signal.test.ts +5 -6
- package/src/__tests__/encrypted-store-test-helpers.ts +10 -20
- package/src/__tests__/events-tail-route.test.ts +6 -4
- package/src/__tests__/feature-flag-test-helpers.ts +10 -20
- package/src/__tests__/file-edit-tool.test.ts +24 -4
- package/src/__tests__/file-list-tool.test.ts +2 -1
- package/src/__tests__/file-read-tool.test.ts +62 -10
- package/src/__tests__/file-write-tool.test.ts +24 -5
- package/src/__tests__/followup-tools.test.ts +58 -0
- package/src/__tests__/gateway-threshold-reader-mock.ts +99 -0
- package/src/__tests__/graph-extraction-event-date.test.ts +1 -1
- package/src/__tests__/guardian-question-mode.test.ts +42 -3
- package/src/__tests__/guardian-reply-router-answer-mode.test.ts +283 -0
- package/src/__tests__/guardian-vellum-destination-pinning.test.ts +221 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +10 -10
- package/src/__tests__/host-browser-proxy.test.ts +1 -1
- package/src/__tests__/host-file-edit-tool.test.ts +3 -3
- package/src/__tests__/host-file-read-tool.test.ts +2 -2
- package/src/__tests__/host-file-write-tool.test.ts +7 -3
- package/src/__tests__/host-shell-tool.test.ts +17 -5
- package/src/__tests__/host-transfer-pending-interactions.test.ts +8 -8
- package/src/__tests__/image-credentials.test.ts +65 -8
- package/src/__tests__/injection-block.test.ts +4 -4
- package/src/__tests__/injector-chain.test.ts +1 -1
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-registry-order-guard.test.ts +4 -9
- package/src/__tests__/injector-v3-suppression.test.ts +6 -4
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +27 -28
- package/src/__tests__/list-messages-page-latest.test.ts +2 -2
- package/src/__tests__/llm-usage-store.test.ts +67 -4
- package/src/__tests__/log-redact.test.ts +18 -0
- package/src/__tests__/managed-profile-guard.test.ts +27 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +62 -11
- package/src/__tests__/memory-identity-context-parity.test.ts +1 -1
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +9 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +4 -4
- package/src/__tests__/notification-decision-strategy.test.ts +115 -11
- package/src/__tests__/notification-deep-link.test.ts +12 -12
- package/src/__tests__/notification-vellum-adapter.test.ts +8 -8
- package/src/__tests__/outbound-slack-persistence.test.ts +8 -8
- package/src/__tests__/path-policy.test.ts +315 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +6 -6
- package/src/__tests__/persistence-layering-guard.test.ts +2 -2
- package/src/__tests__/pkb-autoinject.test.ts +1 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -0
- package/src/__tests__/platform-callback-registration.test.ts +10 -10
- package/src/__tests__/plugin-event-hub-facade.test.ts +22 -15
- package/src/__tests__/plugin-import-boundary-guard.test.ts +15 -15
- package/src/__tests__/processing-flag-persist-failure.test.ts +0 -4
- package/src/__tests__/provider-platform-proxy-integration.test.ts +0 -1
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
- package/src/__tests__/request-secret-standalone-channel-gate.test.ts +5 -5
- package/src/__tests__/require-fresh-approval.test.ts +38 -38
- package/src/__tests__/resolve-app-id.test.ts +32 -0
- package/src/__tests__/run-due-schedules.test.ts +72 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +11 -10
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/schedule-routes.test.ts +44 -5
- package/src/__tests__/schedule-store.test.ts +3 -3
- package/src/__tests__/schedule-tools.test.ts +176 -0
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +3 -3
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +3 -3
- package/src/__tests__/secret-response-routing.test.ts +3 -3
- package/src/__tests__/secret-routes-platform-proxy.test.ts +4 -7
- package/src/__tests__/secret-scanner.test.ts +36 -0
- package/src/__tests__/send-endpoint-busy.test.ts +7 -7
- package/src/__tests__/slack-required-scopes-mirror.test.ts +81 -0
- package/src/__tests__/starter-task-flow.test.ts +26 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
- package/src/__tests__/subagent-disposal.test.ts +40 -11
- package/src/__tests__/subagent-fork-notifications.test.ts +4 -4
- package/src/__tests__/subagent-fork-prompt-role.test.ts +3 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +4 -4
- package/src/__tests__/subagent-manager-notify.test.ts +73 -14
- package/src/__tests__/subagent-notify-parent.test.ts +48 -4
- package/src/__tests__/subagent-spawn-and-await.test.ts +62 -14
- package/src/__tests__/subagent-tools.test.ts +64 -0
- package/src/__tests__/sync-message-contract.test.ts +3 -3
- package/src/__tests__/terminal-tools.test.ts +12 -3
- package/src/__tests__/test-preload.ts +9 -0
- package/src/__tests__/tool-approval-handler.test.ts +587 -43
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +7 -4
- package/src/__tests__/tool-executor.test.ts +116 -0
- package/src/__tests__/tool-grant-request-escalation.test.ts +5 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -29
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +5 -5
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +4 -4
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -22
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +13 -13
- package/src/__tests__/ui-work-result-surface.test.ts +3 -3
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +6 -6
- package/src/__tests__/voice-session-bridge.test.ts +44 -44
- package/src/__tests__/workspace-migration-134-image-generation-mode-to-provider.test.ts +284 -0
- package/src/__tests__/workspace-policy.test.ts +482 -2
- package/src/acp/__tests__/client-handler.test.ts +3 -3
- package/src/acp/__tests__/session-manager-persistence.test.ts +10 -10
- package/src/acp/__tests__/session-manager-resume.test.ts +7 -7
- package/src/acp/client-handler.ts +3 -3
- package/src/acp/session-manager.ts +8 -8
- package/src/api/events/subagent-event.ts +2 -2
- package/src/api/responses/memory-v2-activation-log.ts +2 -2
- package/src/api/responses/subagent-detail.ts +13 -0
- package/src/approvals/AGENTS.md +2 -0
- package/src/approvals/guardian-request-resolvers.ts +118 -24
- package/src/apps/app-store.ts +95 -4
- package/src/calls/__tests__/voice-control-protocol.test.ts +57 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +229 -7
- package/src/calls/__tests__/voice-triage-escalate.test.ts +3 -4
- package/src/calls/call-controller.ts +27 -27
- package/src/calls/call-conversation-messages.ts +58 -18
- package/src/calls/voice-control-protocol.ts +56 -0
- package/src/calls/voice-session-bridge.ts +178 -65
- package/src/calls/voice-triage-escalate.ts +2 -3
- package/src/channels/config.ts +8 -0
- package/src/channels/types.ts +1 -0
- package/src/cli/commands/config.ts +49 -13
- package/src/cli/commands/memory/index.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +5 -0
- package/src/cli/commands/webhooks.help.ts +9 -4
- package/src/cli/lib/bundled-marketplace.json +12 -0
- package/src/cli.ts +2 -109
- package/src/config/__tests__/deployment-context-defaults.test.ts +9 -0
- package/src/config/__tests__/substrate-twin-shadowing.test.ts +191 -0
- package/src/config/assistant-feature-flags.ts +1 -1
- package/src/config/bundled-skills/conversation-groups/SKILL.md +29 -0
- package/src/config/bundled-skills/conversation-groups/TOOLS.json +58 -0
- package/src/config/bundled-skills/conversation-groups/tools/group-create.ts +12 -0
- package/src/config/bundled-skills/conversation-groups/tools/group-list.ts +12 -0
- package/src/config/bundled-skills/conversation-groups/tools/move-to-group.ts +12 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +13 -7
- package/src/config/bundled-skills/schedule/SKILL.md +2 -0
- package/src/config/feature-flag-cache.ts +6 -16
- package/src/config/feature-flag-registry.json +14 -38
- package/src/config/loader.ts +7 -1
- package/src/config/memory-tier.ts +15 -10
- package/src/config/memory-v3-gate.test.ts +278 -1
- package/src/config/memory-v3-gate.ts +88 -26
- package/src/config/raw-config-utils.ts +1 -1
- package/src/config/schemas/__tests__/live-voice.test.ts +27 -16
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/live-voice.ts +33 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrospective.ts +13 -0
- package/src/config/schemas/memory-substrate.ts +183 -0
- package/src/config/schemas/memory-v2.ts +3 -2
- package/src/config/schemas/memory-v3.ts +11 -4
- package/src/config/schemas/memory.ts +35 -1
- package/src/config/schemas/services.ts +10 -2
- package/src/config/substrate-twin-shadowing.ts +195 -0
- package/src/config/webhook-routing.ts +67 -0
- package/src/context/compactor.ts +10 -3
- package/src/context/token-estimator.ts +21 -0
- package/src/daemon/__tests__/channel-ui-capability.test.ts +85 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +3 -3
- package/src/daemon/__tests__/embedding-reconcile.test.ts +3 -3
- package/src/daemon/__tests__/inference-profile-notification.test.ts +3 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +5 -5
- package/src/daemon/__tests__/web-search-status-text.test.ts +2 -2
- package/src/daemon/channel-ui-capability.ts +70 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +4 -4
- package/src/daemon/conversation-agent-loop.ts +12 -5
- package/src/daemon/conversation-evictor.ts +26 -4
- package/src/daemon/conversation-launch.ts +11 -14
- package/src/daemon/conversation-lineage.ts +76 -0
- package/src/daemon/conversation-messaging.ts +14 -14
- package/src/daemon/conversation-notifiers.ts +2 -2
- package/src/daemon/conversation-process.ts +133 -21
- package/src/daemon/conversation-queue-manager.ts +7 -7
- package/src/daemon/conversation-runtime-assembly.ts +16 -0
- package/src/daemon/conversation-store.ts +11 -3
- package/src/daemon/conversation-surfaces.ts +8 -14
- package/src/daemon/conversation-tool-setup.ts +13 -7
- package/src/daemon/conversation-usage.ts +12 -11
- package/src/daemon/conversation.ts +38 -15
- package/src/daemon/disk-pressure-guard.ts +5 -12
- package/src/daemon/embedding-reconcile.ts +14 -9
- package/src/daemon/handlers/config-channels.ts +40 -134
- package/src/daemon/handlers/config-model.ts +15 -7
- package/src/daemon/handlers/conversations.ts +4 -2
- package/src/daemon/handlers/shared.ts +29 -0
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/lifecycle.ts +13 -0
- package/src/daemon/message-protocol.ts +8 -144
- package/src/daemon/message-types/computer-use.ts +0 -12
- package/src/daemon/message-types/conversations.ts +9 -345
- package/src/daemon/message-types/host-app-control.ts +0 -9
- package/src/daemon/message-types/host-browser.ts +0 -7
- package/src/daemon/message-types/host-ui-snapshot.ts +0 -7
- package/src/daemon/message-types/messages.ts +0 -48
- package/src/daemon/message-types/notifications.ts +0 -7
- package/src/daemon/message-types/skills.ts +0 -5
- package/src/daemon/message-types/surfaces.ts +0 -9
- package/src/daemon/message-types/sync.ts +0 -2
- package/src/daemon/process-message.ts +5 -5
- package/src/daemon/skill-memory-refresh.ts +5 -5
- package/src/daemon/tool-side-effects.ts +32 -16
- package/src/daemon/trust-context.ts +1 -1
- package/src/daemon/wake-conversation-ops.ts +4 -4
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +100 -7
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +67 -0
- package/src/heartbeat/heartbeat-run-store.ts +81 -4
- package/src/heartbeat/heartbeat-service.ts +32 -1
- package/src/home/__tests__/feed-writer.test.ts +7 -7
- package/src/home/__tests__/home-content-refresh.test.ts +3 -0
- package/src/home/__tests__/suggested-prompts.test.ts +1 -0
- package/src/home/feed-writer.ts +6 -13
- package/src/home/home-content-refresh.ts +6 -9
- package/src/home/relationship-state-writer.ts +5 -12
- package/src/home/suggested-prompts-cache.ts +6 -19
- package/src/ipc/__tests__/events-publish-client.test.ts +67 -0
- package/src/ipc/events-publish-client.ts +44 -0
- package/src/ipc/routes/__tests__/conversation-sync-ipc-routes.test.ts +2 -2
- package/src/ipc/routes/__tests__/events-ipc-routes.test.ts +38 -9
- package/src/ipc/routes/events-ipc-routes.ts +11 -31
- package/src/live-voice/__tests__/front-decision.test.ts +4 -176
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +276 -68
- package/src/live-voice/__tests__/live-voice-continuation-fork-hydration.test.ts +286 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +6 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +185 -46
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +24 -59
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +38 -54
- package/src/live-voice/__tests__/live-voice-vad.test.ts +1154 -636
- package/src/live-voice/front-decision.ts +5 -155
- package/src/live-voice/live-voice-metrics.ts +4 -1
- package/src/live-voice/live-voice-session.ts +1288 -439
- package/src/live-voice/progress-phrases.ts +17 -0
- package/src/live-voice/protocol.ts +15 -0
- package/src/media/app-icon-generator.ts +15 -8
- package/src/media/avatar-router.ts +9 -5
- package/src/media/image-credentials.ts +42 -10
- package/src/media/image-service.ts +1 -13
- package/src/media/types.ts +26 -3
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/__tests__/broadcaster.test.ts +95 -0
- package/src/notifications/adapters/macos.ts +3 -3
- package/src/notifications/broadcaster.ts +57 -0
- package/src/notifications/copy-composer.ts +0 -15
- package/src/notifications/decision-engine.ts +21 -22
- package/src/notifications/guardian-question-mode.ts +57 -2
- package/src/notifications/home-feed-side-effect.ts +4 -0
- package/src/notifications/signal.ts +6 -4
- package/src/notifications/trusted-contact-payloads.ts +3 -4
- package/src/notifications/vellum-card-affinity.ts +39 -0
- package/src/oauth/AGENTS.md +1 -1
- package/src/permissions/channel-permission-query.test.ts +69 -0
- package/src/permissions/channel-permission-query.ts +73 -1
- package/src/permissions/checker.test.ts +298 -15
- package/src/permissions/checker.ts +102 -36
- package/src/permissions/confirmation-guardian-request.test.ts +2 -2
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/gateway-threshold-reader.test.ts +224 -17
- package/src/permissions/gateway-threshold-reader.ts +82 -10
- package/src/permissions/ipc-risk-types.ts +8 -0
- package/src/permissions/prompter.ts +5 -5
- package/src/permissions/question-guardian-request.test.ts +222 -0
- package/src/permissions/question-guardian-request.ts +179 -0
- package/src/permissions/question-prompter.test.ts +8 -8
- package/src/permissions/question-prompter.ts +18 -0
- package/src/permissions/secret-prompter.ts +2 -4
- package/src/permissions/workspace-policy.ts +269 -34
- package/src/persistence/__tests__/lifecycle-quiesce.test.ts +82 -0
- package/src/persistence/__tests__/message-content-file.test.ts +58 -3
- package/src/persistence/conversation-crud.ts +93 -0
- package/src/persistence/conversation-key-store.ts +63 -0
- package/src/persistence/conversation-parent.ts +27 -0
- package/src/persistence/db-async-query.ts +1 -1
- package/src/persistence/db-singleton.ts +14 -27
- package/src/persistence/delivery-crud.ts +45 -0
- package/src/persistence/embeddings/embedding-backend.ts +2 -1
- package/src/persistence/embeddings/embedding-identity.ts +7 -3
- package/src/persistence/job-utils.ts +32 -27
- package/src/persistence/jobs-store.ts +92 -3
- package/src/persistence/lifecycle-quiesce.ts +80 -0
- package/src/persistence/llm-usage-store.ts +45 -8
- package/src/persistence/message-content-file.ts +4 -4
- package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +1 -1
- package/src/persistence/migrations/351-schedule-skill-script-handoff.ts +39 -0
- package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.test.ts +83 -0
- package/src/persistence/migrations/352-drop-schedule-skill-script-handoff.ts +36 -0
- package/src/persistence/migrations/353-add-llm-usage-conversation-type.test.ts +110 -0
- package/src/persistence/migrations/353-add-llm-usage-conversation-type.ts +49 -0
- package/src/persistence/migrations/354-backfill-app-conversation-lineage.ts +41 -0
- package/src/persistence/schema/infrastructure.ts +11 -0
- package/src/persistence/steps.ts +9 -1
- package/src/plugin-api/__tests__/publish-event-forwarding.test.ts +113 -0
- package/src/plugin-api/__tests__/publish-event.test.ts +57 -0
- package/src/plugin-api/conversation-turn.ts +4 -4
- package/src/plugin-api/event-hub-facade.ts +15 -6
- package/src/plugin-api/index.ts +10 -4
- package/src/plugin-api/publish-event.ts +36 -0
- package/src/plugins/AGENTS.md +2 -0
- package/src/plugins/defaults/compaction/window-manager.ts +9 -0
- package/src/plugins/defaults/index.ts +9 -13
- package/src/plugins/defaults/memory/AGENTS.md +775 -0
- package/src/plugins/defaults/memory/__tests__/capability-seed-embed-gate.test.ts +186 -0
- package/src/plugins/defaults/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/indexer-tier-dispatch.test.ts +129 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-quiesce-gate.test.ts +91 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v1-graph-substrate-noop.test.ts +342 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +9 -1
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +36 -1
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-sweep-defer.test.ts +9 -1
- package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +44 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +151 -10
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1155 -0
- package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -3
- package/src/plugins/defaults/memory/__tests__/tools-delete-memory-page.test.ts +1 -1
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +71 -35
- package/src/plugins/defaults/memory/context-search/sources/memory.ts +5 -1
- package/src/plugins/defaults/memory/fork-conversation-memory.ts +4 -4
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +80 -10
- package/src/plugins/defaults/memory/graph/__tests__/handle-remember-v2.test.ts +1 -1
- package/src/plugins/defaults/memory/graph/capability-seed.ts +24 -10
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +123 -49
- package/src/plugins/defaults/memory/graph/graph-search.ts +6 -118
- package/src/plugins/defaults/memory/graph/in-context-tracker.test.ts +173 -0
- package/src/plugins/defaults/memory/graph/in-context-tracker.ts +109 -0
- package/src/plugins/defaults/memory/graph/store.ts +1 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.test.ts +1 -1
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +5 -2
- package/src/plugins/defaults/memory/graph-topology/__tests__/build-memory-graph.test.ts +1 -1
- package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +6 -9
- package/src/plugins/defaults/memory/hooks/__tests__/user-prompt-submit.test.ts +30 -16
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +18 -14
- package/src/plugins/defaults/memory/indexer.ts +146 -89
- package/src/plugins/defaults/memory/injectors.ts +86 -41
- package/src/plugins/defaults/memory/job-handlers.ts +130 -39
- package/src/plugins/defaults/memory/jobs/__tests__/embed-concept-page.test.ts +2 -2
- package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +22 -10
- package/src/plugins/defaults/memory/jobs-worker.ts +279 -208
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +4 -2
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +40 -3
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +99 -11
- package/src/plugins/defaults/memory/src/__tests__/memory-v2-routes.test.ts +5 -3
- package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +3 -4
- package/src/plugins/defaults/memory/src/memory-graph-routes.ts +2 -1
- package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +7 -5
- package/src/plugins/defaults/memory/src/memory-item-routes.ts +51 -19
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +31 -20
- package/src/plugins/defaults/memory/startup.ts +58 -21
- package/src/plugins/defaults/memory/substrate/__tests__/boot-maintenance.test.ts +335 -0
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/cli-command-store.test.ts +14 -8
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-job.test.ts +51 -21
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/edge-index.test.ts +1 -1
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/frontmatter-sweep.test.ts +3 -3
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-index.test.ts +1 -1
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/page-store.test.ts +1 -1
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/prompts-consolidation.test.ts +2 -2
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/qdrant.test.ts +23 -14
- package/src/plugins/defaults/memory/substrate/__tests__/reembed-job.test.ts +262 -0
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sim.test.ts +9 -12
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-content.test.ts +2 -2
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/skill-store.test.ts +9 -9
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sparse-bm25.test.ts +12 -5
- package/src/plugins/defaults/memory/substrate/__tests__/spread.test.ts +276 -0
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/static-context.test.ts +17 -8
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/sweep-job.test.ts +9 -9
- package/src/plugins/defaults/memory/{v3/substrate/memory-v2-startup.ts → substrate/boot-maintenance.ts} +43 -24
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-store.ts +13 -8
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/consolidation-job.ts +51 -27
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/constants.ts +1 -0
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/edge-index.ts +28 -9
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/frontmatter-sweep.ts +14 -7
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/injected-block-slugs.ts +9 -3
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-index.ts +29 -10
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/page-store.ts +35 -12
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/consolidation.ts +14 -8
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/prompts/sweep.ts +4 -1
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/qdrant.ts +96 -36
- package/src/plugins/defaults/memory/substrate/reembed-job.ts +54 -0
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/sim.ts +26 -15
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-content.ts +1 -1
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/skill-store.ts +13 -8
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/sparse-bm25.ts +27 -10
- package/src/plugins/defaults/memory/substrate/spread.ts +165 -0
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/static-context.ts +12 -7
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/sweep-job.ts +20 -15
- package/src/plugins/defaults/memory/substrate/tuning.ts +93 -0
- package/src/plugins/defaults/memory/{v3/substrate → substrate}/types.ts +7 -4
- package/src/plugins/defaults/memory/tools.test.ts +1 -1
- package/src/plugins/defaults/memory/tools.ts +1 -1
- package/src/plugins/defaults/memory/v1/README.md +5 -0
- package/src/plugins/defaults/memory/{__tests__ → v1/__tests__}/filing-jobs.test.ts +4 -4
- package/src/plugins/defaults/memory/{filing-jobs.ts → v1/filing-jobs.ts} +3 -3
- package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.test.ts +68 -6
- package/src/plugins/defaults/memory/{graph → v1/graph}/bootstrap.ts +40 -21
- package/src/plugins/defaults/memory/{graph → v1/graph}/consolidation.ts +14 -14
- package/src/plugins/defaults/memory/{graph → v1/graph}/decay.test.ts +1 -1
- package/src/plugins/defaults/memory/{graph → v1/graph}/decay.ts +3 -3
- package/src/plugins/defaults/memory/{graph → v1/graph}/extraction-job.ts +5 -5
- package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.test.ts +1 -1
- package/src/plugins/defaults/memory/{graph → v1/graph}/extraction.ts +20 -14
- package/src/plugins/defaults/memory/{graph → v1/graph}/graph-search.test.ts +3 -3
- package/src/plugins/defaults/memory/v1/graph/graph-search.ts +123 -0
- package/src/plugins/defaults/memory/{graph → v1/graph}/injection.test.ts +1 -118
- package/src/plugins/defaults/memory/{graph → v1/graph}/injection.ts +9 -111
- package/src/plugins/defaults/memory/{graph → v1/graph}/narrative.ts +6 -6
- package/src/plugins/defaults/memory/{graph → v1/graph}/pattern-scan.ts +5 -5
- package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.test.ts +135 -42
- package/src/plugins/defaults/memory/{graph → v1/graph}/retriever.ts +40 -22
- package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.test.ts +1 -1
- package/src/plugins/defaults/memory/{graph → v1/graph}/scoring.ts +1 -1
- package/src/plugins/defaults/memory/{graph → v1/graph}/serendipity.ts +1 -1
- package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.test.ts +1 -1
- package/src/plugins/defaults/memory/{graph → v1/graph}/triggers.ts +1 -1
- package/src/plugins/defaults/memory/{identity-context.ts → v1/identity-context.ts} +4 -4
- package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/backfill.ts +6 -6
- package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.test.ts +6 -6
- package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/embedding.ts +6 -6
- package/src/plugins/defaults/memory/{job-handlers → v1/job-handlers}/index-maintenance.ts +8 -8
- package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.test.ts +7 -7
- package/src/plugins/defaults/memory/{jobs → v1/jobs}/embed-pkb-file.ts +5 -5
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/autoinject.ts +1 -1
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/context.ts +2 -2
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.test.ts +3 -3
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-index.ts +2 -2
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.test.ts +3 -3
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-reconcile.ts +2 -2
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.test.ts +3 -3
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/pkb-search.ts +6 -6
- package/src/plugins/defaults/memory/{pkb → v1/pkb}/types.ts +3 -5
- package/src/plugins/defaults/memory/{pkb-schedule.ts → v1/pkb-schedule.ts} +2 -2
- package/src/plugins/defaults/memory/{search/semantic.ts → v1/semantic-search.ts} +1 -1
- package/src/plugins/defaults/memory/v2/__tests__/activation-store.test.ts +1 -1
- package/src/plugins/defaults/memory/v2/__tests__/activation.test.ts +11 -274
- package/src/plugins/defaults/memory/v2/__tests__/backfill-jobs.test.ts +9 -131
- package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +1 -1
- package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +1 -1
- package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +1 -1
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +14 -10
- package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +7 -5
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +12 -6
- package/src/plugins/defaults/memory/{memory-v2-activation-log-store.ts → v2/activation-log-store.ts} +2 -2
- package/src/plugins/defaults/memory/v2/activation-store.ts +22 -8
- package/src/plugins/defaults/memory/v2/activation.ts +30 -163
- package/src/plugins/defaults/memory/v2/backfill-jobs.ts +7 -54
- package/src/plugins/defaults/memory/{memory-v2-concept-frequency.ts → v2/concept-frequency.ts} +3 -3
- package/src/plugins/defaults/memory/v2/harness/oracle.ts +1 -1
- package/src/plugins/defaults/memory/v2/harness/replay-input.ts +2 -2
- package/src/plugins/defaults/memory/v2/harness/retriever.ts +1 -1
- package/src/plugins/defaults/memory/v2/injection.ts +36 -26
- package/src/plugins/defaults/memory/v2/migration.ts +2 -2
- package/src/plugins/defaults/memory/{rerank-local.ts → v2/rerank-local.ts} +44 -18
- package/src/plugins/defaults/memory/v2/reranker.ts +29 -11
- package/src/plugins/defaults/memory/v2/router.ts +38 -15
- package/src/plugins/defaults/memory/v3/__tests__/candidate-match.test.ts +5 -3
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +21 -8
- package/src/plugins/defaults/memory/v3/__tests__/edge.test.ts +1 -1
- package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +1 -1
- package/src/plugins/defaults/memory/v3/__tests__/maintain-job.test.ts +4 -1
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +181 -3
- package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +3 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +14 -5
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +40 -50
- package/src/plugins/defaults/memory/v3/__tests__/tuning-profile.test.ts +1 -0
- package/src/plugins/defaults/memory/v3/candidate-match.ts +2 -2
- package/src/plugins/defaults/memory/v3/capabilities.ts +6 -4
- package/src/plugins/defaults/memory/v3/card.ts +25 -9
- package/src/plugins/defaults/memory/v3/edge.ts +52 -18
- package/src/plugins/defaults/memory/v3/gate.ts +4 -4
- package/src/plugins/defaults/memory/v3/injector.ts +10 -3
- package/src/plugins/defaults/memory/v3/maintain-job.ts +3 -3
- package/src/plugins/defaults/memory/v3/orchestrate.ts +100 -18
- package/src/plugins/defaults/memory/v3/page-content.ts +26 -10
- package/src/plugins/defaults/memory/v3/prune.ts +40 -16
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +34 -12
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +9 -13
- package/src/plugins/defaults/memory/v3/span-query.test.ts +86 -0
- package/src/plugins/defaults/memory/v3/span-query.ts +71 -0
- package/src/plugins/defaults/memory/v3/tuning-profile.ts +3 -0
- package/src/plugins/defaults/memory/v3/types.ts +4 -1
- package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +2 -2
- package/src/plugins/defaults/memory/validation.ts +1 -1
- package/src/plugins/defaults/memory/worker.ts +2 -1
- package/src/plugins/defaults/platform-hosted/__tests__/reengage.test.ts +104 -12
- package/src/plugins/defaults/platform-hosted/routes/reengage.ts +104 -12
- package/src/plugins/types.ts +2 -2
- package/src/prompts/sections.ts +4 -16
- package/src/providers/__tests__/registry-native-web-search.test.ts +47 -2
- package/src/providers/anthropic/client.ts +15 -6
- package/src/providers/content-block-schema.ts +26 -0
- package/src/providers/inference/__tests__/connections-credential-normalize.test.ts +87 -0
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/inference/connections.ts +30 -14
- package/src/providers/model-catalog.ts +77 -0
- package/src/providers/openai/__tests__/chat-completions-input-audio.test.ts +260 -0
- package/src/providers/openai/chat-completions-provider.ts +65 -17
- package/src/providers/openai/input-audio.ts +76 -0
- package/src/providers/openrouter/client.ts +0 -2
- package/src/providers/platform-proxy/constants.ts +4 -0
- package/src/providers/poolside/client.ts +42 -0
- package/src/providers/registry.ts +40 -8
- package/src/providers/retry.ts +2 -0
- package/src/providers/types.ts +39 -1
- package/src/providers/vercel-ai-gateway/client.ts +0 -2
- package/src/runtime/AGENTS.md +7 -5
- package/src/runtime/__tests__/question-request-guardian-bridge.test.ts +173 -0
- package/src/runtime/__tests__/question-resolution.test.ts +111 -0
- package/src/runtime/access-request-helper.ts +7 -15
- package/src/runtime/assistant-event-hub.ts +43 -31
- package/src/runtime/assistant-event-publish-options.ts +31 -0
- package/src/runtime/assistant-event.ts +19 -30
- package/src/runtime/assistant-stream-state.ts +4 -4
- package/src/runtime/channel-approvals.ts +0 -25
- package/src/runtime/channel-readiness-service.ts +99 -40
- package/src/runtime/channel-retry-sweep.ts +38 -15
- package/src/runtime/confirmation-request-guardian-bridge.ts +4 -0
- package/src/runtime/guardian-reply-router.ts +108 -0
- package/src/runtime/http-server.ts +8 -0
- package/src/runtime/http-types.ts +2 -2
- package/src/runtime/process-role.ts +42 -0
- package/src/runtime/question-request-guardian-bridge.ts +184 -0
- package/src/runtime/question-resolution.ts +153 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/subagents-routes.test.ts +250 -0
- package/src/runtime/routes/__tests__/webhook-routes.test.ts +201 -0
- package/src/runtime/routes/acp-routes.ts +2 -2
- package/src/runtime/routes/bookmark-routes.ts +3 -15
- package/src/runtime/routes/canned-message-complete.ts +2 -2
- package/src/runtime/routes/consolidation-routes.ts +8 -2
- package/src/runtime/routes/conversation-list-routes.ts +3 -19
- package/src/runtime/routes/conversation-management-routes.ts +1 -5
- package/src/runtime/routes/conversation-query-routes.ts +52 -8
- package/src/runtime/routes/conversation-routes.ts +36 -22
- package/src/runtime/routes/credential-request-routes.ts +0 -1
- package/src/runtime/routes/filing-routes.ts +7 -7
- package/src/runtime/routes/global-search-routes.ts +1 -1
- package/src/runtime/routes/guardian-approval-prompt.ts +2 -2
- package/src/runtime/routes/image-generation-routes.ts +12 -9
- package/src/runtime/routes/inbound-message-handler.ts +17 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +50 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +19 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -4
- package/src/runtime/routes/inbound-stages/inbound-content-prep.test.ts +129 -0
- package/src/runtime/routes/inbound-stages/inbound-content-prep.ts +141 -9
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/lifecycle-routes.ts +199 -0
- package/src/runtime/routes/platform-routes.ts +3 -8
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +7 -7
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +5 -5
- package/src/runtime/routes/question-routes.ts +34 -87
- package/src/runtime/routes/secret-routes.ts +11 -1
- package/src/runtime/routes/settings-routes.ts +8 -18
- package/src/runtime/routes/subagents-routes.ts +35 -8
- package/src/runtime/routes/upgrade-broadcast-routes.ts +4 -5
- package/src/runtime/routes/webhook-routes.ts +89 -30
- package/src/runtime/routes/workspace-routes.test.ts +7 -4
- package/src/runtime/slack-reply-session.test.ts +12 -12
- package/src/runtime/slack-reply-session.ts +17 -17
- package/src/runtime/sync/sync-publisher.test.ts +7 -5
- package/src/runtime/tool-grant-request-helper.ts +4 -0
- package/src/schedule/schedule-store.ts +70 -0
- package/src/schedule/scheduler.ts +27 -0
- package/src/security/AGENTS.md +1 -1
- package/src/security/credential-key.ts +17 -0
- package/src/security/secret-ingress.ts +14 -105
- package/src/security/secret-patterns.ts +8 -139
- package/src/security/secret-scanner.ts +8 -3
- package/src/security/store-path-override.ts +6 -16
- package/src/sequence/store.ts +8 -0
- package/src/signals/emit-event.ts +5 -10
- package/src/signals/event-stream.ts +20 -10
- package/src/subagent/manager.ts +76 -25
- package/src/subagent/notify.ts +8 -0
- package/src/subagent/types.ts +13 -3
- package/src/telegram/__tests__/webhook-health.test.ts +442 -0
- package/src/telegram/webhook-health.ts +478 -0
- package/src/telemetry/AGENTS.md +6 -0
- package/src/telemetry/memory-corpus-size.test.ts +129 -0
- package/src/telemetry/memory-corpus-size.ts +184 -0
- package/src/telemetry/memory-tier-reporter.test.ts +67 -2
- package/src/telemetry/memory-tier-reporter.ts +30 -3
- package/src/tools/__tests__/conversation-group-tool-input-schemas.test.ts +61 -0
- package/src/tools/__tests__/document-tool-input-schemas.test.ts +103 -0
- package/src/tools/__tests__/followup-call-acp-tool-input-schemas.test.ts +125 -0
- package/src/tools/__tests__/schedule-tool-input-schemas.test.ts +83 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +116 -0
- package/src/tools/__tests__/tool-input-schemas.test.ts +192 -0
- package/src/tools/__tests__/tool-schema-drift-harness.ts +142 -0
- package/src/tools/acp/abort.ts +21 -1
- package/src/tools/acp/context.ts +5 -5
- package/src/tools/acp/spawn.test.ts +15 -5
- package/src/tools/acp/spawn.ts +26 -3
- package/src/tools/acp/status.ts +20 -1
- package/src/tools/acp/steer.test.ts +20 -0
- package/src/tools/acp/steer.ts +23 -2
- package/src/tools/apps/resolve-app-id.ts +15 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +65 -0
- package/src/tools/ask-question/ask-question-tool.ts +68 -92
- package/src/tools/browser/browser-screencast.ts +6 -6
- package/src/tools/calls/call-end.ts +24 -3
- package/src/tools/calls/call-start.ts +35 -11
- package/src/tools/calls/call-status.ts +20 -1
- package/src/tools/conversation-groups/group_create.ts +63 -0
- package/src/tools/conversation-groups/group_list.ts +20 -0
- package/src/tools/conversation-groups/group_shared.ts +52 -0
- package/src/tools/conversation-groups/move_to_group.ts +106 -0
- package/src/tools/document/document-comment-tool.test.ts +49 -0
- package/src/tools/document/document-comment-tool.ts +45 -6
- package/src/tools/document/document-tool.ts +142 -24
- package/src/tools/executor.ts +27 -13
- package/src/tools/filesystem/edit.ts +62 -60
- package/src/tools/filesystem/list.ts +36 -29
- package/src/tools/filesystem/read.ts +56 -39
- package/src/tools/filesystem/write.ts +44 -38
- package/src/tools/followups/followup_create.ts +41 -12
- package/src/tools/followups/followup_list.ts +34 -6
- package/src/tools/followups/followup_resolve.ts +26 -4
- package/src/tools/host-filesystem/edit.ts +50 -60
- package/src/tools/host-filesystem/read.ts +45 -39
- package/src/tools/host-filesystem/write.ts +31 -36
- package/src/tools/host-terminal/host-shell.ts +76 -62
- package/src/tools/policy-context.ts +27 -10
- package/src/tools/schedule/create.ts +76 -33
- package/src/tools/schedule/delete.ts +21 -2
- package/src/tools/schedule/list.ts +25 -3
- package/src/tools/schedule/update.ts +105 -48
- package/src/tools/shared/filesystem/path-policy.ts +246 -40
- package/src/tools/shared/zod-tool-schema.ts +117 -0
- package/src/tools/subagent/abort.ts +12 -4
- package/src/tools/subagent/consult-deadline.ts +9 -3
- package/src/tools/subagent/message.ts +21 -5
- package/src/tools/subagent/notify-parent.ts +44 -23
- package/src/tools/subagent/read.ts +15 -4
- package/src/tools/subagent/resolve.ts +26 -4
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/subagent/status.ts +12 -4
- package/src/tools/system/request-permission.ts +35 -26
- package/src/tools/terminal/shell.ts +68 -55
- package/src/tools/tool-approval-handler.ts +327 -53
- package/src/tools/tool-input-schemas.ts +85 -0
- package/src/tools/tool-types.ts +7 -0
- package/src/tools/types.ts +8 -0
- package/src/tools/workflows/manage-workflows.ts +30 -2
- package/src/tools/workflows/run-workflow.test.ts +54 -2
- package/src/tools/workflows/run-workflow.ts +32 -4
- package/src/util/fs-symlinks.ts +30 -0
- package/src/util/log-redact.ts +7 -3
- package/src/util/platform.ts +15 -0
- package/src/vellum-assistant-namespace.d.ts +80 -0
- package/src/watcher/watcher-store.ts +8 -0
- package/src/workflows/leaf-runner.test.ts +104 -2
- package/src/workflows/leaf-runner.ts +31 -6
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +2 -2
- package/src/workspace/migrations/134-image-generation-mode-to-provider.ts +128 -0
- package/src/workspace/migrations/135-copy-substrate-tunables.ts +248 -0
- package/src/workspace/migrations/__tests__/135-copy-substrate-tunables.test.ts +446 -0
- package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +2 -2
- package/src/workspace/migrations/registry.ts +4 -0
- package/tsconfig.plugin-api.json +1 -1
- package/src/__tests__/memory-retrieval-budget.test.ts +0 -49
- package/src/__tests__/secret-patterns.test.ts +0 -27
- package/src/config/__tests__/proc-to-skills.test.ts +0 -21
- package/src/config/memory-tier.test.ts +0 -44
- package/src/daemon/message-types/acp.ts +0 -20
- package/src/daemon/message-types/apps.ts +0 -11
- package/src/daemon/message-types/background-tools.ts +0 -14
- package/src/daemon/message-types/bookmarks.ts +0 -13
- package/src/daemon/message-types/contacts.ts +0 -13
- package/src/daemon/message-types/document-comments.ts +0 -14
- package/src/daemon/message-types/documents.ts +0 -13
- package/src/daemon/message-types/home.ts +0 -15
- package/src/daemon/message-types/host-bash.ts +0 -12
- package/src/daemon/message-types/host-cu.ts +0 -10
- package/src/daemon/message-types/host-file.ts +0 -12
- package/src/daemon/message-types/host-transfer.ts +0 -12
- package/src/daemon/message-types/integrations.ts +0 -90
- package/src/daemon/message-types/memory.ts +0 -10
- package/src/daemon/message-types/schedules.ts +0 -14
- package/src/daemon/message-types/settings.ts +0 -17
- package/src/daemon/message-types/subagents.ts +0 -63
- package/src/daemon/message-types/upgrades.ts +0 -14
- package/src/daemon/message-types/workflows.ts +0 -25
- package/src/daemon/message-types/workspace.ts +0 -137
- package/src/live-voice/ack-phrases.ts +0 -58
- package/src/plugins/defaults/memory/retrieval-budget.ts +0 -29
- package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +0 -69
- package/src/plugins/defaults/memory/v3/gate-flag.ts +0 -20
- package/src/plugins/defaults/memory/v3/substrate/__tests__/memory-v2-startup.test.ts +0 -86
- package/src/plugins/defaults/memory/v3/substrate/memory-v2-startup.test.ts +0 -205
- /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/concept-page-frontmatter-schema.test.ts +0 -0
- /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/consolidation-prompt-flag-gating-guard.test.ts +0 -0
- /package/src/plugins/defaults/memory/{v3/substrate → substrate}/__tests__/injected-block-slugs.test.ts +0 -0
- /package/src/plugins/defaults/memory/{v3/substrate → substrate}/cli-command-content.ts +0 -0
|
@@ -8,8 +8,7 @@ import { z } from "zod";
|
|
|
8
8
|
import type { ServiceGroupUpdateCompleteEvent } from "../../api/events/service-group-update-complete.js";
|
|
9
9
|
import type { ServiceGroupUpdateProgressEvent } from "../../api/events/service-group-update-progress.js";
|
|
10
10
|
import type { ServiceGroupUpdateStartingEvent } from "../../api/events/service-group-update-starting.js";
|
|
11
|
-
import {
|
|
12
|
-
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
11
|
+
import { broadcastMessage } from "../assistant-event-hub.js";
|
|
13
12
|
import { GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
|
|
14
13
|
import { BadRequestError } from "./errors.js";
|
|
15
14
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
@@ -48,7 +47,7 @@ async function handleUpgradeBroadcast({ body }: RouteHandlerArgs) {
|
|
|
48
47
|
expectedDowntimeSeconds: downtime,
|
|
49
48
|
};
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
broadcastMessage(message);
|
|
52
51
|
|
|
53
52
|
return { ok: true };
|
|
54
53
|
}
|
|
@@ -67,7 +66,7 @@ async function handleUpgradeBroadcast({ body }: RouteHandlerArgs) {
|
|
|
67
66
|
statusMessage,
|
|
68
67
|
};
|
|
69
68
|
|
|
70
|
-
|
|
69
|
+
broadcastMessage(message);
|
|
71
70
|
|
|
72
71
|
return { ok: true };
|
|
73
72
|
}
|
|
@@ -108,7 +107,7 @@ async function handleUpgradeBroadcast({ body }: RouteHandlerArgs) {
|
|
|
108
107
|
: {}),
|
|
109
108
|
};
|
|
110
109
|
|
|
111
|
-
|
|
110
|
+
broadcastMessage(message);
|
|
112
111
|
|
|
113
112
|
return { ok: true };
|
|
114
113
|
}
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Serves two operations:
|
|
5
5
|
* - webhooks_register (POST webhooks/register): Resolve a stable callback URL
|
|
6
|
-
* for a webhook type
|
|
7
|
-
*
|
|
8
|
-
* on self-hosted assistants.
|
|
6
|
+
* for a webhook type. See `handleWebhooksRegister` for the resolution
|
|
7
|
+
* order.
|
|
9
8
|
* - webhooks_list (GET webhooks): List all webhook callback routes registered
|
|
10
9
|
* with the platform for this assistant.
|
|
11
10
|
*/
|
|
@@ -18,7 +17,10 @@ import {
|
|
|
18
17
|
registerCallbackRoute,
|
|
19
18
|
resolvePlatformCallbackRegistrationContext,
|
|
20
19
|
} from "../../inbound/platform-callback-registration.js";
|
|
21
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
getPublicBaseUrl,
|
|
22
|
+
type IngressConfig,
|
|
23
|
+
} from "../../inbound/public-ingress-urls.js";
|
|
22
24
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
23
25
|
import {
|
|
24
26
|
BadRequestError,
|
|
@@ -77,10 +79,66 @@ function deriveWebhookPath(type: string): string {
|
|
|
77
79
|
return `webhooks/${type.replace(/_/g, "/")}`;
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Register `webhookPath` with the platform gateway, mapping failures onto the
|
|
84
|
+
* route error vocabulary.
|
|
85
|
+
*/
|
|
86
|
+
async function registerWithPlatform(
|
|
87
|
+
webhookPath: string,
|
|
88
|
+
type: string,
|
|
89
|
+
source: string | undefined,
|
|
90
|
+
): Promise<WebhooksRegisterResponse> {
|
|
91
|
+
let callbackUrl: string;
|
|
92
|
+
try {
|
|
93
|
+
callbackUrl = await registerCallbackRoute(webhookPath, type, source);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
const msg = (err as Error).message;
|
|
96
|
+
if (msg.includes("missing platform registration context")) {
|
|
97
|
+
throw new UnprocessableEntityError(msg);
|
|
98
|
+
}
|
|
99
|
+
throw new InternalError(`Failed to register callback route: ${msg}`);
|
|
100
|
+
}
|
|
101
|
+
return { callbackUrl, type, path: webhookPath, mode: "platform" };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* True when the user has explicitly switched public ingress off.
|
|
106
|
+
*
|
|
107
|
+
* An explicit opt-out is a decision not to accept inbound webhooks at all, so
|
|
108
|
+
* it must not be silently routed around via platform callbacks. An *absent*
|
|
109
|
+
* ingress config is merely "not set up yet" and is eligible for the platform
|
|
110
|
+
* fallback.
|
|
111
|
+
*/
|
|
112
|
+
function isPublicIngressDisabled(config: IngressConfig): boolean {
|
|
113
|
+
return config.ingress?.enabled === false;
|
|
114
|
+
}
|
|
115
|
+
|
|
80
116
|
// ---------------------------------------------------------------------------
|
|
81
117
|
// Handlers
|
|
82
118
|
// ---------------------------------------------------------------------------
|
|
83
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Resolve a stable callback URL for a webhook type.
|
|
122
|
+
*
|
|
123
|
+
* Resolution order:
|
|
124
|
+
* 1. **Platform pods** (`IS_PLATFORM`) always register with the platform
|
|
125
|
+
* gateway: they have no ingress of their own to advertise.
|
|
126
|
+
* 2. **A configured public ingress wins** for everyone else. That URL is
|
|
127
|
+
* either the user's own tunnel (ngrok, a custom domain) or the Velay
|
|
128
|
+
* tunnel URL the gateway publishes into `ingress.publicBaseUrl`, so a
|
|
129
|
+
* platform-connected local assistant with a live tunnel already resolves
|
|
130
|
+
* to a stable platform-owned URL here.
|
|
131
|
+
* 3. **Platform-connected assistants with no ingress** register with the
|
|
132
|
+
* platform gateway rather than failing. Connectivity is decided by
|
|
133
|
+
* credentials (platform base URL + assistant ID + assistant API key), not
|
|
134
|
+
* by `IS_PLATFORM`, which is only ever true on a platform pod.
|
|
135
|
+
*
|
|
136
|
+
* Ingress deliberately precedes platform registration: any logged-in local
|
|
137
|
+
* assistant holds platform credentials (it needs them for the LLM proxy), so
|
|
138
|
+
* treating credential presence as "managed" would silently reroute an
|
|
139
|
+
* explicitly configured self-hosted webhook through the platform. The gateway's
|
|
140
|
+
* Telegram and email registrars order the same two tiers the same way.
|
|
141
|
+
*/
|
|
84
142
|
async function handleWebhooksRegister(
|
|
85
143
|
args: RouteHandlerArgs,
|
|
86
144
|
): Promise<WebhooksRegisterResponse> {
|
|
@@ -92,39 +150,40 @@ async function handleWebhooksRegister(
|
|
|
92
150
|
|
|
93
151
|
const webhookPath =
|
|
94
152
|
(pathOverride as string | undefined) ?? deriveWebhookPath(type as string);
|
|
153
|
+
const sourceIdentifier = source as string | undefined;
|
|
95
154
|
|
|
96
155
|
if (getIsPlatform()) {
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
callbackUrl = await registerCallbackRoute(
|
|
100
|
-
webhookPath,
|
|
101
|
-
type as string,
|
|
102
|
-
source as string | undefined,
|
|
103
|
-
);
|
|
104
|
-
} catch (err) {
|
|
105
|
-
const msg = (err as Error).message;
|
|
106
|
-
if (msg.includes("missing platform registration context")) {
|
|
107
|
-
throw new UnprocessableEntityError(msg);
|
|
108
|
-
}
|
|
109
|
-
throw new InternalError(`Failed to register callback route: ${msg}`);
|
|
110
|
-
}
|
|
111
|
-
return { callbackUrl, type, path: webhookPath, mode: "platform" };
|
|
156
|
+
return registerWithPlatform(webhookPath, type, sourceIdentifier);
|
|
112
157
|
}
|
|
113
158
|
|
|
114
|
-
// Self-hosted: use ingress.publicBaseUrl
|
|
115
159
|
const config = getConfig();
|
|
116
|
-
|
|
160
|
+
if (isPublicIngressDisabled(config)) {
|
|
161
|
+
throw new UnprocessableEntityError(
|
|
162
|
+
"Public ingress is disabled. Ask the assistant to enable it, or update it from the Settings page.",
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let ingressError: Error;
|
|
117
167
|
try {
|
|
118
|
-
baseUrl = getPublicBaseUrl(config);
|
|
168
|
+
const baseUrl = getPublicBaseUrl(config);
|
|
169
|
+
return {
|
|
170
|
+
callbackUrl: `${baseUrl}/${webhookPath}`,
|
|
171
|
+
type,
|
|
172
|
+
path: webhookPath,
|
|
173
|
+
mode: "self-hosted",
|
|
174
|
+
};
|
|
119
175
|
} catch (err) {
|
|
120
|
-
|
|
176
|
+
ingressError = err as Error;
|
|
121
177
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
178
|
+
|
|
179
|
+
// No ingress configured. Fall back to the platform gateway when this
|
|
180
|
+
// assistant is connected to the platform.
|
|
181
|
+
const context = await resolvePlatformCallbackRegistrationContext();
|
|
182
|
+
if (context.enabled) {
|
|
183
|
+
return registerWithPlatform(webhookPath, type, sourceIdentifier);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
throw new UnprocessableEntityError(ingressError.message);
|
|
128
187
|
}
|
|
129
188
|
|
|
130
189
|
async function handleWebhooksList(
|
|
@@ -189,7 +248,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
189
248
|
},
|
|
190
249
|
summary: "Register a webhook callback URL",
|
|
191
250
|
description:
|
|
192
|
-
"Resolves a stable callback URL for a webhook type. On platform-managed assistants, registers the route with the platform gateway.
|
|
251
|
+
"Resolves a stable callback URL for a webhook type. On platform-managed assistants, registers the route with the platform gateway. Otherwise uses the configured ingress.publicBaseUrl, falling back to the platform gateway when no ingress is configured and the assistant is connected to the platform.",
|
|
193
252
|
tags: ["webhooks"],
|
|
194
253
|
requestBody: WebhooksRegisterRequestSchema,
|
|
195
254
|
responseBody: WebhooksRegisterResponseSchema,
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
import { beforeAll, describe, expect, test } from "bun:test";
|
|
19
19
|
|
|
20
|
+
import type { AssistantEventEnvelope } from "../../api/index.js";
|
|
20
21
|
import { SYNC_TAGS } from "../../daemon/message-types/sync.js";
|
|
21
|
-
import type { AssistantEvent } from "../assistant-event.js";
|
|
22
22
|
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
23
23
|
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
@@ -66,15 +66,18 @@ beforeAll(() => {
|
|
|
66
66
|
|
|
67
67
|
function getRoute(operationId: string): RouteDefinition {
|
|
68
68
|
const route = ROUTES.find((r) => r.operationId === operationId);
|
|
69
|
-
if (!route)
|
|
69
|
+
if (!route) {
|
|
70
70
|
throw new Error(`No shared route found for operationId: ${operationId}`);
|
|
71
|
+
}
|
|
71
72
|
return route;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
async function waitFor(predicate: () => boolean): Promise<void> {
|
|
75
76
|
const deadline = Date.now() + 500;
|
|
76
77
|
while (Date.now() < deadline) {
|
|
77
|
-
if (predicate())
|
|
78
|
+
if (predicate()) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
78
81
|
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
79
82
|
}
|
|
80
83
|
throw new Error("Timed out waiting for workspace route event");
|
|
@@ -557,7 +560,7 @@ describe("POST /v1/workspace/write", () => {
|
|
|
557
560
|
});
|
|
558
561
|
|
|
559
562
|
test("publishes sounds sync events when writing sounds config", async () => {
|
|
560
|
-
const received:
|
|
563
|
+
const received: AssistantEventEnvelope[] = [];
|
|
561
564
|
const subscription = assistantEventHub.subscribe({
|
|
562
565
|
type: "process",
|
|
563
566
|
callback: (event) => {
|
|
@@ -28,7 +28,7 @@ mock.module("./gateway-client.js", () => ({
|
|
|
28
28
|
},
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
|
-
import type {
|
|
31
|
+
import type { AssistantEvent } from "../api/index.js";
|
|
32
32
|
import { SLACK_STREAM_MARKDOWN_LIMIT } from "../messaging/providers/slack/api.js";
|
|
33
33
|
import {
|
|
34
34
|
createSlackReplySession,
|
|
@@ -39,34 +39,34 @@ const CHANNEL = "D-STREAM";
|
|
|
39
39
|
const THREAD_TS = "1700000000.000001";
|
|
40
40
|
const CALLBACK_URL = `https://example.test/deliver/slack?channel=${CHANNEL}&threadTs=${THREAD_TS}`;
|
|
41
41
|
|
|
42
|
-
const textDelta = (text: string):
|
|
42
|
+
const textDelta = (text: string): AssistantEvent =>
|
|
43
43
|
({
|
|
44
44
|
type: "assistant_text_delta",
|
|
45
45
|
text,
|
|
46
46
|
conversationId: "conv-stream",
|
|
47
|
-
}) as
|
|
47
|
+
}) as AssistantEvent;
|
|
48
48
|
|
|
49
|
-
const toolUseStart = (toolUseId: string):
|
|
49
|
+
const toolUseStart = (toolUseId: string): AssistantEvent =>
|
|
50
50
|
({
|
|
51
51
|
type: "tool_use_start",
|
|
52
52
|
toolName: "web_search",
|
|
53
53
|
input: { query: "example" },
|
|
54
54
|
conversationId: "conv-stream",
|
|
55
55
|
toolUseId,
|
|
56
|
-
}) as
|
|
56
|
+
}) as AssistantEvent;
|
|
57
57
|
|
|
58
|
-
const messageComplete = (messageId: string):
|
|
58
|
+
const messageComplete = (messageId: string): AssistantEvent =>
|
|
59
59
|
({
|
|
60
60
|
type: "message_complete",
|
|
61
61
|
conversationId: "conv-stream",
|
|
62
62
|
messageId,
|
|
63
|
-
}) as
|
|
63
|
+
}) as AssistantEvent;
|
|
64
64
|
|
|
65
65
|
const taskProgressShow = (
|
|
66
66
|
surfaceId: string,
|
|
67
67
|
steps: Array<{ label: string; status: string; detail?: string }>,
|
|
68
68
|
templateTitle?: string,
|
|
69
|
-
):
|
|
69
|
+
): AssistantEvent =>
|
|
70
70
|
({
|
|
71
71
|
type: "ui_surface_show",
|
|
72
72
|
conversationId: "conv-stream",
|
|
@@ -80,18 +80,18 @@ const taskProgressShow = (
|
|
|
80
80
|
steps,
|
|
81
81
|
},
|
|
82
82
|
},
|
|
83
|
-
}) as
|
|
83
|
+
}) as AssistantEvent;
|
|
84
84
|
|
|
85
85
|
const taskProgressUpdate = (
|
|
86
86
|
surfaceId: string,
|
|
87
87
|
steps: Array<{ label: string; status: string }>,
|
|
88
|
-
):
|
|
88
|
+
): AssistantEvent =>
|
|
89
89
|
({
|
|
90
90
|
type: "ui_surface_update",
|
|
91
91
|
conversationId: "conv-stream",
|
|
92
92
|
surfaceId,
|
|
93
93
|
data: { templateData: { steps } },
|
|
94
|
-
}) as
|
|
94
|
+
}) as AssistantEvent;
|
|
95
95
|
|
|
96
96
|
const tick = (ms: number): Promise<void> =>
|
|
97
97
|
new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -351,7 +351,7 @@ describe("createSlackReplySession", () => {
|
|
|
351
351
|
test("falls back when stopStream throws after streaming text", async () => {
|
|
352
352
|
deliverImpl = async (_url, payload) => {
|
|
353
353
|
const op = payload.slackStream as { action: string };
|
|
354
|
-
if (op.action === "stop") throw new Error("stop failed");
|
|
354
|
+
if (op.action === "stop") {throw new Error("stop failed");}
|
|
355
355
|
return { ok: true, ts: "stream-ts-1" };
|
|
356
356
|
};
|
|
357
357
|
const session = createSlackReplySession({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SlackStreamTask } from "@vellumai/gateway-client";
|
|
2
2
|
|
|
3
|
+
import type { AssistantEvent } from "../api/index.js";
|
|
3
4
|
import {
|
|
4
5
|
extractThreadTsFromCallbackUrl,
|
|
5
6
|
isSlackDeliveryCallbackUrl,
|
|
@@ -9,7 +10,6 @@ import {
|
|
|
9
10
|
incompleteVellumLinkSuffixLength,
|
|
10
11
|
stripVellumLinks,
|
|
11
12
|
} from "../daemon/assistant-attachments.js";
|
|
12
|
-
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
13
13
|
import { SLACK_STREAM_MARKDOWN_LIMIT } from "../messaging/providers/slack/api.js";
|
|
14
14
|
import { renderSlackBlocks } from "../messaging/providers/slack/render.js";
|
|
15
15
|
import { getLogger } from "../util/logger.js";
|
|
@@ -51,7 +51,7 @@ export type SlackStreamReconciliation =
|
|
|
51
51
|
| { mode: "fallback" };
|
|
52
52
|
|
|
53
53
|
export type SlackReplySession = {
|
|
54
|
-
observeEvent: (msg:
|
|
54
|
+
observeEvent: (msg: AssistantEvent) => void;
|
|
55
55
|
/**
|
|
56
56
|
* Settle in-flight stream operations, finalize the stream, and report how
|
|
57
57
|
* durable delivery should reconcile. Call once after processing completes.
|
|
@@ -79,12 +79,12 @@ export function shouldStreamSlackReply(params: {
|
|
|
79
79
|
recipientUserId?: string;
|
|
80
80
|
recipientTeamId?: string;
|
|
81
81
|
}): boolean {
|
|
82
|
-
if (params.sourceChannel !== "slack") return false;
|
|
83
|
-
if (!isSlackDeliveryCallbackUrl(params.replyCallbackUrl)) return false;
|
|
82
|
+
if (params.sourceChannel !== "slack") {return false;}
|
|
83
|
+
if (!isSlackDeliveryCallbackUrl(params.replyCallbackUrl)) {return false;}
|
|
84
84
|
if (extractThreadTsFromCallbackUrl(params.replyCallbackUrl) === null) {
|
|
85
85
|
return false;
|
|
86
86
|
}
|
|
87
|
-
if (params.chatType === "im") return true;
|
|
87
|
+
if (params.chatType === "im") {return true;}
|
|
88
88
|
return Boolean(params.recipientUserId && params.recipientTeamId);
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -130,7 +130,7 @@ export function createSlackReplySession(params: {
|
|
|
130
130
|
const coalesceMs = params.coalesceMs ?? STREAM_COALESCE_MS;
|
|
131
131
|
const replyCallbackUrl = params.replyCallbackUrl;
|
|
132
132
|
const threadTs = extractThreadTsFromCallbackUrl(replyCallbackUrl);
|
|
133
|
-
if (threadTs === null) return undefined;
|
|
133
|
+
if (threadTs === null) {return undefined;}
|
|
134
134
|
|
|
135
135
|
let state: StreamState = "idle";
|
|
136
136
|
let started = false;
|
|
@@ -172,7 +172,7 @@ export function createSlackReplySession(params: {
|
|
|
172
172
|
// (and `stripVellumLinks` can remove it). Once `finished`, no delta can
|
|
173
173
|
// extend the text, so the full cleaned reply is safe to emit.
|
|
174
174
|
const streamableText = (): string => {
|
|
175
|
-
if (finished) return cleanedText();
|
|
175
|
+
if (finished) {return cleanedText();}
|
|
176
176
|
const hold = incompleteVellumLinkSuffixLength(rawText);
|
|
177
177
|
const stable = hold > 0 ? rawText.slice(0, rawText.length - hold) : rawText;
|
|
178
178
|
return stripVellumLinks(stable).replace(NO_RESPONSE_INLINE_RE, "");
|
|
@@ -205,7 +205,7 @@ export function createSlackReplySession(params: {
|
|
|
205
205
|
const enqueueStart = (): void => {
|
|
206
206
|
enqueue(async () => {
|
|
207
207
|
const clean = streamableText();
|
|
208
|
-
if (clean.trim().length === 0) return;
|
|
208
|
+
if (clean.trim().length === 0) {return;}
|
|
209
209
|
const firstChunk = clean.slice(0, SLACK_STREAM_MARKDOWN_LIMIT);
|
|
210
210
|
const title = activeProgress?.title;
|
|
211
211
|
const tasks = planTasks();
|
|
@@ -259,7 +259,7 @@ export function createSlackReplySession(params: {
|
|
|
259
259
|
|
|
260
260
|
const enqueueAppend = (): void => {
|
|
261
261
|
enqueue(async () => {
|
|
262
|
-
if (state !== "streaming" || !streamTs) return;
|
|
262
|
+
if (state !== "streaming" || !streamTs) {return;}
|
|
263
263
|
const clean = streamableText();
|
|
264
264
|
const title = activeProgress?.title;
|
|
265
265
|
const tasks = planTasks();
|
|
@@ -329,9 +329,9 @@ export function createSlackReplySession(params: {
|
|
|
329
329
|
clearTimeout(coalesceTimer);
|
|
330
330
|
coalesceTimer = undefined;
|
|
331
331
|
}
|
|
332
|
-
if (finished || state === "fallback") return;
|
|
332
|
+
if (finished || state === "fallback") {return;}
|
|
333
333
|
if (!started) {
|
|
334
|
-
if (!hasDeliverableAssistantText(streamableText())) return;
|
|
334
|
+
if (!hasDeliverableAssistantText(streamableText())) {return;}
|
|
335
335
|
started = true;
|
|
336
336
|
enqueueStart();
|
|
337
337
|
return;
|
|
@@ -340,7 +340,7 @@ export function createSlackReplySession(params: {
|
|
|
340
340
|
};
|
|
341
341
|
|
|
342
342
|
const scheduleFlush = (): void => {
|
|
343
|
-
if (coalesceTimer || finished) return;
|
|
343
|
+
if (coalesceTimer || finished) {return;}
|
|
344
344
|
coalesceTimer = setTimeout(() => {
|
|
345
345
|
coalesceTimer = undefined;
|
|
346
346
|
flush();
|
|
@@ -356,15 +356,15 @@ export function createSlackReplySession(params: {
|
|
|
356
356
|
}
|
|
357
357
|
};
|
|
358
358
|
|
|
359
|
-
const observeTaskProgress = (msg:
|
|
359
|
+
const observeTaskProgress = (msg: AssistantEvent): void => {
|
|
360
360
|
if (msg.type === "ui_surface_show") {
|
|
361
361
|
const progress = getTaskProgressDataFromSurfaceData(msg.data);
|
|
362
|
-
if (!progress) return;
|
|
362
|
+
if (!progress) {return;}
|
|
363
363
|
taskProgressBySurfaceId.set(msg.surfaceId, progress);
|
|
364
364
|
} else if (msg.type === "ui_surface_update") {
|
|
365
365
|
const existing = taskProgressBySurfaceId.get(msg.surfaceId);
|
|
366
366
|
const progress = mergeTaskProgressData(existing, msg.data);
|
|
367
|
-
if (!progress) return;
|
|
367
|
+
if (!progress) {return;}
|
|
368
368
|
taskProgressBySurfaceId.set(msg.surfaceId, progress);
|
|
369
369
|
} else {
|
|
370
370
|
return;
|
|
@@ -375,7 +375,7 @@ export function createSlackReplySession(params: {
|
|
|
375
375
|
|
|
376
376
|
return {
|
|
377
377
|
observeEvent(msg) {
|
|
378
|
-
if (finished) return;
|
|
378
|
+
if (finished) {return;}
|
|
379
379
|
|
|
380
380
|
if (msg.type === "ui_surface_show" || msg.type === "ui_surface_update") {
|
|
381
381
|
observeTaskProgress(msg);
|
|
@@ -415,7 +415,7 @@ export function createSlackReplySession(params: {
|
|
|
415
415
|
|
|
416
416
|
enqueueAppend();
|
|
417
417
|
enqueue(async () => {
|
|
418
|
-
if (state !== "streaming" || !streamTs) return;
|
|
418
|
+
if (state !== "streaming" || !streamTs) {return;}
|
|
419
419
|
const clean = cleanedText();
|
|
420
420
|
const remaining = clean.slice(confirmedLength);
|
|
421
421
|
const blocks = imageBlocks(clean);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import type { AssistantEventEnvelope } from "../../api/index.js";
|
|
3
4
|
import { SYNC_TAGS } from "../../daemon/message-types/sync.js";
|
|
4
|
-
import type { AssistantEvent } from "../assistant-event.js";
|
|
5
5
|
import { assistantEventHub, broadcastMessage } from "../assistant-event-hub.js";
|
|
6
6
|
import { publishSyncInvalidation } from "./sync-publisher.js";
|
|
7
7
|
|
|
@@ -18,7 +18,7 @@ async function waitFor(predicate: () => boolean): Promise<void> {
|
|
|
18
18
|
|
|
19
19
|
describe("sync publisher", () => {
|
|
20
20
|
test("publishes a deduped sync_changed event", async () => {
|
|
21
|
-
const received:
|
|
21
|
+
const received: AssistantEventEnvelope[] = [];
|
|
22
22
|
const subscription = assistantEventHub.subscribe({
|
|
23
23
|
type: "process",
|
|
24
24
|
callback: (event) => {
|
|
@@ -104,7 +104,7 @@ describe("sync publisher", () => {
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
test("forwards originClientId when provided", async () => {
|
|
107
|
-
const received:
|
|
107
|
+
const received: AssistantEventEnvelope[] = [];
|
|
108
108
|
const subscription = assistantEventHub.subscribe({
|
|
109
109
|
type: "process",
|
|
110
110
|
callback: (event) => {
|
|
@@ -132,7 +132,7 @@ describe("sync publisher", () => {
|
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
test("omits originClientId when not provided", async () => {
|
|
135
|
-
const received:
|
|
135
|
+
const received: AssistantEventEnvelope[] = [];
|
|
136
136
|
const subscription = assistantEventHub.subscribe({
|
|
137
137
|
type: "process",
|
|
138
138
|
callback: (event) => {
|
|
@@ -141,7 +141,9 @@ describe("sync publisher", () => {
|
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
try {
|
|
144
|
-
const message = await publishSyncInvalidation([
|
|
144
|
+
const message = await publishSyncInvalidation([
|
|
145
|
+
SYNC_TAGS.assistantAvatar,
|
|
146
|
+
]);
|
|
145
147
|
|
|
146
148
|
await waitFor(() => received.length === 1);
|
|
147
149
|
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
recordApprovalCardDelivery,
|
|
22
22
|
recordGuardianRequestDeliveries,
|
|
23
23
|
} from "../notifications/guardian-delivery-recorder.js";
|
|
24
|
+
import { buildVellumCardAffinity } from "../notifications/vellum-card-affinity.js";
|
|
24
25
|
import { getLogger } from "../util/logger.js";
|
|
25
26
|
import { resolveApprovalSourceReference } from "./approval-source-link.js";
|
|
26
27
|
import { getGuardianBinding } from "./channel-verification-service.js";
|
|
@@ -177,6 +178,9 @@ export async function createOrReuseToolGrantRequest(
|
|
|
177
178
|
sourceChannel,
|
|
178
179
|
sourceContextId: conversationId,
|
|
179
180
|
requiresConversation: true,
|
|
181
|
+
// Pin the in-app (vellum) card to the conversation the escalated tool is
|
|
182
|
+
// running in, so the guardian decides it in context.
|
|
183
|
+
...(buildVellumCardAffinity(conversationId) ?? {}),
|
|
180
184
|
attentionHints: {
|
|
181
185
|
requiresAction: true,
|
|
182
186
|
urgency: "high",
|
|
@@ -3,6 +3,7 @@ import { and, asc, desc, eq, isNull, lt, lte, sql } from "drizzle-orm";
|
|
|
3
3
|
import { v4 as uuid } from "uuid";
|
|
4
4
|
|
|
5
5
|
import { getDb } from "../persistence/db-connection.js";
|
|
6
|
+
import { isLifecycleQuiesced } from "../persistence/lifecycle-quiesce.js";
|
|
6
7
|
import { rawChanges } from "../persistence/raw-query.js";
|
|
7
8
|
import { scheduleJobs, scheduleRuns } from "../persistence/schema/index.js";
|
|
8
9
|
import { publishSchedulesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
@@ -465,6 +466,15 @@ export async function deleteSchedule(id: string): Promise<boolean> {
|
|
|
465
466
|
* next_run_at <= now and enabled = true and cron_expression IS NULL.
|
|
466
467
|
*/
|
|
467
468
|
export async function claimDueSchedules(now: number): Promise<ScheduleJob[]> {
|
|
469
|
+
// Drain gate: while a quiesce lease is active, claim nothing so in-flight
|
|
470
|
+
// runs can drain before a stop. The check lives here — immediately before
|
|
471
|
+
// the claim writes — rather than at the tick boundary, to shrink the window
|
|
472
|
+
// in which a lease armed mid-tick could miss a claim that already passed an
|
|
473
|
+
// earlier check. Fail-open via the lease read.
|
|
474
|
+
if (isLifecycleQuiesced()) {
|
|
475
|
+
return [];
|
|
476
|
+
}
|
|
477
|
+
|
|
468
478
|
const db = getDb();
|
|
469
479
|
const claimed: ScheduleJob[] = [];
|
|
470
480
|
|
|
@@ -750,6 +760,27 @@ export async function createScheduleRun(
|
|
|
750
760
|
return id;
|
|
751
761
|
}
|
|
752
762
|
|
|
763
|
+
/** Currently-running schedule runs with their job names, oldest first. */
|
|
764
|
+
export function listRunningScheduleRuns(): Array<{
|
|
765
|
+
runId: string;
|
|
766
|
+
scheduleName: string | null;
|
|
767
|
+
startedAt: number;
|
|
768
|
+
}> {
|
|
769
|
+
const db = getDb();
|
|
770
|
+
return db
|
|
771
|
+
.select({
|
|
772
|
+
runId: scheduleRuns.id,
|
|
773
|
+
scheduleName: scheduleJobs.name,
|
|
774
|
+
startedAt: scheduleRuns.startedAt,
|
|
775
|
+
})
|
|
776
|
+
.from(scheduleRuns)
|
|
777
|
+
.leftJoin(scheduleJobs, eq(scheduleRuns.jobId, scheduleJobs.id))
|
|
778
|
+
.where(eq(scheduleRuns.status, "running"))
|
|
779
|
+
.orderBy(asc(scheduleRuns.startedAt))
|
|
780
|
+
.limit(20)
|
|
781
|
+
.all();
|
|
782
|
+
}
|
|
783
|
+
|
|
753
784
|
export async function setScheduleRunConversationId(
|
|
754
785
|
runId: string,
|
|
755
786
|
conversationId: string,
|
|
@@ -1082,6 +1113,45 @@ export function describeCronExpression(expr: string | null): string {
|
|
|
1082
1113
|
}
|
|
1083
1114
|
}
|
|
1084
1115
|
|
|
1116
|
+
/**
|
|
1117
|
+
* Return a claimed-but-not-started schedule to the queue (drain deferral).
|
|
1118
|
+
*
|
|
1119
|
+
* Restores everything the claim mutated before any execution began:
|
|
1120
|
+
* `nextRunAt` is pulled to `nextRetryAt`, a one-shot's `firing` reverts to
|
|
1121
|
+
* `active`, and `enabled` is restored — a bounded recurring schedule's final
|
|
1122
|
+
* occurrence is claimed by exhausting the job (`enabled = false`,
|
|
1123
|
+
* `nextRunAt = 0`), so without the restore that deferred final occurrence
|
|
1124
|
+
* would never fire.
|
|
1125
|
+
*/
|
|
1126
|
+
export async function deferClaimedSchedule(
|
|
1127
|
+
id: string,
|
|
1128
|
+
nextRetryAt: number,
|
|
1129
|
+
): Promise<void> {
|
|
1130
|
+
const db = getDb();
|
|
1131
|
+
const now = Date.now();
|
|
1132
|
+
await withSqliteRetry(
|
|
1133
|
+
() => {
|
|
1134
|
+
db.update(scheduleJobs)
|
|
1135
|
+
.set({ nextRunAt: nextRetryAt, enabled: true, updatedAt: now })
|
|
1136
|
+
.where(eq(scheduleJobs.id, id))
|
|
1137
|
+
.run();
|
|
1138
|
+
return rawChanges() > 0;
|
|
1139
|
+
},
|
|
1140
|
+
{ op: "deferClaimedSchedule.requeue", context: { scheduleId: id } },
|
|
1141
|
+
);
|
|
1142
|
+
await withSqliteRetry(
|
|
1143
|
+
() => {
|
|
1144
|
+
db.update(scheduleJobs)
|
|
1145
|
+
.set({ status: "active", updatedAt: now })
|
|
1146
|
+
.where(and(eq(scheduleJobs.id, id), eq(scheduleJobs.status, "firing")))
|
|
1147
|
+
.run();
|
|
1148
|
+
return rawChanges() > 0;
|
|
1149
|
+
},
|
|
1150
|
+
{ op: "deferClaimedSchedule.status", context: { scheduleId: id } },
|
|
1151
|
+
);
|
|
1152
|
+
notifySchedulesChanged();
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1085
1155
|
/**
|
|
1086
1156
|
* Set the next retry time for a schedule and revert one-shot status from
|
|
1087
1157
|
* "firing" to "active" so the scheduler will claim it again when nextRetryAt
|
|
@@ -9,6 +9,7 @@ import { processMessage } from "../daemon/process-message.js";
|
|
|
9
9
|
import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../daemon/trust-context.js";
|
|
10
10
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
11
11
|
import { getConversation } from "../persistence/conversation-crud.js";
|
|
12
|
+
import { isLifecycleQuiesced } from "../persistence/lifecycle-quiesce.js";
|
|
12
13
|
import { invalidateAssistantInferredItemsForConversation } from "../plugins/defaults/memory/task-memory-cleanup.js";
|
|
13
14
|
import { wakeAgentForOpportunity } from "../runtime/agent-wake.js";
|
|
14
15
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
completeOneShot,
|
|
34
35
|
completeScheduleRun,
|
|
35
36
|
createScheduleRun,
|
|
37
|
+
deferClaimedSchedule,
|
|
36
38
|
failOneShotPermanently,
|
|
37
39
|
getLastScheduleConversationId,
|
|
38
40
|
resetRetryCount,
|
|
@@ -434,6 +436,9 @@ export async function runScheduleDueWorkOnce(
|
|
|
434
436
|
return result;
|
|
435
437
|
}
|
|
436
438
|
|
|
439
|
+
// The drain quiesce gates live inside claimDueWatchers and
|
|
440
|
+
// claimDueEnrollments, immediately before their claim writes.
|
|
441
|
+
|
|
437
442
|
// ── Watchers (event-driven polling) ────────────────────────────────
|
|
438
443
|
try {
|
|
439
444
|
const watcherProcessed = await runWatchersOnce(emitWatcherNotifySignal);
|
|
@@ -463,6 +468,9 @@ export async function runScheduleDueWorkOnce(
|
|
|
463
468
|
* (`worker.ts`). Claims are atomic in the schedule store, so overlapping ticks
|
|
464
469
|
* cannot double-run a job another claim already took.
|
|
465
470
|
*/
|
|
471
|
+
/** How far a claimed-but-quiesced schedule is pushed back into the queue. */
|
|
472
|
+
const QUIESCE_DEFER_MS = 30_000;
|
|
473
|
+
|
|
466
474
|
export async function runDueSchedulesOnce(
|
|
467
475
|
now: number = Date.now(),
|
|
468
476
|
): Promise<SchedulerDueWorkResult> {
|
|
@@ -490,9 +498,28 @@ export async function runDueSchedulesOnce(
|
|
|
490
498
|
return result;
|
|
491
499
|
}
|
|
492
500
|
|
|
501
|
+
// The drain quiesce gate lives inside claimDueSchedules, immediately
|
|
502
|
+
// before the claim writes.
|
|
493
503
|
const jobs = await claimDueSchedules(now);
|
|
494
504
|
result.claimed = jobs.length;
|
|
495
505
|
for (const job of jobs) {
|
|
506
|
+
// Lease re-check per claimed job: a lease armed between the batch claim
|
|
507
|
+
// and this job's start returns the job to the queue untouched instead of
|
|
508
|
+
// starting work the drain snapshot cannot see — notify mode especially,
|
|
509
|
+
// which emits before any run row exists.
|
|
510
|
+
if (isLifecycleQuiesced()) {
|
|
511
|
+
try {
|
|
512
|
+
await deferClaimedSchedule(job.id, Date.now() + QUIESCE_DEFER_MS);
|
|
513
|
+
} catch (err) {
|
|
514
|
+
log.warn(
|
|
515
|
+
{ err, jobId: job.id },
|
|
516
|
+
"Failed to defer claimed schedule under quiesce",
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
result.skipped += 1;
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
|
|
496
523
|
const isOneShot = job.expression == null;
|
|
497
524
|
|
|
498
525
|
// ── Notify mode (one-shot or recurring) ─────────────────────────
|