@vellumai/assistant 0.9.1-staging.1 → 0.10.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/docs/activation-funnel-telemetry.md +24 -18
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
- package/openapi.yaml +852 -15
- package/package.json +1 -1
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +5 -0
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +150 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +33 -33
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +26 -0
- package/src/api/responses/home.ts +26 -0
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +181 -78
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +16 -11
- package/src/daemon/conversation.ts +64 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/index.ts +27 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +35 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +49 -21
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +6 -22
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +40 -7
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +11 -4
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +1 -2
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -3
- package/src/workflows/run-manager.ts +64 -0
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-time enrichment of history tool calls with in-flight question context.
|
|
3
|
+
*
|
|
4
|
+
* `pendingQuestion` — the outstanding `ask_question` prompt for a tool call
|
|
5
|
+
* still awaiting a user answer — is read from the in-memory
|
|
6
|
+
* `pending-interactions` registry (the authoritative store of unresolved
|
|
7
|
+
* prompts) and stamped onto its tool call so the web/API clients can restore
|
|
8
|
+
* the same question card on a cold reconnect (or a history reopen after the
|
|
9
|
+
* live event buffer has aged out) that the live `question_request` SSE stream
|
|
10
|
+
* would have produced. It appears only while the prompt is genuinely
|
|
11
|
+
* outstanding. Mirrors the confirmation enrichment in
|
|
12
|
+
* `tool-call-confirmation-enrichment.ts`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { QuestionEntry } from "../../api/events/question-request.js";
|
|
16
|
+
import type { ConversationMessageToolCall } from "../../api/responses/conversation-message.js";
|
|
17
|
+
import { getByConversation } from "../pending-interactions.js";
|
|
18
|
+
|
|
19
|
+
/** A pending question matched to the tool call it prompts for, keyed by `toolUseId`. */
|
|
20
|
+
interface PendingQuestionMatch {
|
|
21
|
+
requestId: string;
|
|
22
|
+
entries: QuestionEntry[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build the `toolUseId → pending question` lookup for a conversation from the
|
|
27
|
+
* registry. Only question interactions that carry both a `toolUseId` and
|
|
28
|
+
* `questionDetails` can be stamped onto a wire tool call.
|
|
29
|
+
*/
|
|
30
|
+
export function collectPendingQuestions(
|
|
31
|
+
conversationId: string,
|
|
32
|
+
): Map<string, PendingQuestionMatch> {
|
|
33
|
+
const byToolUseId = new Map<string, PendingQuestionMatch>();
|
|
34
|
+
for (const interaction of getByConversation(conversationId)) {
|
|
35
|
+
if (
|
|
36
|
+
interaction.kind === "question" &&
|
|
37
|
+
interaction.questionDetails &&
|
|
38
|
+
interaction.toolUseId
|
|
39
|
+
) {
|
|
40
|
+
byToolUseId.set(interaction.toolUseId, {
|
|
41
|
+
requestId: interaction.requestId,
|
|
42
|
+
entries: interaction.questionDetails.entries,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return byToolUseId;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Layer any outstanding `pendingQuestion` onto a message's rendered tool calls.
|
|
51
|
+
* Returns a new array; tool calls without a match are returned unchanged.
|
|
52
|
+
*/
|
|
53
|
+
export function enrichToolCallsWithQuestion(
|
|
54
|
+
toolCalls: ConversationMessageToolCall[],
|
|
55
|
+
opts: { pendingQuestions: ReadonlyMap<string, PendingQuestionMatch> },
|
|
56
|
+
): ConversationMessageToolCall[] {
|
|
57
|
+
if (opts.pendingQuestions.size === 0) return toolCalls;
|
|
58
|
+
return toolCalls.map((tc) => {
|
|
59
|
+
const match = tc.id ? opts.pendingQuestions.get(tc.id) : undefined;
|
|
60
|
+
if (!match) return tc;
|
|
61
|
+
return {
|
|
62
|
+
...tc,
|
|
63
|
+
pendingQuestion: { requestId: match.requestId, entries: match.entries },
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
WorkflowJournalEntry,
|
|
5
|
+
WorkflowRun,
|
|
6
|
+
} from "../../workflows/journal-store.js";
|
|
4
7
|
import type { SavedWorkflowEntry } from "../../workflows/library.js";
|
|
5
8
|
import {
|
|
6
9
|
WorkflowResumeNotPossibleError,
|
|
@@ -58,6 +61,22 @@ interface FakeManager {
|
|
|
58
61
|
resume: (id: string) => { runId: string };
|
|
59
62
|
}
|
|
60
63
|
|
|
64
|
+
function makeJournalEntry(
|
|
65
|
+
overrides: Partial<WorkflowJournalEntry> = {},
|
|
66
|
+
): WorkflowJournalEntry {
|
|
67
|
+
return {
|
|
68
|
+
runId: "run-1",
|
|
69
|
+
seq: 0,
|
|
70
|
+
callHash: "call-0",
|
|
71
|
+
kind: "agent",
|
|
72
|
+
request: null,
|
|
73
|
+
result: null,
|
|
74
|
+
status: "completed",
|
|
75
|
+
createdAt: 1000,
|
|
76
|
+
...overrides,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
61
80
|
function setup(opts: {
|
|
62
81
|
runs?: WorkflowRun[];
|
|
63
82
|
saved?: SavedWorkflowEntry[];
|
|
@@ -65,6 +84,8 @@ function setup(opts: {
|
|
|
65
84
|
resume?: (id: string) => { runId: string };
|
|
66
85
|
/** Resolved auto-approve threshold for the resume posture gate. */
|
|
67
86
|
threshold?: "none" | "low" | "medium" | "high";
|
|
87
|
+
/** Journal entries returned by getJournal, keyed by run id. */
|
|
88
|
+
journal?: Record<string, WorkflowJournalEntry[]>;
|
|
68
89
|
}): {
|
|
69
90
|
aborted: string[];
|
|
70
91
|
resumed: string[];
|
|
@@ -97,6 +118,7 @@ function setup(opts: {
|
|
|
97
118
|
getManager: () => manager,
|
|
98
119
|
listWorkflows: () => opts.saved ?? [],
|
|
99
120
|
getAutoApproveThreshold: async () => opts.threshold ?? "none",
|
|
121
|
+
getJournal: (runId) => opts.journal?.[runId] ?? [],
|
|
100
122
|
});
|
|
101
123
|
return { aborted, resumed, listCalls };
|
|
102
124
|
}
|
|
@@ -303,6 +325,208 @@ describe("workflow routes (happy paths)", () => {
|
|
|
303
325
|
});
|
|
304
326
|
});
|
|
305
327
|
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
// Journal projection
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
interface WireLeaf {
|
|
333
|
+
seq: number;
|
|
334
|
+
kind: string;
|
|
335
|
+
label?: string;
|
|
336
|
+
phase?: string;
|
|
337
|
+
promptSummary?: string;
|
|
338
|
+
status: string;
|
|
339
|
+
resultSummary?: string;
|
|
340
|
+
inputTokens?: number;
|
|
341
|
+
outputTokens?: number;
|
|
342
|
+
createdAt: number | null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
interface WireJournal {
|
|
346
|
+
runId: string;
|
|
347
|
+
status: string;
|
|
348
|
+
agentsSpawned: number;
|
|
349
|
+
inputTokens: number;
|
|
350
|
+
outputTokens: number;
|
|
351
|
+
leaves: WireLeaf[];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
describe("getWorkflowRunJournal", () => {
|
|
355
|
+
test("projects agent leaves with label/phase/promptSummary/resultSummary extracted", async () => {
|
|
356
|
+
setup({
|
|
357
|
+
runs: [makeRun({ id: "run-1" })],
|
|
358
|
+
journal: {
|
|
359
|
+
"run-1": [
|
|
360
|
+
makeJournalEntry({
|
|
361
|
+
seq: 0,
|
|
362
|
+
kind: "agent",
|
|
363
|
+
request: {
|
|
364
|
+
prompt: "Investigate the failing build",
|
|
365
|
+
opts: { label: "investigate", phase: "triage" },
|
|
366
|
+
},
|
|
367
|
+
result: { summary: "found the flaky test" },
|
|
368
|
+
status: "completed",
|
|
369
|
+
inputTokens: 120,
|
|
370
|
+
outputTokens: 45,
|
|
371
|
+
}),
|
|
372
|
+
],
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
const result = (await route("getWorkflowRunJournal").handler({
|
|
376
|
+
pathParams: { id: "run-1" },
|
|
377
|
+
})) as WireJournal;
|
|
378
|
+
|
|
379
|
+
expect(result.runId).toBe("run-1");
|
|
380
|
+
expect(result.status).toBe("running");
|
|
381
|
+
expect(result.agentsSpawned).toBe(2);
|
|
382
|
+
expect(result.leaves.map((l) => l.seq)).toEqual([0]);
|
|
383
|
+
|
|
384
|
+
const first = result.leaves[0];
|
|
385
|
+
expect(first.kind).toBe("agent");
|
|
386
|
+
expect(first.label).toBe("investigate");
|
|
387
|
+
expect(first.phase).toBe("triage");
|
|
388
|
+
expect(first.promptSummary).toBe("Investigate the failing build");
|
|
389
|
+
expect(first.resultSummary).toContain("found the flaky test");
|
|
390
|
+
// Per-leaf token usage is projected onto the wire leaf.
|
|
391
|
+
expect(first.inputTokens).toBe(120);
|
|
392
|
+
expect(first.outputTokens).toBe(45);
|
|
393
|
+
// The bulky raw request/result payloads are dropped.
|
|
394
|
+
expect(first).not.toHaveProperty("request");
|
|
395
|
+
expect(first).not.toHaveProperty("result");
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("omits per-leaf token fields when the journal entry carries none", async () => {
|
|
399
|
+
setup({
|
|
400
|
+
runs: [makeRun({ id: "run-1" })],
|
|
401
|
+
journal: {
|
|
402
|
+
"run-1": [
|
|
403
|
+
makeJournalEntry({
|
|
404
|
+
seq: 0,
|
|
405
|
+
kind: "agent",
|
|
406
|
+
request: { prompt: "no tokens recorded", opts: {} },
|
|
407
|
+
status: "completed",
|
|
408
|
+
}),
|
|
409
|
+
],
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
const result = (await route("getWorkflowRunJournal").handler({
|
|
413
|
+
pathParams: { id: "run-1" },
|
|
414
|
+
})) as WireJournal;
|
|
415
|
+
|
|
416
|
+
const leaf = result.leaves[0];
|
|
417
|
+
expect(leaf).not.toHaveProperty("inputTokens");
|
|
418
|
+
expect(leaf).not.toHaveProperty("outputTokens");
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
test("excludes kind:workflow entries so the backfill matches the live agent-only stream", async () => {
|
|
422
|
+
// The live `workflow_leaf_*` stream is emitted only for agent leaves; nested
|
|
423
|
+
// `workflow(name)` resolutions never reach it. The journal route must filter
|
|
424
|
+
// to agent leaves too, or a nested-workflow run renders a phantom unlabeled
|
|
425
|
+
// node on backfill and a different leaf set than live.
|
|
426
|
+
setup({
|
|
427
|
+
runs: [makeRun({ id: "run-1" })],
|
|
428
|
+
journal: {
|
|
429
|
+
"run-1": [
|
|
430
|
+
makeJournalEntry({
|
|
431
|
+
seq: 0,
|
|
432
|
+
kind: "agent",
|
|
433
|
+
request: { prompt: "first agent", opts: { label: "first" } },
|
|
434
|
+
status: "completed",
|
|
435
|
+
}),
|
|
436
|
+
makeJournalEntry({
|
|
437
|
+
seq: 1,
|
|
438
|
+
kind: "workflow",
|
|
439
|
+
request: { name: "child", args: { foo: 1 } },
|
|
440
|
+
result: "child done",
|
|
441
|
+
status: "completed",
|
|
442
|
+
}),
|
|
443
|
+
makeJournalEntry({
|
|
444
|
+
seq: 2,
|
|
445
|
+
kind: "agent",
|
|
446
|
+
request: { prompt: "second agent", opts: { label: "second" } },
|
|
447
|
+
status: "completed",
|
|
448
|
+
}),
|
|
449
|
+
],
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
const result = (await route("getWorkflowRunJournal").handler({
|
|
453
|
+
pathParams: { id: "run-1" },
|
|
454
|
+
})) as WireJournal;
|
|
455
|
+
|
|
456
|
+
// Only the agent leaves survive, in seq order; the workflow-kind entry (seq
|
|
457
|
+
// 1) is dropped entirely.
|
|
458
|
+
expect(result.leaves.map((l) => l.seq)).toEqual([0, 2]);
|
|
459
|
+
expect(result.leaves.every((l) => l.kind === "agent")).toBe(true);
|
|
460
|
+
expect(result.leaves.map((l) => l.label)).toEqual(["first", "second"]);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
test("surfaces a failed leaf's { error } result as resultSummary", async () => {
|
|
464
|
+
setup({
|
|
465
|
+
runs: [makeRun({ id: "run-1" })],
|
|
466
|
+
journal: {
|
|
467
|
+
"run-1": [
|
|
468
|
+
makeJournalEntry({
|
|
469
|
+
seq: 0,
|
|
470
|
+
status: "failed",
|
|
471
|
+
request: { prompt: "do the thing", opts: {} },
|
|
472
|
+
result: { error: "agent crashed: out of tokens" },
|
|
473
|
+
}),
|
|
474
|
+
],
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
const result = (await route("getWorkflowRunJournal").handler({
|
|
478
|
+
pathParams: { id: "run-1" },
|
|
479
|
+
})) as WireJournal;
|
|
480
|
+
|
|
481
|
+
expect(result.leaves[0].status).toBe("failed");
|
|
482
|
+
expect(result.leaves[0].resultSummary).toContain(
|
|
483
|
+
"agent crashed: out of tokens",
|
|
484
|
+
);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test("truncates a long prompt/result to ~200 chars", async () => {
|
|
488
|
+
const longPrompt = "x".repeat(500);
|
|
489
|
+
const longResult = "y".repeat(500);
|
|
490
|
+
setup({
|
|
491
|
+
runs: [makeRun({ id: "run-1" })],
|
|
492
|
+
journal: {
|
|
493
|
+
"run-1": [
|
|
494
|
+
makeJournalEntry({
|
|
495
|
+
seq: 0,
|
|
496
|
+
request: { prompt: longPrompt, opts: {} },
|
|
497
|
+
result: longResult,
|
|
498
|
+
}),
|
|
499
|
+
],
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
const result = (await route("getWorkflowRunJournal").handler({
|
|
503
|
+
pathParams: { id: "run-1" },
|
|
504
|
+
})) as WireJournal;
|
|
505
|
+
|
|
506
|
+
const leaf = result.leaves[0];
|
|
507
|
+
expect(leaf.promptSummary!.length).toBeLessThanOrEqual(201);
|
|
508
|
+
expect(leaf.promptSummary!.startsWith("x")).toBe(true);
|
|
509
|
+
// A truncated summary ends with the ellipsis marker.
|
|
510
|
+
expect(leaf.promptSummary!.endsWith("…")).toBe(true);
|
|
511
|
+
expect(leaf.resultSummary!.length).toBeLessThanOrEqual(201);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test("returns an empty leaf list for a run with no journal entries", async () => {
|
|
515
|
+
setup({ runs: [makeRun({ id: "run-1" })] });
|
|
516
|
+
const result = (await route("getWorkflowRunJournal").handler({
|
|
517
|
+
pathParams: { id: "run-1" },
|
|
518
|
+
})) as WireJournal;
|
|
519
|
+
expect(result.leaves).toEqual([]);
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
test("throws NotFoundError for an unknown id", () => {
|
|
523
|
+
setup({ runs: [] });
|
|
524
|
+
expect(() =>
|
|
525
|
+
route("getWorkflowRunJournal").handler({ pathParams: { id: "nope" } }),
|
|
526
|
+
).toThrow(NotFoundError);
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
|
|
306
530
|
// ---------------------------------------------------------------------------
|
|
307
531
|
// Unknown run → 404
|
|
308
532
|
// ---------------------------------------------------------------------------
|
|
@@ -12,7 +12,11 @@ import { z } from "zod";
|
|
|
12
12
|
import { getAutoApproveThreshold } from "../../permissions/gateway-threshold-reader.js";
|
|
13
13
|
import { isFullAccessThreshold } from "../../permissions/threshold.js";
|
|
14
14
|
import { manifestGrantsSideEffects } from "../../workflows/capabilities.js";
|
|
15
|
-
import
|
|
15
|
+
import {
|
|
16
|
+
getJournal,
|
|
17
|
+
type WorkflowJournalEntry,
|
|
18
|
+
type WorkflowRun,
|
|
19
|
+
} from "../../workflows/journal-store.js";
|
|
16
20
|
import { listWorkflows } from "../../workflows/library.js";
|
|
17
21
|
import {
|
|
18
22
|
getWorkflowRunManager,
|
|
@@ -40,6 +44,7 @@ export interface WorkflowRoutesDeps {
|
|
|
40
44
|
>;
|
|
41
45
|
listWorkflows: typeof listWorkflows;
|
|
42
46
|
getAutoApproveThreshold: typeof getAutoApproveThreshold;
|
|
47
|
+
getJournal: typeof getJournal;
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
function defaultDeps(): WorkflowRoutesDeps {
|
|
@@ -47,6 +52,7 @@ function defaultDeps(): WorkflowRoutesDeps {
|
|
|
47
52
|
getManager: getWorkflowRunManager,
|
|
48
53
|
listWorkflows,
|
|
49
54
|
getAutoApproveThreshold,
|
|
55
|
+
getJournal,
|
|
50
56
|
};
|
|
51
57
|
}
|
|
52
58
|
|
|
@@ -93,6 +99,29 @@ const savedWorkflowSchema = z.object({
|
|
|
93
99
|
path: z.string(),
|
|
94
100
|
});
|
|
95
101
|
|
|
102
|
+
const workflowLeafSchema = z.object({
|
|
103
|
+
seq: z.number(),
|
|
104
|
+
kind: z.enum(["agent", "workflow"]),
|
|
105
|
+
label: z.string().optional(),
|
|
106
|
+
phase: z.string().optional(),
|
|
107
|
+
promptSummary: z.string().optional(),
|
|
108
|
+
status: z.string(),
|
|
109
|
+
resultSummary: z.string().optional(),
|
|
110
|
+
inputTokens: z.number().optional(),
|
|
111
|
+
outputTokens: z.number().optional(),
|
|
112
|
+
createdAt: z.number().nullable(),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const workflowJournalSchema = z.object({
|
|
116
|
+
runId: z.string(),
|
|
117
|
+
status: z.enum(VALID_RUN_STATUSES).optional(),
|
|
118
|
+
agentsSpawned: z.number().optional(),
|
|
119
|
+
inputTokens: z.number().optional(),
|
|
120
|
+
outputTokens: z.number().optional(),
|
|
121
|
+
phase: z.string().optional(),
|
|
122
|
+
leaves: z.array(workflowLeafSchema),
|
|
123
|
+
});
|
|
124
|
+
|
|
96
125
|
// ---------------------------------------------------------------------------
|
|
97
126
|
// Handlers (transport-agnostic)
|
|
98
127
|
// ---------------------------------------------------------------------------
|
|
@@ -122,6 +151,64 @@ export function toWireRun(run: WorkflowRun): WorkflowRunWire {
|
|
|
122
151
|
};
|
|
123
152
|
}
|
|
124
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Render an arbitrary journal value (prompt, result, `{ error }`) into a short
|
|
156
|
+
* single-string preview, truncating to `max` characters. Objects are
|
|
157
|
+
* JSON-stringified so a structured result still produces a readable summary.
|
|
158
|
+
*/
|
|
159
|
+
function summarize(value: unknown, max = 200): string | undefined {
|
|
160
|
+
if (value === undefined || value === null) return undefined;
|
|
161
|
+
let text: string;
|
|
162
|
+
if (typeof value === "string") {
|
|
163
|
+
text = value;
|
|
164
|
+
} else {
|
|
165
|
+
try {
|
|
166
|
+
text = JSON.stringify(value);
|
|
167
|
+
} catch {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Project a journal entry into the bounded leaf wire shape, dropping the bulky
|
|
176
|
+
* raw `request`/`result` payloads in favor of short summaries. The `request`
|
|
177
|
+
* and `result` columns are `unknown`, so read their fields defensively.
|
|
178
|
+
*/
|
|
179
|
+
export function toWireLeaf(
|
|
180
|
+
entry: WorkflowJournalEntry,
|
|
181
|
+
): z.infer<typeof workflowLeafSchema> {
|
|
182
|
+
const request =
|
|
183
|
+
entry.request && typeof entry.request === "object"
|
|
184
|
+
? (entry.request as Record<string, unknown>)
|
|
185
|
+
: undefined;
|
|
186
|
+
const opts =
|
|
187
|
+
request?.opts && typeof request.opts === "object"
|
|
188
|
+
? (request.opts as Record<string, unknown>)
|
|
189
|
+
: undefined;
|
|
190
|
+
const label = typeof opts?.label === "string" ? opts.label : undefined;
|
|
191
|
+
const phase = typeof opts?.phase === "string" ? opts.phase : undefined;
|
|
192
|
+
const promptSummary = summarize(request?.prompt);
|
|
193
|
+
const resultSummary = summarize(entry.result);
|
|
194
|
+
return {
|
|
195
|
+
seq: entry.seq,
|
|
196
|
+
kind: entry.kind,
|
|
197
|
+
...(label !== undefined ? { label } : {}),
|
|
198
|
+
...(phase !== undefined ? { phase } : {}),
|
|
199
|
+
...(promptSummary !== undefined ? { promptSummary } : {}),
|
|
200
|
+
status: entry.status,
|
|
201
|
+
...(resultSummary !== undefined ? { resultSummary } : {}),
|
|
202
|
+
...(entry.inputTokens !== undefined
|
|
203
|
+
? { inputTokens: entry.inputTokens }
|
|
204
|
+
: {}),
|
|
205
|
+
...(entry.outputTokens !== undefined
|
|
206
|
+
? { outputTokens: entry.outputTokens }
|
|
207
|
+
: {}),
|
|
208
|
+
createdAt: entry.createdAt,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
125
212
|
function parseLimit(raw: string | undefined): number | undefined {
|
|
126
213
|
if (raw === undefined) return undefined;
|
|
127
214
|
const n = Number(raw);
|
|
@@ -156,6 +243,28 @@ function handleGetRun(id: string) {
|
|
|
156
243
|
return toWireRun(run);
|
|
157
244
|
}
|
|
158
245
|
|
|
246
|
+
function handleGetRunJournal(id: string) {
|
|
247
|
+
const run = deps.getManager().status(id);
|
|
248
|
+
if (!run) {
|
|
249
|
+
throw new NotFoundError(`Workflow run ${id} not found`);
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
runId: run.id,
|
|
253
|
+
status: run.status,
|
|
254
|
+
agentsSpawned: run.agentsSpawned,
|
|
255
|
+
inputTokens: run.inputTokens,
|
|
256
|
+
outputTokens: run.outputTokens,
|
|
257
|
+
// Only agent leaves; the live `workflow_leaf_*` stream is emitted solely for
|
|
258
|
+
// agent leaves (nested workflow resolutions never reach it), so the
|
|
259
|
+
// backfilled set must match. `kind: "workflow"` entries carry no label and
|
|
260
|
+
// would render as phantom unlabeled nodes.
|
|
261
|
+
leaves: deps
|
|
262
|
+
.getJournal(id)
|
|
263
|
+
.filter((entry) => entry.kind === "agent")
|
|
264
|
+
.map(toWireLeaf),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
159
268
|
function handleAbortRun(id: string) {
|
|
160
269
|
// status() is the source of truth for existence; abort() itself is a no-op
|
|
161
270
|
// for unknown/finished runs, so 404 on an unknown id is surfaced here.
|
|
@@ -271,6 +380,27 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
271
380
|
additionalResponses: { "404": { description: "Run not found" } },
|
|
272
381
|
handler: ({ pathParams }: RouteHandlerArgs) => handleGetRun(pathParams!.id),
|
|
273
382
|
},
|
|
383
|
+
{
|
|
384
|
+
operationId: "getWorkflowRunJournal",
|
|
385
|
+
endpoint: "workflows/runs/:id/journal",
|
|
386
|
+
method: "GET",
|
|
387
|
+
// Unlike the run list/status routes (settings metadata), the journal returns
|
|
388
|
+
// per-leaf prompt/result summaries — conversation/work-product content — so it
|
|
389
|
+
// requires `chat.read`, matching the subagent-detail route (the analogous
|
|
390
|
+
// content surface), not the `settings.read` used by the management routes.
|
|
391
|
+
policy: {
|
|
392
|
+
requiredScopes: ["chat.read"],
|
|
393
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
394
|
+
},
|
|
395
|
+
summary: "Get workflow run journal",
|
|
396
|
+
description:
|
|
397
|
+
"Return a workflow run's leaf journal as bounded per-leaf summaries (one entry per finished leaf).",
|
|
398
|
+
tags: ["workflows"],
|
|
399
|
+
responseBody: workflowJournalSchema,
|
|
400
|
+
additionalResponses: { "404": { description: "Run not found" } },
|
|
401
|
+
handler: ({ pathParams }: RouteHandlerArgs) =>
|
|
402
|
+
handleGetRunJournal(pathParams!.id),
|
|
403
|
+
},
|
|
274
404
|
{
|
|
275
405
|
operationId: "abortWorkflowRun",
|
|
276
406
|
endpoint: "workflows/runs/:id/abort",
|
|
@@ -13,12 +13,14 @@
|
|
|
13
13
|
|
|
14
14
|
import type { ChannelId } from "../channels/types.js";
|
|
15
15
|
import {
|
|
16
|
-
createCanonicalGuardianDelivery,
|
|
17
16
|
createCanonicalGuardianRequest,
|
|
18
17
|
listCanonicalGuardianRequests,
|
|
19
18
|
} from "../memory/canonical-guardian-store.js";
|
|
19
|
+
import {
|
|
20
|
+
recordApprovalCardDelivery,
|
|
21
|
+
recordGuardianRequestDeliveries,
|
|
22
|
+
} from "../notifications/canonical-delivery-recorder.js";
|
|
20
23
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
21
|
-
import type { NotificationSourceChannel } from "../notifications/signal.js";
|
|
22
24
|
import { getLogger } from "../util/logger.js";
|
|
23
25
|
import { getGuardianBinding } from "./channel-verification-service.js";
|
|
24
26
|
import { GUARDIAN_APPROVAL_TTL_MS } from "./routes/channel-route-shared.js";
|
|
@@ -140,12 +142,16 @@ export function createOrReuseToolGrantRequest(
|
|
|
140
142
|
canonicalRequest.requestCode ??
|
|
141
143
|
canonicalRequest.id.slice(0, 6).toUpperCase();
|
|
142
144
|
|
|
145
|
+
// The vellum delivery row is created up front in onConversationCreated so the
|
|
146
|
+
// in-app client sees it immediately; the post-resolve recorder reuses it.
|
|
147
|
+
let vellumDeliveryId: string | undefined;
|
|
148
|
+
|
|
143
149
|
// Emit notification so guardian is alerted. Uses 'guardian.question' as
|
|
144
150
|
// sourceEventName so that existing request-code guidance in the notification
|
|
145
151
|
// pipeline is preserved.
|
|
146
152
|
const signalPromise = emitNotificationSignal({
|
|
147
153
|
sourceEventName: "guardian.question",
|
|
148
|
-
sourceChannel
|
|
154
|
+
sourceChannel,
|
|
149
155
|
sourceContextId: conversationId,
|
|
150
156
|
requiresConversation: true,
|
|
151
157
|
attentionHints: {
|
|
@@ -167,25 +173,21 @@ export function createOrReuseToolGrantRequest(
|
|
|
167
173
|
},
|
|
168
174
|
dedupeKey: `tool-grant-request:${canonicalRequest.id}`,
|
|
169
175
|
onConversationCreated: (info) => {
|
|
170
|
-
|
|
176
|
+
vellumDeliveryId = recordApprovalCardDelivery({
|
|
171
177
|
requestId: canonicalRequest.id,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
});
|
|
178
|
+
channel: "vellum",
|
|
179
|
+
conversationId: info.conversationId,
|
|
180
|
+
})?.id;
|
|
175
181
|
},
|
|
176
182
|
});
|
|
177
183
|
|
|
178
184
|
// Record deliveries from the notification pipeline results (fire-and-forget).
|
|
179
185
|
void signalPromise.then((signalResult) => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
destinationChatId:
|
|
186
|
-
result.destination.length > 0 ? result.destination : undefined,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
186
|
+
recordGuardianRequestDeliveries({
|
|
187
|
+
requestId: canonicalRequest.id,
|
|
188
|
+
deliveryResults: signalResult.deliveryResults,
|
|
189
|
+
vellumDeliveryId,
|
|
190
|
+
});
|
|
189
191
|
});
|
|
190
192
|
|
|
191
193
|
log.info(
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type ResolveActorTrustInput,
|
|
17
17
|
toTrustContext,
|
|
18
18
|
} from "./actor-trust-resolver.js";
|
|
19
|
+
import { resolveCapabilities } from "./capabilities.js";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Resolve route-level trust context from canonical identity state.
|
|
@@ -64,14 +65,15 @@ export interface RoutingState {
|
|
|
64
65
|
* Compute the routing state for a channel actor turn.
|
|
65
66
|
*
|
|
66
67
|
* Guardian actors are always interactive (they self-approve).
|
|
67
|
-
* Trusted contacts are only interactive when a guardian
|
|
68
|
-
* to receive approval notifications. Unknown actors are
|
|
68
|
+
* Trusted and unverified contacts are only interactive when a guardian
|
|
69
|
+
* binding exists to receive approval notifications. Unknown actors are
|
|
70
|
+
* never interactive.
|
|
69
71
|
*/
|
|
70
72
|
export function resolveRoutingState(
|
|
71
73
|
ctx: Pick<TrustContext, "trustClass" | "guardianExternalUserId">,
|
|
72
74
|
): RoutingState {
|
|
75
|
+
const caps = resolveCapabilities(ctx.trustClass);
|
|
73
76
|
const isGuardian = ctx.trustClass === "guardian";
|
|
74
|
-
const isTrustedContact = ctx.trustClass === "trusted_contact";
|
|
75
77
|
|
|
76
78
|
// Guardians self-approve — they are always interactive and route-resolvable.
|
|
77
79
|
if (isGuardian) {
|
|
@@ -82,12 +84,13 @@ export function resolveRoutingState(
|
|
|
82
84
|
};
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
//
|
|
87
|
+
// Identity-known non-guardian contacts (trusted_contact /
|
|
88
|
+
// unverified_contact) can be interactive only if a guardian destination
|
|
86
89
|
// exists. The guardian binding populates guardianExternalUserId during
|
|
87
90
|
// trust resolution; its presence means there is a verified guardian
|
|
88
91
|
// to route approval notifications to.
|
|
89
92
|
const guardianRouteResolvable = !!ctx.guardianExternalUserId;
|
|
90
|
-
if (
|
|
93
|
+
if (caps.mayBeInteractive) {
|
|
91
94
|
return {
|
|
92
95
|
canBeInteractive: true,
|
|
93
96
|
guardianRouteResolvable,
|
|
@@ -158,7 +158,7 @@ export function createSchedule(params: {
|
|
|
158
158
|
const routingIntent = params.routingIntent ?? "all_channels";
|
|
159
159
|
const routingHints = params.routingHints ?? {};
|
|
160
160
|
const quiet = params.quiet ?? false;
|
|
161
|
-
const reuseConversation = params.reuseConversation ??
|
|
161
|
+
const reuseConversation = params.reuseConversation ?? false;
|
|
162
162
|
const maxRetries = params.maxRetries ?? 3;
|
|
163
163
|
const retryBackoffMs = params.retryBackoffMs ?? 60000;
|
|
164
164
|
const timeoutMs = params.timeoutMs ?? null;
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
8
8
|
|
|
9
9
|
export interface ScheduleMessageOptions {
|
|
10
|
-
trustClass?:
|
|
10
|
+
trustClass?:
|
|
11
|
+
| "guardian"
|
|
12
|
+
| "trusted_contact"
|
|
13
|
+
| "unverified_contact"
|
|
14
|
+
| "unknown";
|
|
11
15
|
taskRunId?: string;
|
|
12
16
|
/**
|
|
13
17
|
* Optional LLM call-site identifier propagated to the per-call provider
|
|
@@ -53,6 +53,7 @@ describe("getProviderKeyAsync env-var fallback (regression #27126)", () => {
|
|
|
53
53
|
"BRAVE_API_KEY",
|
|
54
54
|
"PERPLEXITY_API_KEY",
|
|
55
55
|
"TAVILY_API_KEY",
|
|
56
|
+
"FIRECRAWL_API_KEY",
|
|
56
57
|
"ANTHROPIC_API_KEY",
|
|
57
58
|
"OPENAI_API_KEY",
|
|
58
59
|
];
|
|
@@ -103,6 +104,11 @@ describe("getProviderKeyAsync env-var fallback (regression #27126)", () => {
|
|
|
103
104
|
expect(await getProviderKeyAsync("tavily")).toBe("tavily-env-test");
|
|
104
105
|
});
|
|
105
106
|
|
|
107
|
+
test("returns FIRECRAWL_API_KEY from process.env when secure store is empty", async () => {
|
|
108
|
+
process.env.FIRECRAWL_API_KEY = "fc-env-test";
|
|
109
|
+
expect(await getProviderKeyAsync("firecrawl")).toBe("fc-env-test");
|
|
110
|
+
});
|
|
111
|
+
|
|
106
112
|
test("returns ANTHROPIC_API_KEY from process.env when secure store is empty (LLM regression)", async () => {
|
|
107
113
|
process.env.ANTHROPIC_API_KEY = "anthropic-env-test";
|
|
108
114
|
expect(await getProviderKeyAsync("anthropic")).toBe("anthropic-env-test");
|