@vellumai/assistant 0.9.0 → 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/ARCHITECTURE.md +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- 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 +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +976 -63
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- 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 +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- 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__/checker.test.ts +0 -3
- 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 +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- 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-inference-profile.test.ts +22 -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-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- 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 +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- 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__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- 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 +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- 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__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -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__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +51 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- 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 +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -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__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- 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__/tool-executor.test.ts +0 -79
- 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-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -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 +49 -29
- package/src/api/README.md +6 -6
- 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 +28 -4
- package/src/api/responses/home.ts +26 -4
- 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 +183 -80
- 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-access-wait.ts +1 -1
- 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/calls/voice-session-bridge.ts +2 -2
- 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 +197 -4
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- 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/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -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/elevenlabs.ts +0 -1
- 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/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- 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-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +66 -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 +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.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/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- 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/home/relationship-state.ts +2 -4
- 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 +229 -401
- 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/bookmark-crud.ts +1 -2
- 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 +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- 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 +72 -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 +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- 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/adapter.ts +1 -1
- 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/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- 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 +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- 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/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- 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 +60 -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 +129 -9
- 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 +144 -11
- 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/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- 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/AGENTS.md +0 -1
- 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 +59 -63
- 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/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- 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 +9 -25
- 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 +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- 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-compaction-routes.ts +1 -1
- 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 +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- 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/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- 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 +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- 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 +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- 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 +17 -4
- package/src/subagent/types.ts +6 -0
- 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/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- 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-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- 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 -28
- package/src/workflows/run-manager.ts +66 -24
- 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/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
WorkflowJournalEntry,
|
|
5
|
+
WorkflowRun,
|
|
6
|
+
} from "../../workflows/journal-store.js";
|
|
5
7
|
import type { SavedWorkflowEntry } from "../../workflows/library.js";
|
|
6
8
|
import {
|
|
7
9
|
WorkflowResumeNotPossibleError,
|
|
@@ -59,14 +61,31 @@ interface FakeManager {
|
|
|
59
61
|
resume: (id: string) => { runId: string };
|
|
60
62
|
}
|
|
61
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
|
+
|
|
62
80
|
function setup(opts: {
|
|
63
|
-
flagEnabled?: boolean;
|
|
64
81
|
runs?: WorkflowRun[];
|
|
65
82
|
saved?: SavedWorkflowEntry[];
|
|
66
83
|
/** Custom resume impl; defaults to a success that records the id. */
|
|
67
84
|
resume?: (id: string) => { runId: string };
|
|
68
85
|
/** Resolved auto-approve threshold for the resume posture gate. */
|
|
69
86
|
threshold?: "none" | "low" | "medium" | "high";
|
|
87
|
+
/** Journal entries returned by getJournal, keyed by run id. */
|
|
88
|
+
journal?: Record<string, WorkflowJournalEntry[]>;
|
|
70
89
|
}): {
|
|
71
90
|
aborted: string[];
|
|
72
91
|
resumed: string[];
|
|
@@ -98,9 +117,8 @@ function setup(opts: {
|
|
|
98
117
|
__setWorkflowRoutesDeps({
|
|
99
118
|
getManager: () => manager,
|
|
100
119
|
listWorkflows: () => opts.saved ?? [],
|
|
101
|
-
getConfig: () => ({}) as AssistantConfig,
|
|
102
|
-
isFlagEnabled: () => opts.flagEnabled ?? true,
|
|
103
120
|
getAutoApproveThreshold: async () => opts.threshold ?? "none",
|
|
121
|
+
getJournal: (runId) => opts.journal?.[runId] ?? [],
|
|
104
122
|
});
|
|
105
123
|
return { aborted, resumed, listCalls };
|
|
106
124
|
}
|
|
@@ -113,10 +131,9 @@ afterEach(() => {
|
|
|
113
131
|
// Happy paths
|
|
114
132
|
// ---------------------------------------------------------------------------
|
|
115
133
|
|
|
116
|
-
describe("workflow routes (
|
|
134
|
+
describe("workflow routes (happy paths)", () => {
|
|
117
135
|
beforeEach(() => {
|
|
118
136
|
setup({
|
|
119
|
-
flagEnabled: true,
|
|
120
137
|
runs: [
|
|
121
138
|
makeRun({ id: "run-1" }),
|
|
122
139
|
makeRun({ id: "run-2", status: "completed" }),
|
|
@@ -145,7 +162,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
145
162
|
|
|
146
163
|
test("listWorkflowRuns honors limit + status query params", async () => {
|
|
147
164
|
const { listCalls } = setup({
|
|
148
|
-
flagEnabled: true,
|
|
149
165
|
runs: [makeRun({ id: "run-1", status: "completed" })],
|
|
150
166
|
});
|
|
151
167
|
await route("listWorkflowRuns").handler({
|
|
@@ -163,7 +179,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
163
179
|
|
|
164
180
|
test("abortWorkflowRun aborts a known run", async () => {
|
|
165
181
|
const { aborted } = setup({
|
|
166
|
-
flagEnabled: true,
|
|
167
182
|
runs: [makeRun({ id: "run-1" })],
|
|
168
183
|
});
|
|
169
184
|
const result = (await route("abortWorkflowRun").handler({
|
|
@@ -175,7 +190,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
175
190
|
|
|
176
191
|
test("resumeWorkflowRun resumes an interrupted run", async () => {
|
|
177
192
|
const { resumed } = setup({
|
|
178
|
-
flagEnabled: true,
|
|
179
193
|
runs: [makeRun({ id: "run-1", status: "interrupted" })],
|
|
180
194
|
});
|
|
181
195
|
const result = (await route("resumeWorkflowRun").handler({
|
|
@@ -192,7 +206,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
192
206
|
// full-access posture it must refuse rather than silently bypass consent —
|
|
193
207
|
// resume() is never called.
|
|
194
208
|
const { resumed } = setup({
|
|
195
|
-
flagEnabled: true,
|
|
196
209
|
threshold: "medium",
|
|
197
210
|
runs: [
|
|
198
211
|
makeRun({
|
|
@@ -213,7 +226,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
213
226
|
// high-risk tools, so no prompt is needed — the side-effecting resume
|
|
214
227
|
// proceeds directly.
|
|
215
228
|
const { resumed } = setup({
|
|
216
|
-
flagEnabled: true,
|
|
217
229
|
threshold: "high",
|
|
218
230
|
runs: [
|
|
219
231
|
makeRun({
|
|
@@ -236,7 +248,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
236
248
|
// the object shape too — a strict parse would treat it as read-only and let
|
|
237
249
|
// the side-effecting resume through without approval.
|
|
238
250
|
const { resumed } = setup({
|
|
239
|
-
flagEnabled: true,
|
|
240
251
|
threshold: "medium",
|
|
241
252
|
runs: [
|
|
242
253
|
makeRun({
|
|
@@ -259,7 +270,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
259
270
|
// An explicit empty manifest grants no side effects, so the route resumes
|
|
260
271
|
// it directly — the gate keys on the stored manifest, not run existence.
|
|
261
272
|
const { resumed } = setup({
|
|
262
|
-
flagEnabled: true,
|
|
263
273
|
threshold: "medium",
|
|
264
274
|
runs: [
|
|
265
275
|
makeRun({
|
|
@@ -275,7 +285,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
275
285
|
|
|
276
286
|
test("resumeWorkflowRun maps a non-interrupted run to a 409 ConflictError", async () => {
|
|
277
287
|
setup({
|
|
278
|
-
flagEnabled: true,
|
|
279
288
|
runs: [makeRun({ id: "run-1", status: "completed" })],
|
|
280
289
|
resume: (id) => {
|
|
281
290
|
throw new WorkflowResumeNotPossibleError(
|
|
@@ -292,7 +301,6 @@ describe("workflow routes (flag on)", () => {
|
|
|
292
301
|
|
|
293
302
|
test("resumeWorkflowRun maps a cap error to a 429 TooManyRequestsError", async () => {
|
|
294
303
|
setup({
|
|
295
|
-
flagEnabled: true,
|
|
296
304
|
runs: [makeRun({ id: "run-1", status: "interrupted" })],
|
|
297
305
|
resume: () => {
|
|
298
306
|
throw new WorkflowRunCapError(3);
|
|
@@ -317,20 +325,222 @@ describe("workflow routes (flag on)", () => {
|
|
|
317
325
|
});
|
|
318
326
|
});
|
|
319
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
|
+
|
|
320
530
|
// ---------------------------------------------------------------------------
|
|
321
531
|
// Unknown run → 404
|
|
322
532
|
// ---------------------------------------------------------------------------
|
|
323
533
|
|
|
324
534
|
describe("workflow routes (unknown run)", () => {
|
|
325
535
|
test("getWorkflowRun throws NotFoundError for an unknown id", () => {
|
|
326
|
-
setup({
|
|
536
|
+
setup({ runs: [] });
|
|
327
537
|
expect(() =>
|
|
328
538
|
route("getWorkflowRun").handler({ pathParams: { id: "nope" } }),
|
|
329
539
|
).toThrow(NotFoundError);
|
|
330
540
|
});
|
|
331
541
|
|
|
332
542
|
test("abortWorkflowRun throws NotFoundError for an unknown id", () => {
|
|
333
|
-
const { aborted } = setup({
|
|
543
|
+
const { aborted } = setup({ runs: [] });
|
|
334
544
|
expect(() =>
|
|
335
545
|
route("abortWorkflowRun").handler({ pathParams: { id: "nope" } }),
|
|
336
546
|
).toThrow(NotFoundError);
|
|
@@ -338,35 +548,10 @@ describe("workflow routes (unknown run)", () => {
|
|
|
338
548
|
});
|
|
339
549
|
|
|
340
550
|
test("resumeWorkflowRun throws NotFoundError for an unknown id", async () => {
|
|
341
|
-
const { resumed } = setup({
|
|
551
|
+
const { resumed } = setup({ runs: [] });
|
|
342
552
|
await expect(
|
|
343
553
|
route("resumeWorkflowRun").handler({ pathParams: { id: "nope" } }),
|
|
344
554
|
).rejects.toThrow(NotFoundError);
|
|
345
555
|
expect(resumed).toEqual([]);
|
|
346
556
|
});
|
|
347
557
|
});
|
|
348
|
-
|
|
349
|
-
// ---------------------------------------------------------------------------
|
|
350
|
-
// Flag off → every route 404s
|
|
351
|
-
// ---------------------------------------------------------------------------
|
|
352
|
-
|
|
353
|
-
describe("workflow routes (flag off)", () => {
|
|
354
|
-
beforeEach(() => {
|
|
355
|
-
setup({ flagEnabled: false, runs: [makeRun({ id: "run-1" })], saved: [] });
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
test.each([
|
|
359
|
-
["listWorkflowRuns", { queryParams: {} }],
|
|
360
|
-
["getWorkflowRun", { pathParams: { id: "run-1" } }],
|
|
361
|
-
["abortWorkflowRun", { pathParams: { id: "run-1" } }],
|
|
362
|
-
["listSavedWorkflows", {}],
|
|
363
|
-
] as const)("%s throws NotFoundError when the flag is off", (op, args) => {
|
|
364
|
-
expect(() => route(op).handler(args)).toThrow(NotFoundError);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
test("resumeWorkflowRun rejects with NotFoundError when the flag is off", async () => {
|
|
368
|
-
await expect(
|
|
369
|
-
route("resumeWorkflowRun").handler({ pathParams: { id: "run-1" } }),
|
|
370
|
-
).rejects.toThrow(NotFoundError);
|
|
371
|
-
});
|
|
372
|
-
});
|
|
@@ -5,21 +5,18 @@
|
|
|
5
5
|
* shared ROUTES array. They are read/abort surfaces over the workflow run
|
|
6
6
|
* manager and the saved-workflow library — a human (via the `vellum workflows`
|
|
7
7
|
* CLI or the app) can inspect runs and abort an in-flight one.
|
|
8
|
-
*
|
|
9
|
-
* Every handler is gated on the `workflows` feature flag: when it is off, the
|
|
10
|
-
* routes behave as if they do not exist (404), so disabling the flag fully
|
|
11
|
-
* hides the surface.
|
|
12
8
|
*/
|
|
13
9
|
|
|
14
10
|
import { z } from "zod";
|
|
15
11
|
|
|
16
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
17
|
-
import { getConfig } from "../../config/loader.js";
|
|
18
|
-
import type { AssistantConfig } from "../../config/schema.js";
|
|
19
12
|
import { getAutoApproveThreshold } from "../../permissions/gateway-threshold-reader.js";
|
|
20
13
|
import { isFullAccessThreshold } from "../../permissions/threshold.js";
|
|
21
14
|
import { manifestGrantsSideEffects } from "../../workflows/capabilities.js";
|
|
22
|
-
import
|
|
15
|
+
import {
|
|
16
|
+
getJournal,
|
|
17
|
+
type WorkflowJournalEntry,
|
|
18
|
+
type WorkflowRun,
|
|
19
|
+
} from "../../workflows/journal-store.js";
|
|
23
20
|
import { listWorkflows } from "../../workflows/library.js";
|
|
24
21
|
import {
|
|
25
22
|
getWorkflowRunManager,
|
|
@@ -46,19 +43,16 @@ export interface WorkflowRoutesDeps {
|
|
|
46
43
|
"list" | "status" | "abort" | "resume"
|
|
47
44
|
>;
|
|
48
45
|
listWorkflows: typeof listWorkflows;
|
|
49
|
-
getConfig: () => AssistantConfig;
|
|
50
|
-
isFlagEnabled: (config: AssistantConfig) => boolean;
|
|
51
46
|
getAutoApproveThreshold: typeof getAutoApproveThreshold;
|
|
47
|
+
getJournal: typeof getJournal;
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
function defaultDeps(): WorkflowRoutesDeps {
|
|
55
51
|
return {
|
|
56
52
|
getManager: getWorkflowRunManager,
|
|
57
53
|
listWorkflows,
|
|
58
|
-
getConfig,
|
|
59
|
-
isFlagEnabled: (config) =>
|
|
60
|
-
isAssistantFeatureFlagEnabled("workflows", config),
|
|
61
54
|
getAutoApproveThreshold,
|
|
55
|
+
getJournal,
|
|
62
56
|
};
|
|
63
57
|
}
|
|
64
58
|
|
|
@@ -105,21 +99,33 @@ const savedWorkflowSchema = z.object({
|
|
|
105
99
|
path: z.string(),
|
|
106
100
|
});
|
|
107
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
|
+
|
|
108
125
|
// ---------------------------------------------------------------------------
|
|
109
126
|
// Handlers (transport-agnostic)
|
|
110
127
|
// ---------------------------------------------------------------------------
|
|
111
128
|
|
|
112
|
-
/**
|
|
113
|
-
* Throw a 404 when the `workflows` flag is off, hiding the whole surface. The
|
|
114
|
-
* NotFoundError (rather than a 403) keeps a disabled-flag indistinguishable
|
|
115
|
-
* from a route that does not exist.
|
|
116
|
-
*/
|
|
117
|
-
function assertFlagEnabled(): void {
|
|
118
|
-
if (!deps.isFlagEnabled(deps.getConfig())) {
|
|
119
|
-
throw new NotFoundError("Workflows are not enabled.");
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
129
|
/**
|
|
124
130
|
* The single compact-run wire contract, inferred from {@link workflowRunSchema}.
|
|
125
131
|
* The HTTP routes return this shape; other producers (e.g. the CLI's client-side
|
|
@@ -145,6 +151,64 @@ export function toWireRun(run: WorkflowRun): WorkflowRunWire {
|
|
|
145
151
|
};
|
|
146
152
|
}
|
|
147
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
|
+
|
|
148
212
|
function parseLimit(raw: string | undefined): number | undefined {
|
|
149
213
|
if (raw === undefined) return undefined;
|
|
150
214
|
const n = Number(raw);
|
|
@@ -162,7 +226,6 @@ function parseStatus(
|
|
|
162
226
|
}
|
|
163
227
|
|
|
164
228
|
function handleListRuns(queryParams: Record<string, string>) {
|
|
165
|
-
assertFlagEnabled();
|
|
166
229
|
const limit = parseLimit(queryParams.limit);
|
|
167
230
|
const status = parseStatus(queryParams.status);
|
|
168
231
|
const runs = deps.getManager().list({
|
|
@@ -173,7 +236,6 @@ function handleListRuns(queryParams: Record<string, string>) {
|
|
|
173
236
|
}
|
|
174
237
|
|
|
175
238
|
function handleGetRun(id: string) {
|
|
176
|
-
assertFlagEnabled();
|
|
177
239
|
const run = deps.getManager().status(id);
|
|
178
240
|
if (!run) {
|
|
179
241
|
throw new NotFoundError(`Workflow run ${id} not found`);
|
|
@@ -181,8 +243,29 @@ function handleGetRun(id: string) {
|
|
|
181
243
|
return toWireRun(run);
|
|
182
244
|
}
|
|
183
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
|
+
|
|
184
268
|
function handleAbortRun(id: string) {
|
|
185
|
-
assertFlagEnabled();
|
|
186
269
|
// status() is the source of truth for existence; abort() itself is a no-op
|
|
187
270
|
// for unknown/finished runs, so 404 on an unknown id is surfaced here.
|
|
188
271
|
const manager = deps.getManager();
|
|
@@ -194,7 +277,6 @@ function handleAbortRun(id: string) {
|
|
|
194
277
|
}
|
|
195
278
|
|
|
196
279
|
async function handleResumeRun(id: string) {
|
|
197
|
-
assertFlagEnabled();
|
|
198
280
|
const manager = deps.getManager();
|
|
199
281
|
// status() is the source of truth for existence, so 404 an unknown id here
|
|
200
282
|
// (matching abort) rather than letting resume's "not_found" reach the client
|
|
@@ -245,7 +327,6 @@ async function handleResumeRun(id: string) {
|
|
|
245
327
|
}
|
|
246
328
|
|
|
247
329
|
function handleListSavedWorkflows() {
|
|
248
|
-
assertFlagEnabled();
|
|
249
330
|
return { workflows: deps.listWorkflows() };
|
|
250
331
|
}
|
|
251
332
|
|
|
@@ -299,6 +380,27 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
299
380
|
additionalResponses: { "404": { description: "Run not found" } },
|
|
300
381
|
handler: ({ pathParams }: RouteHandlerArgs) => handleGetRun(pathParams!.id),
|
|
301
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
|
+
},
|
|
302
404
|
{
|
|
303
405
|
operationId: "abortWorkflowRun",
|
|
304
406
|
endpoint: "workflows/runs/:id/abort",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { getWorkspacePromptPath } from "../../util/platform.js";
|
|
4
|
+
|
|
5
|
+
function readWorkspaceFile(name: string): string {
|
|
6
|
+
try {
|
|
7
|
+
const path = getWorkspacePromptPath(name);
|
|
8
|
+
if (!existsSync(path)) return "";
|
|
9
|
+
return readFileSync(path, "utf-8");
|
|
10
|
+
} catch {
|
|
11
|
+
return "";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse the `## Greetings` section from SOUL.md. Returns bullet items as an
|
|
17
|
+
* array of strings, or `null` if the section is missing or empty.
|
|
18
|
+
*/
|
|
19
|
+
export function parseGreetingsSection(content: string): string[] | null {
|
|
20
|
+
let inSection = false;
|
|
21
|
+
let sectionLevel: number | null = null;
|
|
22
|
+
const greetings: string[] = [];
|
|
23
|
+
|
|
24
|
+
for (const line of content.split("\n")) {
|
|
25
|
+
const trimmed = line.trim();
|
|
26
|
+
const heading = /^(#{1,6})\s+(.+?)\s*#*$/.exec(trimmed);
|
|
27
|
+
if (heading) {
|
|
28
|
+
const level = heading[1]!.length;
|
|
29
|
+
const title = heading[2]!.trim();
|
|
30
|
+
if (inSection) {
|
|
31
|
+
if (sectionLevel !== null && level <= sectionLevel) break;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (level === 2 && /^greetings$/i.test(title)) {
|
|
35
|
+
inSection = true;
|
|
36
|
+
sectionLevel = level;
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (!inSection) continue;
|
|
41
|
+
const bullet = /^(?:[-*+]\s+|\d+[.)]\s+)(.+)$/.exec(trimmed);
|
|
42
|
+
const greeting = bullet?.[1]?.trim();
|
|
43
|
+
if (greeting) {
|
|
44
|
+
greetings.push(greeting);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return greetings.length > 0 ? greetings : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function readWorkspaceGreetings(): string[] | null {
|
|
52
|
+
const soulContent = readWorkspaceFile("SOUL.md");
|
|
53
|
+
if (!soulContent) return null;
|
|
54
|
+
return parseGreetingsSection(soulContent);
|
|
55
|
+
}
|