@vellumai/assistant 0.8.12 → 0.9.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 +0 -14
- package/ARCHITECTURE.md +45 -45
- package/README.md +1 -1
- package/bun.lock +200 -154
- package/docs/architecture/integrations.md +3 -3
- package/docs/architecture/memory.md +2 -2
- package/docs/architecture/security.md +10 -10
- package/docs/runbook-trusted-contacts.md +12 -12
- package/docs/skills.md +6 -6
- package/docs/workflows-testing.md +221 -0
- package/docs/workflows.md +510 -0
- package/examples/plugins/echo/README.md +5 -5
- package/knip.json +2 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
- package/openapi.yaml +7197 -5708
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +66 -114
- package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
- package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
- package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
- package/src/__tests__/app-compiler.test.ts +7 -1
- package/src/__tests__/app-dir-path-guard.test.ts +27 -3
- package/src/__tests__/app-executors.test.ts +43 -0
- package/src/__tests__/approval-cascade.test.ts +0 -5
- package/src/__tests__/approval-routes-http.test.ts +91 -0
- package/src/__tests__/assistant-stream-state.test.ts +107 -0
- package/src/__tests__/browser-fill-credential.test.ts +3 -3
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +63 -7
- package/src/__tests__/compaction-trail-store.test.ts +74 -1
- package/src/__tests__/compaction.benchmark.test.ts +63 -41
- package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -5
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
- package/src/__tests__/conversation-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
- package/src/__tests__/conversation-speed-override.test.ts +0 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
- package/src/__tests__/credential-broker-server-use.test.ts +2 -2
- package/src/__tests__/credential-broker.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +417 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault.test.ts +37 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
- package/src/__tests__/dynamic-page-surface.test.ts +219 -0
- package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
- package/src/__tests__/gateway-flag-listener.test.ts +24 -7
- package/src/__tests__/guardian-action-sweep.test.ts +56 -219
- package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
- package/src/__tests__/llm-resolver.test.ts +115 -0
- package/src/__tests__/managed-profile-guard.test.ts +6 -5
- package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
- package/src/__tests__/media-generate-image.test.ts +20 -9
- package/src/__tests__/mock-gateway-ipc.ts +23 -0
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/normalize-onboarding.test.ts +26 -0
- package/src/__tests__/notification-decision-strategy.test.ts +4 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
- package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
- package/src/__tests__/require-fresh-approval.test.ts +425 -1
- package/src/__tests__/resolve-app-id.test.ts +56 -0
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
- package/src/__tests__/schedule-routes.test.ts +257 -4
- package/src/__tests__/schedule-store.test.ts +60 -0
- package/src/__tests__/schedule-tools.test.ts +247 -2
- package/src/__tests__/skill-execute-input.test.ts +85 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
- package/src/__tests__/task-scheduler.test.ts +299 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
- package/src/__tests__/tool-result-spool.test.ts +3 -1
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
- package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
- package/src/agent/compaction-circuit.ts +11 -0
- package/src/agent/loop.ts +181 -12
- package/src/api/constants/call-sites.ts +12 -0
- package/src/api/events/assistant-thinking-delta.ts +10 -0
- package/src/api/events/usage-update.ts +7 -0
- package/src/api/index.ts +4 -1
- package/src/api/responses/memory-v3-selection-log.ts +18 -11
- package/src/approvals/approval-primitive.ts +2 -2
- package/src/background-wake/background-wake-routes.test.ts +5 -2
- package/src/bundler/compiler-tools.ts +1 -1
- package/src/bundler/package-resolver.ts +0 -1
- package/src/calls/call-domain.ts +1 -1
- package/src/calls/guardian-action-sweep.ts +16 -93
- package/src/cli/AGENTS.md +4 -0
- package/src/cli/commands/__tests__/schedules.test.ts +430 -1
- package/src/cli/commands/credentials.ts +28 -24
- package/src/cli/commands/image-generation.ts +23 -9
- package/src/cli/commands/notifications.ts +1 -1
- package/src/cli/commands/plugins.ts +89 -46
- package/src/cli/commands/schedules.ts +384 -11
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
- package/src/cli/lib/inspect-plugin.ts +62 -1
- package/src/cli/lib/install-from-github.ts +52 -5
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/config/__tests__/workflows-schema.test.ts +60 -0
- package/src/config/bundled-skills/acp/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
- package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
- package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
- package/src/config/bundled-skills/schedule/SKILL.md +7 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
- package/src/config/bundled-skills/workflows/SKILL.md +214 -0
- package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
- package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
- package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
- package/src/config/bundled-tool-registry.ts +14 -2
- package/src/config/call-site-defaults.ts +5 -0
- package/src/config/feature-flag-registry.json +12 -4
- package/src/config/llm-context-resolution.ts +8 -0
- package/src/config/llm-resolver.ts +30 -0
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
- package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
- package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
- package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/heartbeat.ts +4 -1
- package/src/config/schemas/llm.ts +33 -26
- package/src/config/schemas/memory-retrospective.ts +19 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/services.ts +5 -2
- package/src/config/schemas/workflows.ts +42 -0
- package/src/config/skills.ts +3 -3
- package/src/context/compactor.ts +273 -39
- package/src/context/post-turn-tool-result-truncation.ts +23 -6
- package/src/context/tool-result-spool.ts +12 -17
- package/src/credential-execution/executable-discovery.ts +1 -1
- package/src/credential-execution/process-manager.ts +37 -3
- package/src/credential-execution/prompted-credential.ts +205 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
- package/src/daemon/conversation-process.ts +11 -2
- package/src/daemon/conversation-surfaces.ts +167 -3
- package/src/daemon/conversation-tool-setup.ts +103 -26
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +115 -11
- package/src/daemon/handlers/shared.ts +26 -14
- package/src/daemon/host-cu-proxy.ts +15 -12
- package/src/daemon/host-file-proxy.ts +15 -12
- package/src/daemon/host-transfer-proxy.ts +30 -24
- package/src/daemon/lifecycle.ts +40 -3
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/messages.ts +2 -10
- package/src/daemon/message-types/workflows.ts +49 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
- package/src/daemon/process-message.ts +6 -0
- package/src/daemon/tool-setup-types.ts +57 -0
- package/src/daemon/wake-conversation-ops.ts +18 -0
- package/src/heartbeat/heartbeat-run-store.ts +8 -2
- package/src/home/feed-types.ts +1 -1
- package/src/ipc/gateway-flag-listener.ts +28 -6
- package/src/mcp/mcp-auth-state.ts +8 -20
- package/src/media/__tests__/image-models.test.ts +57 -0
- package/src/media/image-models.ts +66 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
- package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
- package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
- package/src/memory/auto-analysis-enqueue.ts +14 -1
- package/src/memory/compaction-log-store-clickhouse.ts +6 -4
- package/src/memory/conversation-crud.ts +9 -2
- package/src/memory/conversation-disk-view.ts +1 -1
- package/src/memory/conversation-queries.ts +22 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/db-maintenance.ts +16 -0
- package/src/memory/embedding-runtime-manager.ts +1 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
- package/src/memory/llm-request-log-source-local.ts +19 -1
- package/src/memory/llm-request-log-source.ts +35 -6
- package/src/memory/llm-request-log-store.ts +90 -2
- package/src/memory/memory-retrospective-constants.ts +9 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -6
- package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
- package/src/memory/memory-retrospective-job.ts +500 -208
- package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
- package/src/memory/memory-retrospective-state.ts +85 -2
- package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
- package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
- package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
- package/src/memory/migrations/284-workflow-runs.ts +51 -0
- package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
- package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
- package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
- package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
- package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
- package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
- package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/preloaded-apps.ts +116 -0
- package/src/memory/schema/infrastructure.ts +4 -0
- package/src/memory/schema/memory-core.ts +4 -0
- package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
- package/src/memory/v2/__tests__/page-store.test.ts +13 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -6
- package/src/memory/v2/page-store.ts +4 -3
- package/src/memory/v2/qdrant.ts +42 -3
- package/src/memory/v2/types.ts +16 -10
- package/src/messaging/draft-store.ts +1 -1
- package/src/notifications/access-request-copy.ts +200 -113
- package/src/notifications/adapters/slack.ts +250 -111
- package/src/notifications/adapters/telegram.ts +7 -44
- package/src/notifications/approval-card-builder.ts +93 -0
- package/src/notifications/broadcaster.ts +74 -0
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/copy-composer.ts +32 -26
- package/src/notifications/decision-engine.ts +59 -7
- package/src/notifications/guardian-question-mode.ts +145 -155
- package/src/notifications/home-feed-side-effect.ts +28 -11
- package/src/notifications/notification-utils.ts +66 -0
- package/src/notifications/signal.ts +6 -0
- package/src/notifications/tool-approval-copy.ts +142 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/threshold.ts +11 -0
- package/src/plugin-api/types.ts +16 -4
- package/src/plugins/defaults/compaction/window-manager.ts +44 -0
- package/src/plugins/defaults/index.ts +46 -0
- package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
- package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
- package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
- package/src/prompts/__tests__/system-prompt.test.ts +100 -1
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
- package/src/prompts/normalize-onboarding.ts +2 -0
- package/src/prompts/persona-resolver.ts +3 -0
- package/src/prompts/system-prompt.ts +51 -2
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
- package/src/prompts/templates/system-sections.ts +8 -3
- package/src/providers/call-site-routing.ts +6 -3
- package/src/providers/fireworks/client.ts +3 -0
- package/src/providers/inference/auth.ts +52 -46
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +47 -9
- package/src/providers/openai/coerce-object-args.ts +104 -0
- package/src/providers/retry.ts +8 -5
- package/src/providers/types.ts +10 -0
- package/src/runtime/__tests__/agent-wake.test.ts +629 -7
- package/src/runtime/access-request-helper.ts +16 -9
- package/src/runtime/agent-wake.ts +302 -51
- package/src/runtime/assistant-stream-state.ts +141 -8
- package/src/runtime/background-job-runner.ts +9 -0
- package/src/runtime/channel-approval-types.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +6 -5
- package/src/runtime/channel-invite-transports/voice.ts +2 -2
- package/src/runtime/channel-invite-types.ts +4 -2
- package/src/runtime/channel-retry-sweep.ts +19 -41
- package/src/runtime/finalize-event-delivery.ts +72 -0
- package/src/runtime/guardian-action-message-composer.ts +0 -54
- package/src/runtime/http-server.ts +6 -14
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/message-composer-types.ts +0 -9
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
- package/src/runtime/middleware/auth.ts +27 -3
- package/src/runtime/middleware/rate-limiter.ts +28 -1
- package/src/runtime/migrations/vbundle-builder.ts +6 -5
- package/src/runtime/pending-interactions.ts +20 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
- package/src/runtime/routes/approval-routes.ts +35 -8
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
- package/src/runtime/routes/btw-routes.ts +37 -1
- package/src/runtime/routes/channel-delivery-routes.ts +11 -7
- package/src/runtime/routes/channel-route-definitions.ts +3 -0
- package/src/runtime/routes/channel-route-shared.ts +3 -1
- package/src/runtime/routes/consolidation-routes.ts +17 -13
- package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
- package/src/runtime/routes/conversation-list-routes.ts +41 -4
- package/src/runtime/routes/conversation-query-routes.ts +196 -11
- package/src/runtime/routes/conversation-routes.ts +15 -1
- package/src/runtime/routes/credential-prompt-routes.ts +39 -17
- package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
- package/src/runtime/routes/heartbeat-routes.ts +18 -13
- package/src/runtime/routes/image-generation-routes.ts +20 -2
- package/src/runtime/routes/inbound-message-handler.ts +13 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/log-export/AGENTS.md +1 -1
- package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +5 -9
- package/src/runtime/routes/plugins-routes.ts +26 -0
- package/src/runtime/routes/ps-routes.ts +10 -8
- package/src/runtime/routes/retrospective-routes.ts +235 -0
- package/src/runtime/routes/runs-pagination.ts +75 -0
- package/src/runtime/routes/schedule-routes.ts +367 -59
- package/src/runtime/routes/sounds-config-routes.ts +239 -0
- package/src/runtime/routes/surface-action-routes.ts +84 -4
- package/src/runtime/routes/workflow-routes.test.ts +372 -0
- package/src/runtime/routes/workflow-routes.ts +363 -0
- package/src/runtime/routes/workspace-routes.test.ts +61 -1
- package/src/runtime/routes/workspace-routes.ts +26 -1
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
- package/src/runtime/services/analyze-conversation.ts +26 -13
- package/src/schedule/inference-profile.ts +28 -0
- package/src/schedule/schedule-store.ts +152 -4
- package/src/schedule/scheduler-types.ts +6 -0
- package/src/schedule/scheduler.ts +96 -0
- package/src/security/secret-allowlist.ts +1 -1
- package/src/skills/path-classifier.ts +1 -1
- package/src/tools/apps/executors.ts +23 -0
- package/src/tools/apps/resolve-app-id.ts +42 -0
- package/src/tools/browser/browser-execution.ts +9 -11
- package/src/tools/credentials/broker.ts +4 -4
- package/src/tools/credentials/vault.ts +26 -137
- package/src/tools/executor.ts +69 -0
- package/src/tools/flag-gated-tools.test.ts +76 -0
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/registry.ts +51 -0
- package/src/tools/schedule/create.ts +77 -1
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +73 -1
- package/src/tools/skills/execute.ts +56 -0
- package/src/tools/terminal/shell.ts +1 -1
- package/src/tools/ui-surface/definitions.ts +91 -2
- package/src/tools/workflows/manage-workflows.ts +183 -0
- package/src/tools/workflows/run-workflow.test.ts +442 -0
- package/src/tools/workflows/run-workflow.ts +88 -0
- package/src/types/onboarding-context.ts +2 -0
- package/src/usage/attribution.ts +24 -0
- package/src/util/canonicalize-identity.ts +12 -3
- package/src/util/platform.ts +17 -17
- package/src/watcher/__tests__/engine.test.ts +24 -0
- package/src/watcher/__tests__/telemetry.test.ts +135 -0
- package/src/watcher/engine.ts +7 -0
- package/src/watcher/telemetry.ts +74 -0
- package/src/workflows/capabilities.test.ts +365 -0
- package/src/workflows/capabilities.ts +359 -0
- package/src/workflows/deterministic-stringify.ts +27 -0
- package/src/workflows/engine-integration.test.ts +656 -0
- package/src/workflows/engine.test.ts +1144 -0
- package/src/workflows/engine.ts +1078 -0
- package/src/workflows/fanout-load.test.ts +168 -0
- package/src/workflows/journal-store.test.ts +369 -0
- package/src/workflows/journal-store.ts +470 -0
- package/src/workflows/leaf-runner.test.ts +704 -0
- package/src/workflows/leaf-runner.ts +589 -0
- package/src/workflows/library.test.ts +134 -0
- package/src/workflows/library.ts +124 -0
- package/src/workflows/run-manager.test.ts +711 -0
- package/src/workflows/run-manager.ts +593 -0
- package/src/workflows/sandbox-escape.test.ts +339 -0
- package/src/workflows/sandbox.test.ts +251 -0
- package/src/workflows/sandbox.ts +447 -0
- package/src/workspace/adaptive-thinking-repair.ts +33 -11
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
- package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
- package/src/workspace/migrations/031-drop-user-md.ts +1 -4
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
- package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
- package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
- package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/migrations/runner.ts +1 -1
- package/tsconfig.json +1 -1
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
- package/src/__tests__/guardian-action-store.test.ts +0 -106
- package/src/daemon/guardian-action-generators.ts +0 -71
- package/src/memory/guardian-action-store.ts +0 -484
- package/src/runtime/guardian-action-grant-minter.ts +0 -150
|
@@ -20,10 +20,16 @@
|
|
|
20
20
|
* - Total size: 256 KB
|
|
21
21
|
* - Age: 30 seconds
|
|
22
22
|
*
|
|
23
|
-
* The ring is in-memory and per-daemon-process
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* The ring is in-memory and per-daemon-process, but the seq counter
|
|
24
|
+
* itself survives restarts: blocks of seq values are reserved ahead of
|
|
25
|
+
* use and the reserved ceiling is persisted to the workspace
|
|
26
|
+
* (`data/stream-seq.json`), so a restarted daemon resumes stamping
|
|
27
|
+
* above every seq the previous process could have emitted. Clients
|
|
28
|
+
* therefore never observe the counter moving backwards — a restart
|
|
29
|
+
* shows up as (at most) a bounded forward gap, which the normal
|
|
30
|
+
* gap-reconcile / snapshot-resync path already handles. The ring is
|
|
31
|
+
* sized generously enough that a typical refresh round-trip (~1-3s)
|
|
32
|
+
* is well within window.
|
|
27
33
|
*
|
|
28
34
|
* Persisted-seq map: alongside the live counter and ring, this module
|
|
29
35
|
* tracks, per conversation, the `seq` of the last event whose content is
|
|
@@ -34,12 +40,17 @@
|
|
|
34
40
|
* persistence flush (assistant rows persist incrementally, debounced, so
|
|
35
41
|
* the snapshot can lag the live counter) -- never the live counter
|
|
36
42
|
* itself, which would over-claim events that have streamed but not yet
|
|
37
|
-
* been written.
|
|
38
|
-
*
|
|
39
|
-
*
|
|
43
|
+
* been written. The map is in-memory and clears on restart; because the
|
|
44
|
+
* counter resumes above the persisted reservation, a value recorded by
|
|
45
|
+
* a previous process could only ever be lower than any seq the new
|
|
46
|
+
* process assigns -- never ambiguous against it. The map is LRU-bounded; an
|
|
40
47
|
* evicted conversation reports no seq and the client cold-starts.
|
|
41
48
|
*/
|
|
42
49
|
|
|
50
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
51
|
+
import { dirname, join } from "node:path";
|
|
52
|
+
|
|
53
|
+
import { getWorkspaceDir } from "../util/platform.js";
|
|
43
54
|
import type { AssistantEvent } from "./assistant-event.js";
|
|
44
55
|
|
|
45
56
|
// ── Tunables ─────────────────────────────────────────────────────────
|
|
@@ -58,6 +69,15 @@ const RING_AGE_LIMIT_MS = 30_000;
|
|
|
58
69
|
*/
|
|
59
70
|
const PERSISTED_SEQ_CONVERSATION_LIMIT = 1024;
|
|
60
71
|
|
|
72
|
+
/**
|
|
73
|
+
* How many seq values are reserved per persisted write. The counter can
|
|
74
|
+
* hand out seqs up to the persisted ceiling without touching disk, so
|
|
75
|
+
* the file is written once per block rather than once per event. A
|
|
76
|
+
* restart skips at most one block's worth of unused seqs — a bounded
|
|
77
|
+
* forward gap that clients already treat as a missed-events signal.
|
|
78
|
+
*/
|
|
79
|
+
const SEQ_RESERVATION_BLOCK = 1024;
|
|
80
|
+
|
|
61
81
|
// ── Types ────────────────────────────────────────────────────────────
|
|
62
82
|
|
|
63
83
|
/**
|
|
@@ -97,6 +117,21 @@ interface RingEntry {
|
|
|
97
117
|
|
|
98
118
|
interface AssistantStreamState {
|
|
99
119
|
nextSeq: number;
|
|
120
|
+
/**
|
|
121
|
+
* Highest seq this process may assign without persisting a new
|
|
122
|
+
* reservation. `0` until the first stamp loads (or creates) the
|
|
123
|
+
* persisted reservation.
|
|
124
|
+
*/
|
|
125
|
+
reservedSeqCeiling: number;
|
|
126
|
+
/** Whether the persisted reservation has been loaded this process. */
|
|
127
|
+
seqReservationLoaded: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Seq of the first event stamped by this process, or `0` before any
|
|
130
|
+
* stamp. Distinguishes a reservation skip (seqs below this value were
|
|
131
|
+
* never assigned by this process, so nothing is missing from the
|
|
132
|
+
* ring) from genuine ring eviction when judging replay validity.
|
|
133
|
+
*/
|
|
134
|
+
firstStampedSeq: number;
|
|
100
135
|
ring: RingEntry[];
|
|
101
136
|
totalSizeBytes: number;
|
|
102
137
|
/**
|
|
@@ -112,6 +147,9 @@ interface AssistantStreamState {
|
|
|
112
147
|
|
|
113
148
|
const state: AssistantStreamState = {
|
|
114
149
|
nextSeq: 1,
|
|
150
|
+
reservedSeqCeiling: 0,
|
|
151
|
+
seqReservationLoaded: false,
|
|
152
|
+
firstStampedSeq: 0,
|
|
115
153
|
ring: [],
|
|
116
154
|
totalSizeBytes: 0,
|
|
117
155
|
persistedSeqByConversation: new Map(),
|
|
@@ -138,7 +176,9 @@ export function stampAndBuffer(
|
|
|
138
176
|
): void {
|
|
139
177
|
if (event.conversationId == null) return;
|
|
140
178
|
|
|
179
|
+
reserveSeqCapacity();
|
|
141
180
|
event.seq = state.nextSeq++;
|
|
181
|
+
if (state.firstStampedSeq === 0) state.firstStampedSeq = event.seq;
|
|
142
182
|
|
|
143
183
|
// Approximate size by serialized JSON length. This is the same
|
|
144
184
|
// bytes-on-wire we'll send, so it tracks ring memory pressure
|
|
@@ -187,8 +227,16 @@ export function getReplayWindow(
|
|
|
187
227
|
|
|
188
228
|
if (state.ring.length === 0) return [];
|
|
189
229
|
|
|
230
|
+
// A cursor from before this process started can skip over the
|
|
231
|
+
// reservation gap: seqs below `firstStampedSeq` were never assigned
|
|
232
|
+
// by this process, so as long as the ring still holds everything
|
|
233
|
+
// this process stamped, no replayable event is missing. (Events from
|
|
234
|
+
// the previous process are unrecoverable either way — clients catch
|
|
235
|
+
// up on those via the snapshot path, triggered by the seq jump.)
|
|
190
236
|
const oldest = state.ring[0]?.seq ?? Infinity;
|
|
191
|
-
|
|
237
|
+
const coversRestartGap =
|
|
238
|
+
lastSeenSeq < state.firstStampedSeq && oldest === state.firstStampedSeq;
|
|
239
|
+
if (lastSeenSeq < oldest - 1 && !coversRestartGap) return null;
|
|
192
240
|
|
|
193
241
|
return state.ring
|
|
194
242
|
.filter(
|
|
@@ -260,6 +308,26 @@ export function getPersistedSeq(conversationId: string): number | null {
|
|
|
260
308
|
*/
|
|
261
309
|
export function _resetStreamStateForTesting(): void {
|
|
262
310
|
state.nextSeq = 1;
|
|
311
|
+
// Mark the reservation as loaded with no ceiling so the next stamp
|
|
312
|
+
// re-reserves from 1, ignoring any reservation file a previous test
|
|
313
|
+
// wrote into the (per-process temp) workspace.
|
|
314
|
+
state.reservedSeqCeiling = 0;
|
|
315
|
+
state.seqReservationLoaded = true;
|
|
316
|
+
state.firstStampedSeq = 0;
|
|
317
|
+
state.ring = [];
|
|
318
|
+
state.totalSizeBytes = 0;
|
|
319
|
+
state.persistedSeqByConversation.clear();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Simulate a daemon restart: clear all in-memory state and force the
|
|
324
|
+
* next stamp to reload the persisted seq reservation. Test-only.
|
|
325
|
+
*/
|
|
326
|
+
export function _simulateRestartForTesting(): void {
|
|
327
|
+
state.nextSeq = 1;
|
|
328
|
+
state.reservedSeqCeiling = 0;
|
|
329
|
+
state.seqReservationLoaded = false;
|
|
330
|
+
state.firstStampedSeq = 0;
|
|
263
331
|
state.ring = [];
|
|
264
332
|
state.totalSizeBytes = 0;
|
|
265
333
|
state.persistedSeqByConversation.clear();
|
|
@@ -286,6 +354,71 @@ export function _peekStreamForTesting(): {
|
|
|
286
354
|
|
|
287
355
|
// ── Internals ────────────────────────────────────────────────────────
|
|
288
356
|
|
|
357
|
+
function seqReservationPath(): string {
|
|
358
|
+
return join(getWorkspaceDir(), "data", "stream-seq.json");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Ensure `state.nextSeq` is covered by the persisted reservation,
|
|
363
|
+
* loading the reservation file on the first stamp of the process and
|
|
364
|
+
* extending it by {@link SEQ_RESERVATION_BLOCK} whenever the counter
|
|
365
|
+
* reaches the ceiling.
|
|
366
|
+
*
|
|
367
|
+
* Persistence is best-effort: if the file cannot be read or written
|
|
368
|
+
* the counter falls back to in-memory-only behavior (the ceiling is
|
|
369
|
+
* still advanced so the write is retried at most once per block, not
|
|
370
|
+
* per event), matching the daemon's degraded-mode philosophy.
|
|
371
|
+
*/
|
|
372
|
+
function reserveSeqCapacity(): void {
|
|
373
|
+
if (!state.seqReservationLoaded) {
|
|
374
|
+
state.seqReservationLoaded = true;
|
|
375
|
+
state.reservedSeqCeiling = readReservedCeiling();
|
|
376
|
+
if (state.reservedSeqCeiling >= state.nextSeq) {
|
|
377
|
+
state.nextSeq = state.reservedSeqCeiling + 1;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (state.nextSeq <= state.reservedSeqCeiling) return;
|
|
382
|
+
|
|
383
|
+
const ceiling = state.nextSeq + SEQ_RESERVATION_BLOCK - 1;
|
|
384
|
+
try {
|
|
385
|
+
const path = seqReservationPath();
|
|
386
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
387
|
+
const tmp = `${path}.tmp`;
|
|
388
|
+
writeFileSync(tmp, JSON.stringify({ reservedSeqCeiling: ceiling }));
|
|
389
|
+
renameSync(tmp, path);
|
|
390
|
+
} catch {
|
|
391
|
+
// Degraded mode: keep stamping from memory.
|
|
392
|
+
}
|
|
393
|
+
state.reservedSeqCeiling = ceiling;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function readReservedCeiling(): number {
|
|
397
|
+
try {
|
|
398
|
+
const parsed: unknown = JSON.parse(
|
|
399
|
+
readFileSync(seqReservationPath(), "utf8"),
|
|
400
|
+
);
|
|
401
|
+
if (
|
|
402
|
+
typeof parsed === "object" &&
|
|
403
|
+
parsed !== null &&
|
|
404
|
+
"reservedSeqCeiling" in parsed
|
|
405
|
+
) {
|
|
406
|
+
const ceiling = (parsed as { reservedSeqCeiling: unknown })
|
|
407
|
+
.reservedSeqCeiling;
|
|
408
|
+
if (
|
|
409
|
+
typeof ceiling === "number" &&
|
|
410
|
+
Number.isFinite(ceiling) &&
|
|
411
|
+
ceiling > 0
|
|
412
|
+
) {
|
|
413
|
+
return Math.floor(ceiling);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
} catch {
|
|
417
|
+
// Missing or unreadable file: cold start from seq 1.
|
|
418
|
+
}
|
|
419
|
+
return 0;
|
|
420
|
+
}
|
|
421
|
+
|
|
289
422
|
/**
|
|
290
423
|
* Mirrors the delivery logic in `AssistantEventHub.publish()`. Returns
|
|
291
424
|
* `true` when `subscriber` would have received the entry during live
|
|
@@ -67,6 +67,12 @@ export interface RunBackgroundJobOptions {
|
|
|
67
67
|
trustContext: TrustContext;
|
|
68
68
|
/** LLM call-site identifier — drives provider/model/effort/etc. resolution. */
|
|
69
69
|
callSite: LLMCallSite;
|
|
70
|
+
/**
|
|
71
|
+
* Optional ad-hoc inference-profile override (`llm.profiles` key) applied
|
|
72
|
+
* to every LLM call the job's turn issues. Used by schedules with a pinned
|
|
73
|
+
* profile; omitted = the call site's default resolution.
|
|
74
|
+
*/
|
|
75
|
+
overrideProfile?: string;
|
|
70
76
|
/** Hard timeout for `processMessage` in milliseconds. */
|
|
71
77
|
timeoutMs: number;
|
|
72
78
|
/**
|
|
@@ -267,6 +273,9 @@ export async function runBackgroundJob(
|
|
|
267
273
|
const work = processMessage(conversation.id, opts.prompt, {
|
|
268
274
|
trustContext: opts.trustContext,
|
|
269
275
|
callSite: opts.callSite,
|
|
276
|
+
...(opts.overrideProfile
|
|
277
|
+
? { overrideProfile: opts.overrideProfile }
|
|
278
|
+
: {}),
|
|
270
279
|
});
|
|
271
280
|
// Absorb late rejections: if the timeout wins the race, `work` keeps
|
|
272
281
|
// running and may eventually reject — swallow so it doesn't surface as
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* verification) tokens that use the same `/start` deep-link mechanism.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import type { CommandIntent, SourceMetadata } from "@vellumai/gateway-client";
|
|
13
|
+
|
|
12
14
|
import type { ChannelId } from "../../channels/types.js";
|
|
13
15
|
import {
|
|
14
16
|
invalidateConfigCache,
|
|
@@ -130,17 +132,16 @@ export const telegramInviteAdapter: ChannelInviteAdapter = {
|
|
|
130
132
|
},
|
|
131
133
|
|
|
132
134
|
extractInboundToken(params: {
|
|
133
|
-
commandIntent?:
|
|
135
|
+
commandIntent?: CommandIntent;
|
|
134
136
|
content: string;
|
|
135
|
-
sourceMetadata?:
|
|
137
|
+
sourceMetadata?: SourceMetadata;
|
|
136
138
|
}): string | undefined {
|
|
137
139
|
// Primary path: structured command intent from the gateway.
|
|
138
140
|
// The gateway normalizes `/start <payload>` into
|
|
139
141
|
// `{ type: 'start', payload: '<payload>' }`.
|
|
140
142
|
if (
|
|
141
|
-
params.commandIntent &&
|
|
142
|
-
params.commandIntent.
|
|
143
|
-
typeof params.commandIntent.payload === "string"
|
|
143
|
+
params.commandIntent?.type === "start" &&
|
|
144
|
+
params.commandIntent.payload
|
|
144
145
|
) {
|
|
145
146
|
const payload = params.commandIntent.payload;
|
|
146
147
|
if (payload.startsWith(INVITE_TOKEN_PREFIX)) {
|
|
@@ -40,9 +40,9 @@ export const voiceInviteAdapter: ChannelInviteAdapter = {
|
|
|
40
40
|
},
|
|
41
41
|
|
|
42
42
|
extractInboundToken(_params: {
|
|
43
|
-
commandIntent?:
|
|
43
|
+
commandIntent?: import("@vellumai/gateway-client").CommandIntent;
|
|
44
44
|
content: string;
|
|
45
|
-
sourceMetadata?:
|
|
45
|
+
sourceMetadata?: import("@vellumai/gateway-client").SourceMetadata;
|
|
46
46
|
}): string | undefined {
|
|
47
47
|
// Voice invite redemption bypasses generic token extraction — it uses
|
|
48
48
|
// the identity-bound voice-code flow in invite-redemption-service.ts.
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* transport ↔ channel-invite-transports/* cycles (×5).
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { CommandIntent, SourceMetadata } from "@vellumai/gateway-client";
|
|
7
|
+
|
|
6
8
|
import type { ChannelId } from "../channels/types.js";
|
|
7
9
|
|
|
8
10
|
export interface InviteShareLink {
|
|
@@ -31,9 +33,9 @@ export interface ChannelInviteAdapter {
|
|
|
31
33
|
* channels with link-based invites.
|
|
32
34
|
*/
|
|
33
35
|
extractInboundToken?(params: {
|
|
34
|
-
commandIntent?:
|
|
36
|
+
commandIntent?: CommandIntent;
|
|
35
37
|
content: string;
|
|
36
|
-
sourceMetadata?:
|
|
38
|
+
sourceMetadata?: SourceMetadata;
|
|
37
39
|
}): string | undefined;
|
|
38
40
|
|
|
39
41
|
/**
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
deliverReplyViaCallback,
|
|
36
36
|
findAssistantReplyMessageIdForTurn,
|
|
37
37
|
} from "./channel-reply-delivery.js";
|
|
38
|
+
import { finalizeEventDelivery } from "./finalize-event-delivery.js";
|
|
38
39
|
import { deliverChannelReply } from "./gateway-client.js";
|
|
39
40
|
import type { MessageProcessor } from "./http-types.js";
|
|
40
41
|
import { createSlackDmTextDeliveryController } from "./slack-dm-text-delivery.js";
|
|
@@ -154,7 +155,7 @@ export async function sweepFailedEvents(
|
|
|
154
155
|
parseInterfaceId(payload.sourceChannel) ??
|
|
155
156
|
"web";
|
|
156
157
|
const sourceMetadata = payload.sourceMetadata as
|
|
157
|
-
|
|
|
158
|
+
| import("@vellumai/gateway-client").SourceMetadata
|
|
158
159
|
| undefined;
|
|
159
160
|
const assistantId =
|
|
160
161
|
typeof payload.assistantId === "string" ? payload.assistantId : undefined;
|
|
@@ -334,46 +335,23 @@ export async function sweepFailedEvents(
|
|
|
334
335
|
continue;
|
|
335
336
|
}
|
|
336
337
|
|
|
337
|
-
if (replyCallbackUrl) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
event.conversationId,
|
|
355
|
-
externalChatId,
|
|
356
|
-
replyCallbackUrl,
|
|
357
|
-
assistantId,
|
|
358
|
-
{
|
|
359
|
-
messageId: replyMessageId,
|
|
360
|
-
sinceMessageId: userMessageId,
|
|
361
|
-
startFromSegment: finalDeliveryStartFromSegment,
|
|
362
|
-
...(liveDeliveryResumeOptions?.messageTs
|
|
363
|
-
? { messageTs: liveDeliveryResumeOptions.messageTs }
|
|
364
|
-
: {}),
|
|
365
|
-
onSegmentDelivered: (count) =>
|
|
366
|
-
updateDeliveredSegmentCount(event.id, count),
|
|
367
|
-
},
|
|
368
|
-
);
|
|
369
|
-
markDeliveryDelivered(event.id);
|
|
370
|
-
} catch (err) {
|
|
371
|
-
log.error(
|
|
372
|
-
{ err, eventId: event.id },
|
|
373
|
-
"Retry delivery failed for channel event",
|
|
374
|
-
);
|
|
375
|
-
recordDeliveryFailure(event.id, err);
|
|
376
|
-
}
|
|
338
|
+
if (replyCallbackUrl && externalChatId) {
|
|
339
|
+
try {
|
|
340
|
+
await finalizeEventDelivery({
|
|
341
|
+
eventId: event.id,
|
|
342
|
+
conversationId: event.conversationId,
|
|
343
|
+
externalChatId,
|
|
344
|
+
replyCallbackUrl,
|
|
345
|
+
assistantId,
|
|
346
|
+
replyMessageId,
|
|
347
|
+
userMessageId,
|
|
348
|
+
slackDmTextDelivery,
|
|
349
|
+
});
|
|
350
|
+
} catch (err) {
|
|
351
|
+
log.error(
|
|
352
|
+
{ err, eventId: event.id },
|
|
353
|
+
"Retry delivery failed for channel event",
|
|
354
|
+
);
|
|
377
355
|
}
|
|
378
356
|
}
|
|
379
357
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { updateDeliveredSegmentCount } from "../memory/delivery-channels.js";
|
|
2
|
+
import {
|
|
3
|
+
markDeliveryDelivered,
|
|
4
|
+
recordDeliveryFailure,
|
|
5
|
+
} from "../memory/delivery-status.js";
|
|
6
|
+
import { deliverReplyViaCallback } from "./channel-reply-delivery.js";
|
|
7
|
+
import type { SlackDmTextDeliveryController } from "./slack-dm-text-delivery.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Owns the complete delivery-after-processing sequence for a channel
|
|
11
|
+
* inbound event: waits for in-flight live Slack DM deliveries to settle,
|
|
12
|
+
* persists the segment baseline so delivery-only retries resume correctly,
|
|
13
|
+
* delivers remaining content + attachments, and transitions the event to
|
|
14
|
+
* its terminal delivery state.
|
|
15
|
+
*
|
|
16
|
+
* Both the primary dispatch path and the processing-retry path call this
|
|
17
|
+
* function. The delivery-only retry path does NOT use this function — it
|
|
18
|
+
* reads the already-persisted segment count and calls
|
|
19
|
+
* `deliverReplyViaCallback` directly.
|
|
20
|
+
*/
|
|
21
|
+
export async function finalizeEventDelivery(params: {
|
|
22
|
+
eventId: string;
|
|
23
|
+
conversationId: string;
|
|
24
|
+
externalChatId: string;
|
|
25
|
+
replyCallbackUrl: string;
|
|
26
|
+
assistantId: string | undefined;
|
|
27
|
+
replyMessageId: string | undefined;
|
|
28
|
+
userMessageId: string | undefined;
|
|
29
|
+
slackDmTextDelivery: SlackDmTextDeliveryController | undefined;
|
|
30
|
+
}): Promise<void> {
|
|
31
|
+
const {
|
|
32
|
+
eventId,
|
|
33
|
+
conversationId,
|
|
34
|
+
externalChatId,
|
|
35
|
+
replyCallbackUrl,
|
|
36
|
+
assistantId,
|
|
37
|
+
replyMessageId,
|
|
38
|
+
userMessageId,
|
|
39
|
+
slackDmTextDelivery,
|
|
40
|
+
} = params;
|
|
41
|
+
|
|
42
|
+
if (slackDmTextDelivery) {
|
|
43
|
+
await slackDmTextDelivery.waitForPendingDeliveries();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const resumeOptions =
|
|
47
|
+
slackDmTextDelivery?.getFinalDeliveryResumeOptions(replyMessageId);
|
|
48
|
+
const startFromSegment = resumeOptions?.startFromSegment ?? 0;
|
|
49
|
+
try {
|
|
50
|
+
updateDeliveredSegmentCount(eventId, startFromSegment);
|
|
51
|
+
await deliverReplyViaCallback(
|
|
52
|
+
conversationId,
|
|
53
|
+
externalChatId,
|
|
54
|
+
replyCallbackUrl,
|
|
55
|
+
assistantId,
|
|
56
|
+
{
|
|
57
|
+
messageId: replyMessageId,
|
|
58
|
+
sinceMessageId: userMessageId,
|
|
59
|
+
startFromSegment,
|
|
60
|
+
...(resumeOptions?.messageTs
|
|
61
|
+
? { messageTs: resumeOptions.messageTs }
|
|
62
|
+
: {}),
|
|
63
|
+
onSegmentDelivered: (count) =>
|
|
64
|
+
updateDeliveredSegmentCount(eventId, count),
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
markDeliveryDelivered(eventId);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
recordDeliveryFailure(eventId, err);
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -8,10 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Follows the same pattern as approval-message-composer.ts.
|
|
10
10
|
*/
|
|
11
|
-
import { getLogger } from "../util/logger.js";
|
|
12
11
|
import type {
|
|
13
12
|
ComposeGuardianActionMessageOptions,
|
|
14
|
-
GuardianActionCopyGenerator,
|
|
15
13
|
GuardianActionMessageContext,
|
|
16
14
|
} from "./message-composer-types.js";
|
|
17
15
|
|
|
@@ -21,21 +19,6 @@ export type {
|
|
|
21
19
|
GuardianActionMessageScenario,
|
|
22
20
|
} from "./message-composer-types.js";
|
|
23
21
|
|
|
24
|
-
const log = getLogger("guardian-action-message-composer");
|
|
25
|
-
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// Constants (exported for the daemon-injected generator implementation)
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
|
|
30
|
-
export const GUARDIAN_ACTION_COPY_TIMEOUT_MS = 4_000;
|
|
31
|
-
export const GUARDIAN_ACTION_COPY_MAX_TOKENS = 200;
|
|
32
|
-
export const GUARDIAN_ACTION_COPY_SYSTEM_PROMPT =
|
|
33
|
-
"You are an assistant writing one user-facing message about a guardian action in a voice call scenario. " +
|
|
34
|
-
"Keep it concise, natural, and conversational. Preserve factual details exactly. " +
|
|
35
|
-
"These messages are spoken aloud, so use a warm, human tone. " +
|
|
36
|
-
"Do not mention internal systems, scenario IDs, or technical details. " +
|
|
37
|
-
"Return plain text only.";
|
|
38
|
-
|
|
39
22
|
// ---------------------------------------------------------------------------
|
|
40
23
|
// Public API
|
|
41
24
|
// ---------------------------------------------------------------------------
|
|
@@ -51,7 +34,6 @@ export const GUARDIAN_ACTION_COPY_SYSTEM_PROMPT =
|
|
|
51
34
|
export async function composeGuardianActionMessageGenerative(
|
|
52
35
|
context: GuardianActionMessageContext,
|
|
53
36
|
options: ComposeGuardianActionMessageOptions = {},
|
|
54
|
-
generator?: GuardianActionCopyGenerator,
|
|
55
37
|
): Promise<string> {
|
|
56
38
|
const fallbackText =
|
|
57
39
|
options.fallbackText?.trim() || getGuardianActionFallbackMessage(context);
|
|
@@ -60,45 +42,9 @@ export async function composeGuardianActionMessageGenerative(
|
|
|
60
42
|
return fallbackText;
|
|
61
43
|
}
|
|
62
44
|
|
|
63
|
-
if (generator) {
|
|
64
|
-
try {
|
|
65
|
-
const generated = await generator(context, options);
|
|
66
|
-
if (generated) return generated;
|
|
67
|
-
} catch (err) {
|
|
68
|
-
log.warn(
|
|
69
|
-
{ err, scenario: context.scenario },
|
|
70
|
-
"Failed to generate guardian action copy, using fallback",
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
45
|
return fallbackText;
|
|
76
46
|
}
|
|
77
47
|
|
|
78
|
-
/** @internal Exported for use by the daemon-injected generator implementation. */
|
|
79
|
-
export function buildGuardianActionGenerationPrompt(
|
|
80
|
-
context: GuardianActionMessageContext,
|
|
81
|
-
fallbackText: string,
|
|
82
|
-
requiredKeywords: string[] | undefined,
|
|
83
|
-
): string {
|
|
84
|
-
const keywordClause =
|
|
85
|
-
requiredKeywords && requiredKeywords.length > 0
|
|
86
|
-
? `Required words to include (as standalone words): ${requiredKeywords.join(
|
|
87
|
-
", ",
|
|
88
|
-
)}.\n`
|
|
89
|
-
: "";
|
|
90
|
-
return [
|
|
91
|
-
"Rewrite the following guardian action message as a natural, conversational reply.",
|
|
92
|
-
"These messages are for voice call scenarios and may be spoken aloud.",
|
|
93
|
-
"Keep the same concrete facts and next-step guidance.",
|
|
94
|
-
keywordClause,
|
|
95
|
-
`Context JSON: ${JSON.stringify(context)}`,
|
|
96
|
-
`Fallback message: ${fallbackText}`,
|
|
97
|
-
]
|
|
98
|
-
.filter(Boolean)
|
|
99
|
-
.join("\n\n");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
48
|
/** @internal Exported for use by the daemon-injected generator implementation. */
|
|
103
49
|
export function includesRequiredKeywords(
|
|
104
50
|
text: string,
|
|
@@ -7,10 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ServerWebSocket } from "bun";
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
startGuardianActionSweep,
|
|
12
|
-
stopGuardianActionSweep,
|
|
13
|
-
} from "../calls/guardian-action-sweep.js";
|
|
14
10
|
import {
|
|
15
11
|
activeMediaStreamSessions,
|
|
16
12
|
MediaStreamCallSession,
|
|
@@ -29,7 +25,6 @@ import { isHttpAuthDisabled } from "../config/env.js";
|
|
|
29
25
|
import { getIsPlatform } from "../config/env-registry.js";
|
|
30
26
|
import { getConfig } from "../config/loader.js";
|
|
31
27
|
import { createApprovalCopyGenerator } from "../daemon/approval-generators.js";
|
|
32
|
-
import { createGuardianActionCopyGenerator } from "../daemon/guardian-action-generators.js";
|
|
33
28
|
import { processMessage } from "../daemon/process-message.js";
|
|
34
29
|
import { createLiveVoiceSession } from "../live-voice/live-voice-session.js";
|
|
35
30
|
import { LiveVoiceSessionManager } from "../live-voice/live-voice-session-manager.js";
|
|
@@ -61,11 +56,11 @@ import {
|
|
|
61
56
|
} from "./middleware/auth.js";
|
|
62
57
|
import { withErrorHandling } from "./middleware/error-handler.js";
|
|
63
58
|
import {
|
|
64
|
-
apiRateLimiter,
|
|
65
59
|
extractClientIp,
|
|
66
60
|
ipRateLimiter,
|
|
67
61
|
rateLimitHeaders,
|
|
68
62
|
rateLimitResponse,
|
|
63
|
+
selectAuthenticatedRateLimiter,
|
|
69
64
|
} from "./middleware/rate-limiter.js";
|
|
70
65
|
import { withRequestLogging } from "./middleware/request-logger.js";
|
|
71
66
|
import {
|
|
@@ -99,7 +94,6 @@ export { isPrivateAddress } from "./middleware/auth.js";
|
|
|
99
94
|
|
|
100
95
|
import type {
|
|
101
96
|
ApprovalCopyGenerator,
|
|
102
|
-
GuardianActionCopyGenerator,
|
|
103
97
|
RuntimeHttpServerOptions,
|
|
104
98
|
} from "./http-types.js";
|
|
105
99
|
|
|
@@ -162,7 +156,6 @@ export class RuntimeHttpServer {
|
|
|
162
156
|
private hostname: string;
|
|
163
157
|
|
|
164
158
|
private readonly approvalCopyGenerator: ApprovalCopyGenerator;
|
|
165
|
-
private readonly guardianActionCopyGenerator: GuardianActionCopyGenerator;
|
|
166
159
|
private retrySweepTimer: ReturnType<typeof setInterval> | null = null;
|
|
167
160
|
private sweepInProgress = false;
|
|
168
161
|
|
|
@@ -174,7 +167,6 @@ export class RuntimeHttpServer {
|
|
|
174
167
|
this.hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
175
168
|
|
|
176
169
|
this.approvalCopyGenerator = createApprovalCopyGenerator();
|
|
177
|
-
this.guardianActionCopyGenerator = createGuardianActionCopyGenerator();
|
|
178
170
|
this.liveVoiceSessionManager = new LiveVoiceSessionManager({
|
|
179
171
|
createSession: (context) => createLiveVoiceSession(context),
|
|
180
172
|
});
|
|
@@ -486,9 +478,6 @@ export class RuntimeHttpServer {
|
|
|
486
478
|
startGuardianExpirySweep(this.approvalCopyGenerator);
|
|
487
479
|
log.info("Guardian approval expiry sweep started");
|
|
488
480
|
|
|
489
|
-
startGuardianActionSweep(this.guardianActionCopyGenerator);
|
|
490
|
-
log.info("Guardian action expiry sweep started");
|
|
491
|
-
|
|
492
481
|
startCanonicalGuardianExpirySweep();
|
|
493
482
|
log.info("Canonical guardian request expiry sweep started");
|
|
494
483
|
|
|
@@ -498,7 +487,6 @@ export class RuntimeHttpServer {
|
|
|
498
487
|
|
|
499
488
|
async stop(): Promise<void> {
|
|
500
489
|
stopGuardianExpirySweep();
|
|
501
|
-
stopGuardianActionSweep();
|
|
502
490
|
stopCanonicalGuardianExpirySweep();
|
|
503
491
|
stopInferenceProfileSessionReaper();
|
|
504
492
|
if (this.retrySweepTimer) {
|
|
@@ -702,7 +690,11 @@ export class RuntimeHttpServer {
|
|
|
702
690
|
if (!isHttpAuthDisabled()) {
|
|
703
691
|
const clientIp = extractClientIp(req, server);
|
|
704
692
|
const token = extractBearerToken(req);
|
|
705
|
-
|
|
693
|
+
// Authenticated loopback clients (desktop app, CLI — anything on the
|
|
694
|
+
// daemon's own host) get a higher budget than remote clients.
|
|
695
|
+
const limiter = token
|
|
696
|
+
? selectAuthenticatedRateLimiter(clientIp)
|
|
697
|
+
: ipRateLimiter;
|
|
706
698
|
const limiterKind = token ? "authenticated" : "unauthenticated";
|
|
707
699
|
const result = limiter.check(clientIp, path);
|
|
708
700
|
if (!result.allowed) {
|
|
@@ -22,7 +22,6 @@ import type { AssistantEventHub } from "./assistant-event-hub.js";
|
|
|
22
22
|
export type {
|
|
23
23
|
ApprovalCopyGenerator,
|
|
24
24
|
ComposeApprovalMessageGenerativeOptions,
|
|
25
|
-
GuardianActionCopyGenerator,
|
|
26
25
|
} from "./message-composer-types.js";
|
|
27
26
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
28
27
|
|
|
@@ -123,12 +123,3 @@ export interface ComposeGuardianActionMessageOptions {
|
|
|
123
123
|
maxTokens?: number;
|
|
124
124
|
timeoutMs?: number;
|
|
125
125
|
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Daemon-injected function that generates guardian action copy using a provider.
|
|
129
|
-
* Returns generated text or `null` on failure (caller falls back to deterministic text).
|
|
130
|
-
*/
|
|
131
|
-
export type GuardianActionCopyGenerator = (
|
|
132
|
-
context: GuardianActionMessageContext,
|
|
133
|
-
options?: ComposeGuardianActionMessageOptions,
|
|
134
|
-
) => Promise<string | null>;
|