@vellumai/assistant 0.8.12 → 0.9.0-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +0 -14
- package/ARCHITECTURE.md +45 -45
- package/README.md +1 -1
- package/bun.lock +200 -154
- package/docs/architecture/integrations.md +3 -3
- package/docs/architecture/memory.md +2 -2
- package/docs/architecture/security.md +10 -10
- package/docs/runbook-trusted-contacts.md +12 -12
- package/docs/skills.md +6 -6
- package/docs/workflows-testing.md +221 -0
- package/docs/workflows.md +510 -0
- package/examples/plugins/echo/README.md +5 -5
- package/knip.json +2 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
- package/openapi.yaml +7197 -5708
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +66 -114
- package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
- package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
- package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
- package/src/__tests__/app-compiler.test.ts +7 -1
- package/src/__tests__/app-dir-path-guard.test.ts +27 -3
- package/src/__tests__/app-executors.test.ts +43 -0
- package/src/__tests__/approval-cascade.test.ts +0 -5
- package/src/__tests__/approval-routes-http.test.ts +91 -0
- package/src/__tests__/assistant-stream-state.test.ts +107 -0
- package/src/__tests__/browser-fill-credential.test.ts +3 -3
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +63 -7
- package/src/__tests__/compaction-trail-store.test.ts +74 -1
- package/src/__tests__/compaction.benchmark.test.ts +63 -41
- package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -5
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
- package/src/__tests__/conversation-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
- package/src/__tests__/conversation-speed-override.test.ts +0 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
- package/src/__tests__/credential-broker-server-use.test.ts +2 -2
- package/src/__tests__/credential-broker.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +417 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault.test.ts +37 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
- package/src/__tests__/dynamic-page-surface.test.ts +219 -0
- package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
- package/src/__tests__/gateway-flag-listener.test.ts +24 -7
- package/src/__tests__/guardian-action-sweep.test.ts +56 -219
- package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
- package/src/__tests__/llm-resolver.test.ts +115 -0
- package/src/__tests__/managed-profile-guard.test.ts +6 -5
- package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
- package/src/__tests__/media-generate-image.test.ts +20 -9
- package/src/__tests__/mock-gateway-ipc.ts +23 -0
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/normalize-onboarding.test.ts +26 -0
- package/src/__tests__/notification-decision-strategy.test.ts +4 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
- package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
- package/src/__tests__/require-fresh-approval.test.ts +425 -1
- package/src/__tests__/resolve-app-id.test.ts +56 -0
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
- package/src/__tests__/schedule-routes.test.ts +257 -4
- package/src/__tests__/schedule-store.test.ts +60 -0
- package/src/__tests__/schedule-tools.test.ts +247 -2
- package/src/__tests__/skill-execute-input.test.ts +85 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
- package/src/__tests__/task-scheduler.test.ts +299 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
- package/src/__tests__/tool-result-spool.test.ts +3 -1
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
- package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
- package/src/agent/compaction-circuit.ts +11 -0
- package/src/agent/loop.ts +181 -12
- package/src/api/constants/call-sites.ts +12 -0
- package/src/api/events/assistant-thinking-delta.ts +10 -0
- package/src/api/events/usage-update.ts +7 -0
- package/src/api/index.ts +4 -1
- package/src/api/responses/memory-v3-selection-log.ts +18 -11
- package/src/approvals/approval-primitive.ts +2 -2
- package/src/background-wake/background-wake-routes.test.ts +5 -2
- package/src/bundler/compiler-tools.ts +1 -1
- package/src/bundler/package-resolver.ts +0 -1
- package/src/calls/call-domain.ts +1 -1
- package/src/calls/guardian-action-sweep.ts +16 -93
- package/src/cli/AGENTS.md +4 -0
- package/src/cli/commands/__tests__/schedules.test.ts +430 -1
- package/src/cli/commands/credentials.ts +28 -24
- package/src/cli/commands/image-generation.ts +23 -9
- package/src/cli/commands/notifications.ts +1 -1
- package/src/cli/commands/plugins.ts +89 -46
- package/src/cli/commands/schedules.ts +384 -11
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
- package/src/cli/lib/inspect-plugin.ts +62 -1
- package/src/cli/lib/install-from-github.ts +52 -5
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/config/__tests__/workflows-schema.test.ts +60 -0
- package/src/config/bundled-skills/acp/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
- package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
- package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
- package/src/config/bundled-skills/schedule/SKILL.md +7 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
- package/src/config/bundled-skills/workflows/SKILL.md +214 -0
- package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
- package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
- package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
- package/src/config/bundled-tool-registry.ts +14 -2
- package/src/config/call-site-defaults.ts +5 -0
- package/src/config/feature-flag-registry.json +12 -4
- package/src/config/llm-context-resolution.ts +8 -0
- package/src/config/llm-resolver.ts +30 -0
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
- package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
- package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
- package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/heartbeat.ts +4 -1
- package/src/config/schemas/llm.ts +33 -26
- package/src/config/schemas/memory-retrospective.ts +19 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/services.ts +5 -2
- package/src/config/schemas/workflows.ts +42 -0
- package/src/config/skills.ts +3 -3
- package/src/context/compactor.ts +273 -39
- package/src/context/post-turn-tool-result-truncation.ts +23 -6
- package/src/context/tool-result-spool.ts +12 -17
- package/src/credential-execution/executable-discovery.ts +1 -1
- package/src/credential-execution/process-manager.ts +37 -3
- package/src/credential-execution/prompted-credential.ts +205 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
- package/src/daemon/conversation-process.ts +11 -2
- package/src/daemon/conversation-surfaces.ts +167 -3
- package/src/daemon/conversation-tool-setup.ts +103 -26
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +115 -11
- package/src/daemon/handlers/shared.ts +26 -14
- package/src/daemon/host-cu-proxy.ts +15 -12
- package/src/daemon/host-file-proxy.ts +15 -12
- package/src/daemon/host-transfer-proxy.ts +30 -24
- package/src/daemon/lifecycle.ts +40 -3
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/messages.ts +2 -10
- package/src/daemon/message-types/workflows.ts +49 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
- package/src/daemon/process-message.ts +6 -0
- package/src/daemon/tool-setup-types.ts +57 -0
- package/src/daemon/wake-conversation-ops.ts +18 -0
- package/src/heartbeat/heartbeat-run-store.ts +8 -2
- package/src/home/feed-types.ts +1 -1
- package/src/ipc/gateway-flag-listener.ts +28 -6
- package/src/mcp/mcp-auth-state.ts +8 -20
- package/src/media/__tests__/image-models.test.ts +57 -0
- package/src/media/image-models.ts +66 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
- package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
- package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
- package/src/memory/auto-analysis-enqueue.ts +14 -1
- package/src/memory/compaction-log-store-clickhouse.ts +6 -4
- package/src/memory/conversation-crud.ts +9 -2
- package/src/memory/conversation-disk-view.ts +1 -1
- package/src/memory/conversation-queries.ts +22 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/db-maintenance.ts +16 -0
- package/src/memory/embedding-runtime-manager.ts +1 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
- package/src/memory/llm-request-log-source-local.ts +19 -1
- package/src/memory/llm-request-log-source.ts +35 -6
- package/src/memory/llm-request-log-store.ts +90 -2
- package/src/memory/memory-retrospective-constants.ts +9 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -6
- package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
- package/src/memory/memory-retrospective-job.ts +500 -208
- package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
- package/src/memory/memory-retrospective-state.ts +85 -2
- package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
- package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
- package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
- package/src/memory/migrations/284-workflow-runs.ts +51 -0
- package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
- package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
- package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
- package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
- package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
- package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
- package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/preloaded-apps.ts +116 -0
- package/src/memory/schema/infrastructure.ts +4 -0
- package/src/memory/schema/memory-core.ts +4 -0
- package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
- package/src/memory/v2/__tests__/page-store.test.ts +13 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -6
- package/src/memory/v2/page-store.ts +4 -3
- package/src/memory/v2/qdrant.ts +42 -3
- package/src/memory/v2/types.ts +16 -10
- package/src/messaging/draft-store.ts +1 -1
- package/src/notifications/access-request-copy.ts +200 -113
- package/src/notifications/adapters/slack.ts +250 -111
- package/src/notifications/adapters/telegram.ts +7 -44
- package/src/notifications/approval-card-builder.ts +93 -0
- package/src/notifications/broadcaster.ts +74 -0
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/copy-composer.ts +32 -26
- package/src/notifications/decision-engine.ts +59 -7
- package/src/notifications/guardian-question-mode.ts +145 -155
- package/src/notifications/home-feed-side-effect.ts +28 -11
- package/src/notifications/notification-utils.ts +66 -0
- package/src/notifications/signal.ts +6 -0
- package/src/notifications/tool-approval-copy.ts +142 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/threshold.ts +11 -0
- package/src/plugin-api/types.ts +16 -4
- package/src/plugins/defaults/compaction/window-manager.ts +44 -0
- package/src/plugins/defaults/index.ts +46 -0
- package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
- package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
- package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
- package/src/prompts/__tests__/system-prompt.test.ts +100 -1
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
- package/src/prompts/normalize-onboarding.ts +2 -0
- package/src/prompts/persona-resolver.ts +3 -0
- package/src/prompts/system-prompt.ts +51 -2
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
- package/src/prompts/templates/system-sections.ts +8 -3
- package/src/providers/call-site-routing.ts +6 -3
- package/src/providers/fireworks/client.ts +3 -0
- package/src/providers/inference/auth.ts +52 -46
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +47 -9
- package/src/providers/openai/coerce-object-args.ts +104 -0
- package/src/providers/retry.ts +8 -5
- package/src/providers/types.ts +10 -0
- package/src/runtime/__tests__/agent-wake.test.ts +629 -7
- package/src/runtime/access-request-helper.ts +16 -9
- package/src/runtime/agent-wake.ts +302 -51
- package/src/runtime/assistant-stream-state.ts +141 -8
- package/src/runtime/background-job-runner.ts +9 -0
- package/src/runtime/channel-approval-types.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +6 -5
- package/src/runtime/channel-invite-transports/voice.ts +2 -2
- package/src/runtime/channel-invite-types.ts +4 -2
- package/src/runtime/channel-retry-sweep.ts +19 -41
- package/src/runtime/finalize-event-delivery.ts +72 -0
- package/src/runtime/guardian-action-message-composer.ts +0 -54
- package/src/runtime/http-server.ts +6 -14
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/message-composer-types.ts +0 -9
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
- package/src/runtime/middleware/auth.ts +27 -3
- package/src/runtime/middleware/rate-limiter.ts +28 -1
- package/src/runtime/migrations/vbundle-builder.ts +6 -5
- package/src/runtime/pending-interactions.ts +20 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
- package/src/runtime/routes/approval-routes.ts +35 -8
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
- package/src/runtime/routes/btw-routes.ts +37 -1
- package/src/runtime/routes/channel-delivery-routes.ts +11 -7
- package/src/runtime/routes/channel-route-definitions.ts +3 -0
- package/src/runtime/routes/channel-route-shared.ts +3 -1
- package/src/runtime/routes/consolidation-routes.ts +17 -13
- package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
- package/src/runtime/routes/conversation-list-routes.ts +41 -4
- package/src/runtime/routes/conversation-query-routes.ts +196 -11
- package/src/runtime/routes/conversation-routes.ts +15 -1
- package/src/runtime/routes/credential-prompt-routes.ts +39 -17
- package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
- package/src/runtime/routes/heartbeat-routes.ts +18 -13
- package/src/runtime/routes/image-generation-routes.ts +20 -2
- package/src/runtime/routes/inbound-message-handler.ts +13 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/log-export/AGENTS.md +1 -1
- package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +5 -9
- package/src/runtime/routes/plugins-routes.ts +26 -0
- package/src/runtime/routes/ps-routes.ts +10 -8
- package/src/runtime/routes/retrospective-routes.ts +235 -0
- package/src/runtime/routes/runs-pagination.ts +75 -0
- package/src/runtime/routes/schedule-routes.ts +367 -59
- package/src/runtime/routes/sounds-config-routes.ts +239 -0
- package/src/runtime/routes/surface-action-routes.ts +84 -4
- package/src/runtime/routes/workflow-routes.test.ts +372 -0
- package/src/runtime/routes/workflow-routes.ts +363 -0
- package/src/runtime/routes/workspace-routes.test.ts +61 -1
- package/src/runtime/routes/workspace-routes.ts +26 -1
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
- package/src/runtime/services/analyze-conversation.ts +26 -13
- package/src/schedule/inference-profile.ts +28 -0
- package/src/schedule/schedule-store.ts +152 -4
- package/src/schedule/scheduler-types.ts +6 -0
- package/src/schedule/scheduler.ts +96 -0
- package/src/security/secret-allowlist.ts +1 -1
- package/src/skills/path-classifier.ts +1 -1
- package/src/tools/apps/executors.ts +23 -0
- package/src/tools/apps/resolve-app-id.ts +42 -0
- package/src/tools/browser/browser-execution.ts +9 -11
- package/src/tools/credentials/broker.ts +4 -4
- package/src/tools/credentials/vault.ts +26 -137
- package/src/tools/executor.ts +69 -0
- package/src/tools/flag-gated-tools.test.ts +76 -0
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/registry.ts +51 -0
- package/src/tools/schedule/create.ts +77 -1
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +73 -1
- package/src/tools/skills/execute.ts +56 -0
- package/src/tools/terminal/shell.ts +1 -1
- package/src/tools/ui-surface/definitions.ts +91 -2
- package/src/tools/workflows/manage-workflows.ts +183 -0
- package/src/tools/workflows/run-workflow.test.ts +442 -0
- package/src/tools/workflows/run-workflow.ts +88 -0
- package/src/types/onboarding-context.ts +2 -0
- package/src/usage/attribution.ts +24 -0
- package/src/util/canonicalize-identity.ts +12 -3
- package/src/util/platform.ts +17 -17
- package/src/watcher/__tests__/engine.test.ts +24 -0
- package/src/watcher/__tests__/telemetry.test.ts +135 -0
- package/src/watcher/engine.ts +7 -0
- package/src/watcher/telemetry.ts +74 -0
- package/src/workflows/capabilities.test.ts +365 -0
- package/src/workflows/capabilities.ts +359 -0
- package/src/workflows/deterministic-stringify.ts +27 -0
- package/src/workflows/engine-integration.test.ts +656 -0
- package/src/workflows/engine.test.ts +1144 -0
- package/src/workflows/engine.ts +1078 -0
- package/src/workflows/fanout-load.test.ts +168 -0
- package/src/workflows/journal-store.test.ts +369 -0
- package/src/workflows/journal-store.ts +470 -0
- package/src/workflows/leaf-runner.test.ts +704 -0
- package/src/workflows/leaf-runner.ts +589 -0
- package/src/workflows/library.test.ts +134 -0
- package/src/workflows/library.ts +124 -0
- package/src/workflows/run-manager.test.ts +711 -0
- package/src/workflows/run-manager.ts +593 -0
- package/src/workflows/sandbox-escape.test.ts +339 -0
- package/src/workflows/sandbox.test.ts +251 -0
- package/src/workflows/sandbox.ts +447 -0
- package/src/workspace/adaptive-thinking-repair.ts +33 -11
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
- package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
- package/src/workspace/migrations/031-drop-user-md.ts +1 -4
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
- package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
- package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
- package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/migrations/runner.ts +1 -1
- package/tsconfig.json +1 -1
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
- package/src/__tests__/guardian-action-store.test.ts +0 -106
- package/src/daemon/guardian-action-generators.ts +0 -71
- package/src/memory/guardian-action-store.ts +0 -484
- package/src/runtime/guardian-action-grant-minter.ts +0 -150
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { SlackChannelConfigResult } from "../daemon/handlers/config-slack-channel.js";
|
|
4
|
+
import type { SecretPromptResult } from "../permissions/secret-prompter.js";
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Mutable mock state (closed over by the mock factories below)
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
let capturedSecretParams: Record<string, unknown> | undefined;
|
|
11
|
+
let capturedMetadata: Record<string, unknown> | undefined;
|
|
12
|
+
let secretResult: SecretPromptResult = {
|
|
13
|
+
value: "secret-value",
|
|
14
|
+
delivery: "store",
|
|
15
|
+
};
|
|
16
|
+
let allowOneTimeSend = true;
|
|
17
|
+
let existingMetadata: Record<string, unknown> | undefined;
|
|
18
|
+
let slackConfigResult: SlackChannelConfigResult;
|
|
19
|
+
let slackConfigArgs: Array<(string | undefined)[]>;
|
|
20
|
+
let secureKeyWrites: Array<{ key: string; value: string }>;
|
|
21
|
+
let transientInjections: Array<{
|
|
22
|
+
service: string;
|
|
23
|
+
field: string;
|
|
24
|
+
value: string;
|
|
25
|
+
}>;
|
|
26
|
+
let syncedServices: string[];
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Mocks for the route's collaborators
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
mock.module("../util/logger.js", () => ({
|
|
33
|
+
getLogger: () =>
|
|
34
|
+
new Proxy({} as Record<string, unknown>, {
|
|
35
|
+
get: () => () => {},
|
|
36
|
+
}),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
mock.module("../daemon/handlers/shared.js", () => ({
|
|
40
|
+
requestSecretStandalone: mock(
|
|
41
|
+
async (params: Record<string, unknown>): Promise<SecretPromptResult> => {
|
|
42
|
+
capturedSecretParams = params;
|
|
43
|
+
return secretResult;
|
|
44
|
+
},
|
|
45
|
+
),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
mock.module("../tools/credentials/metadata-store.js", () => ({
|
|
49
|
+
assertMetadataWritable: () => {},
|
|
50
|
+
getCredentialMetadata: mock(() => existingMetadata),
|
|
51
|
+
upsertCredentialMetadata: mock(
|
|
52
|
+
(service: string, field: string, meta: Record<string, unknown>) => {
|
|
53
|
+
capturedMetadata = { service, field, ...meta };
|
|
54
|
+
},
|
|
55
|
+
),
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
mock.module("../security/secure-keys.js", () => ({
|
|
59
|
+
setSecureKeyAsync: mock(async (key: string, value: string) => {
|
|
60
|
+
secureKeyWrites.push({ key, value });
|
|
61
|
+
return true;
|
|
62
|
+
}),
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
mock.module("../security/credential-key.js", () => ({
|
|
66
|
+
credentialKey: (service: string, field: string) => `${service}:${field}`,
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
mock.module("../oauth/manual-token-connection.js", () => ({
|
|
70
|
+
syncManualTokenConnection: mock(async (service: string) => {
|
|
71
|
+
syncedServices.push(service);
|
|
72
|
+
}),
|
|
73
|
+
}));
|
|
74
|
+
|
|
75
|
+
mock.module("../config/loader.js", () => ({
|
|
76
|
+
getConfig: () => ({ secretDetection: { allowOneTimeSend } }),
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
mock.module("../daemon/handlers/config-slack-channel.js", () => ({
|
|
80
|
+
setSlackChannelConfig: mock(
|
|
81
|
+
async (
|
|
82
|
+
botToken?: string,
|
|
83
|
+
appToken?: string,
|
|
84
|
+
userToken?: string,
|
|
85
|
+
): Promise<SlackChannelConfigResult> => {
|
|
86
|
+
slackConfigArgs.push([botToken, appToken, userToken]);
|
|
87
|
+
return slackConfigResult;
|
|
88
|
+
},
|
|
89
|
+
),
|
|
90
|
+
}));
|
|
91
|
+
|
|
92
|
+
mock.module("../tools/credentials/broker.js", () => ({
|
|
93
|
+
credentialBroker: {
|
|
94
|
+
injectTransient: mock((service: string, field: string, value: string) => {
|
|
95
|
+
transientInjections.push({ service, field, value });
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
import { ROUTES } from "../runtime/routes/credential-prompt-routes.js";
|
|
101
|
+
|
|
102
|
+
const promptRoute = ROUTES.find((r) => r.operationId === "credentials_prompt");
|
|
103
|
+
|
|
104
|
+
function slackResult(
|
|
105
|
+
overrides: Partial<SlackChannelConfigResult>,
|
|
106
|
+
): SlackChannelConfigResult {
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
hasBotToken: false,
|
|
110
|
+
hasAppToken: false,
|
|
111
|
+
hasUserToken: false,
|
|
112
|
+
connected: false,
|
|
113
|
+
...overrides,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
type PromptResponse = {
|
|
118
|
+
ok: boolean;
|
|
119
|
+
error?: string;
|
|
120
|
+
service?: string;
|
|
121
|
+
field?: string;
|
|
122
|
+
message?: string;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
describe("credentials/prompt route", () => {
|
|
126
|
+
beforeEach(() => {
|
|
127
|
+
capturedSecretParams = undefined;
|
|
128
|
+
capturedMetadata = undefined;
|
|
129
|
+
secretResult = { value: "secret-value", delivery: "store" };
|
|
130
|
+
allowOneTimeSend = true;
|
|
131
|
+
existingMetadata = undefined;
|
|
132
|
+
slackConfigResult = slackResult({});
|
|
133
|
+
slackConfigArgs = [];
|
|
134
|
+
secureKeyWrites = [];
|
|
135
|
+
transientInjections = [];
|
|
136
|
+
syncedServices = [];
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("forwards usageDescription as the prompt purpose and to metadata", async () => {
|
|
140
|
+
/**
|
|
141
|
+
* The CLI `credentials prompt` command exposes `--usage-description` to
|
|
142
|
+
* match the credential_store tool's prompt action. The route must thread
|
|
143
|
+
* it to the secure prompt (as `purpose`, shown to the user) and persist it
|
|
144
|
+
* to credential metadata (as `usageDescription`).
|
|
145
|
+
*/
|
|
146
|
+
// GIVEN the credentials/prompt route is registered
|
|
147
|
+
expect(promptRoute).toBeDefined();
|
|
148
|
+
|
|
149
|
+
// WHEN a prompt request carries a usageDescription
|
|
150
|
+
const result = (await promptRoute!.handler({
|
|
151
|
+
body: {
|
|
152
|
+
service: "sentry",
|
|
153
|
+
field: "auth_token",
|
|
154
|
+
label: "Sentry Auth Token",
|
|
155
|
+
usageDescription: "Needed to read issues",
|
|
156
|
+
},
|
|
157
|
+
})) as PromptResponse;
|
|
158
|
+
|
|
159
|
+
// THEN the prompt is issued with the usageDescription as its purpose
|
|
160
|
+
expect(result.ok).toBe(true);
|
|
161
|
+
expect(capturedSecretParams?.purpose).toBe("Needed to read issues");
|
|
162
|
+
|
|
163
|
+
// AND the credential metadata records the usageDescription
|
|
164
|
+
expect(capturedMetadata?.usageDescription).toBe("Needed to read issues");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("omits purpose when usageDescription is not provided", async () => {
|
|
168
|
+
/**
|
|
169
|
+
* usageDescription is optional, so a prompt without it must not invent a
|
|
170
|
+
* purpose or a metadata usageDescription.
|
|
171
|
+
*/
|
|
172
|
+
// GIVEN a prompt request with no usageDescription
|
|
173
|
+
// WHEN the route handles it
|
|
174
|
+
const result = (await promptRoute!.handler({
|
|
175
|
+
body: {
|
|
176
|
+
service: "github",
|
|
177
|
+
field: "pat",
|
|
178
|
+
label: "GitHub PAT",
|
|
179
|
+
},
|
|
180
|
+
})) as PromptResponse;
|
|
181
|
+
|
|
182
|
+
// THEN no purpose is forwarded to the prompt
|
|
183
|
+
expect(result.ok).toBe(true);
|
|
184
|
+
expect(capturedSecretParams?.purpose).toBeUndefined();
|
|
185
|
+
|
|
186
|
+
// AND no usageDescription is written to metadata
|
|
187
|
+
expect(capturedMetadata?.usageDescription).toBeUndefined();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("stores a non-slack credential to secure storage without a message", async () => {
|
|
191
|
+
/**
|
|
192
|
+
* The default delivery persists the value to secure storage and runs the
|
|
193
|
+
* manual-token connection sync, returning no message so the CLI prints its
|
|
194
|
+
* generic "Stored credential" line.
|
|
195
|
+
*/
|
|
196
|
+
// GIVEN a standard store-delivery prompt for a non-slack credential
|
|
197
|
+
// WHEN the route handles it
|
|
198
|
+
const result = (await promptRoute!.handler({
|
|
199
|
+
body: { service: "stripe", field: "api_key", label: "Stripe API Key" },
|
|
200
|
+
})) as PromptResponse;
|
|
201
|
+
|
|
202
|
+
// THEN the value is written to secure storage
|
|
203
|
+
expect(result.ok).toBe(true);
|
|
204
|
+
expect(secureKeyWrites).toEqual([
|
|
205
|
+
{ key: "stripe:api_key", value: "secret-value" },
|
|
206
|
+
]);
|
|
207
|
+
|
|
208
|
+
// AND the manual token connection is synced for the service
|
|
209
|
+
expect(syncedServices).toEqual(["stripe"]);
|
|
210
|
+
|
|
211
|
+
// AND no message is returned (the CLI falls back to its default line)
|
|
212
|
+
expect(result.message).toBeUndefined();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("connects the channel when a slack_channel token is provided", async () => {
|
|
216
|
+
/**
|
|
217
|
+
* Slack channel tokens must route through the Slack channel config so the
|
|
218
|
+
* channel actually connects, and the route surfaces the connection status
|
|
219
|
+
* to the CLI via `message`.
|
|
220
|
+
*/
|
|
221
|
+
// GIVEN the Slack channel config reports a successful connection
|
|
222
|
+
slackConfigResult = slackResult({
|
|
223
|
+
connected: true,
|
|
224
|
+
teamName: "Acme",
|
|
225
|
+
botUsername: "acmebot",
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// WHEN a slack_channel bot_token is submitted
|
|
229
|
+
const result = (await promptRoute!.handler({
|
|
230
|
+
body: {
|
|
231
|
+
service: "slack_channel",
|
|
232
|
+
field: "bot_token",
|
|
233
|
+
label: "Slack Bot Token",
|
|
234
|
+
},
|
|
235
|
+
})) as PromptResponse;
|
|
236
|
+
|
|
237
|
+
// THEN the token is routed through the Slack channel config, not secure storage
|
|
238
|
+
expect(slackConfigArgs).toEqual([["secret-value", undefined, undefined]]);
|
|
239
|
+
expect(secureKeyWrites).toEqual([]);
|
|
240
|
+
|
|
241
|
+
// AND the connection status is surfaced as the response message
|
|
242
|
+
expect(result.ok).toBe(true);
|
|
243
|
+
expect(result.message).toBe("Slack channel connected to Acme (@acmebot).");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test("surfaces the warning when a slack_channel connection is incomplete", async () => {
|
|
247
|
+
/**
|
|
248
|
+
* When only one of the required Slack tokens is present the config returns a
|
|
249
|
+
* warning instead of a connection; the route surfaces it so the user knows
|
|
250
|
+
* to provide the remaining token.
|
|
251
|
+
*/
|
|
252
|
+
// GIVEN the Slack channel config reports an incomplete connection
|
|
253
|
+
slackConfigResult = slackResult({
|
|
254
|
+
warning: "Slack channel connection incomplete; provide the app token.",
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// WHEN a slack_channel app_token is submitted
|
|
258
|
+
const result = (await promptRoute!.handler({
|
|
259
|
+
body: {
|
|
260
|
+
service: "slack_channel",
|
|
261
|
+
field: "app_token",
|
|
262
|
+
label: "Slack App Token",
|
|
263
|
+
},
|
|
264
|
+
})) as PromptResponse;
|
|
265
|
+
|
|
266
|
+
// THEN the warning is surfaced as the response message
|
|
267
|
+
expect(result.ok).toBe(true);
|
|
268
|
+
expect(result.message).toBe(
|
|
269
|
+
"Slack channel connection incomplete; provide the app token.",
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("fails when the slack_channel config rejects the token", async () => {
|
|
274
|
+
/**
|
|
275
|
+
* A failed Slack configuration (e.g. invalid_auth) must surface as an error
|
|
276
|
+
* rather than silently storing an unusable token.
|
|
277
|
+
*/
|
|
278
|
+
// GIVEN the Slack channel config rejects the token
|
|
279
|
+
slackConfigResult = slackResult({ success: false, error: "invalid_auth" });
|
|
280
|
+
|
|
281
|
+
// WHEN a slack_channel bot_token is submitted
|
|
282
|
+
const result = (await promptRoute!.handler({
|
|
283
|
+
body: {
|
|
284
|
+
service: "slack_channel",
|
|
285
|
+
field: "bot_token",
|
|
286
|
+
label: "Slack Bot Token",
|
|
287
|
+
},
|
|
288
|
+
})) as PromptResponse;
|
|
289
|
+
|
|
290
|
+
// THEN the route returns the configuration error
|
|
291
|
+
expect(result.ok).toBe(false);
|
|
292
|
+
expect(result.error).toBe("invalid_auth");
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("injects a one-time credential without persisting it", async () => {
|
|
296
|
+
/**
|
|
297
|
+
* One-time-send delivery hands the value to the broker for the next
|
|
298
|
+
* operation and must never write it to secure storage.
|
|
299
|
+
*/
|
|
300
|
+
// GIVEN one-time send is enabled and the user chose transient delivery
|
|
301
|
+
allowOneTimeSend = true;
|
|
302
|
+
secretResult = { value: "secret-value", delivery: "transient_send" };
|
|
303
|
+
|
|
304
|
+
// WHEN a non-slack credential is submitted for one-time use
|
|
305
|
+
const result = (await promptRoute!.handler({
|
|
306
|
+
body: { service: "stripe", field: "api_key", label: "Stripe API Key" },
|
|
307
|
+
})) as PromptResponse;
|
|
308
|
+
|
|
309
|
+
// THEN the value is injected into the broker, not secure storage
|
|
310
|
+
expect(transientInjections).toEqual([
|
|
311
|
+
{ service: "stripe", field: "api_key", value: "secret-value" },
|
|
312
|
+
]);
|
|
313
|
+
expect(secureKeyWrites).toEqual([]);
|
|
314
|
+
|
|
315
|
+
// AND the response message notes the value was not saved
|
|
316
|
+
expect(result.ok).toBe(true);
|
|
317
|
+
expect(result.message).toContain("One-time credential provided");
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("rejects one-time send when it is disabled in config", async () => {
|
|
321
|
+
/**
|
|
322
|
+
* Transient delivery requires the secretDetection.allowOneTimeSend config
|
|
323
|
+
* flag; otherwise the value is rejected rather than injected.
|
|
324
|
+
*/
|
|
325
|
+
// GIVEN one-time send is disabled and the user chose transient delivery
|
|
326
|
+
allowOneTimeSend = false;
|
|
327
|
+
secretResult = { value: "secret-value", delivery: "transient_send" };
|
|
328
|
+
|
|
329
|
+
// WHEN a credential is submitted for one-time use
|
|
330
|
+
const result = (await promptRoute!.handler({
|
|
331
|
+
body: { service: "stripe", field: "api_key", label: "Stripe API Key" },
|
|
332
|
+
})) as PromptResponse;
|
|
333
|
+
|
|
334
|
+
// THEN the request is rejected and nothing is injected
|
|
335
|
+
expect(result.ok).toBe(false);
|
|
336
|
+
expect(result.error).toContain("one-time send is not enabled");
|
|
337
|
+
expect(transientInjections).toEqual([]);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("rejects one-time send for slack_channel credentials", async () => {
|
|
341
|
+
/**
|
|
342
|
+
* Slack channel tokens must be saved so the channel can connect; a one-time
|
|
343
|
+
* send for them is rejected.
|
|
344
|
+
*/
|
|
345
|
+
// GIVEN the user chose transient delivery for a slack_channel token
|
|
346
|
+
secretResult = { value: "secret-value", delivery: "transient_send" };
|
|
347
|
+
|
|
348
|
+
// WHEN a slack_channel bot_token is submitted for one-time use
|
|
349
|
+
const result = (await promptRoute!.handler({
|
|
350
|
+
body: {
|
|
351
|
+
service: "slack_channel",
|
|
352
|
+
field: "bot_token",
|
|
353
|
+
label: "Slack Bot Token",
|
|
354
|
+
},
|
|
355
|
+
})) as PromptResponse;
|
|
356
|
+
|
|
357
|
+
// THEN the request is rejected without injecting or connecting
|
|
358
|
+
expect(result.ok).toBe(false);
|
|
359
|
+
expect(result.error).toContain("must be saved to secure storage");
|
|
360
|
+
expect(transientInjections).toEqual([]);
|
|
361
|
+
expect(slackConfigArgs).toEqual([]);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("omitting allowed-tools/-domains forwards undefined so an existing policy is preserved", async () => {
|
|
365
|
+
/**
|
|
366
|
+
* Rotating an existing credential without policy flags must not wipe its
|
|
367
|
+
* allowed tools/domains. The route forwards `undefined` (not `[]`) for
|
|
368
|
+
* omitted flags so the metadata store's partial-update path leaves the
|
|
369
|
+
* existing policy untouched.
|
|
370
|
+
*/
|
|
371
|
+
// GIVEN a credential already exists with an allowed-tools policy
|
|
372
|
+
existingMetadata = {
|
|
373
|
+
service: "github",
|
|
374
|
+
field: "pat",
|
|
375
|
+
allowedTools: ["bash"],
|
|
376
|
+
allowedDomains: ["github.com"],
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// WHEN it is re-prompted without allowed-tools or allowed-domains
|
|
380
|
+
const result = (await promptRoute!.handler({
|
|
381
|
+
body: { service: "github", field: "pat", label: "GitHub PAT" },
|
|
382
|
+
})) as PromptResponse;
|
|
383
|
+
|
|
384
|
+
// THEN the metadata upsert receives undefined for both lists
|
|
385
|
+
expect(result.ok).toBe(true);
|
|
386
|
+
expect(capturedMetadata).toBeDefined();
|
|
387
|
+
expect(capturedMetadata!.allowedTools).toBeUndefined();
|
|
388
|
+
expect(capturedMetadata!.allowedDomains).toBeUndefined();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test("forwards provided allowed-tools/-domains to credential metadata", async () => {
|
|
392
|
+
/**
|
|
393
|
+
* When the caller does supply policy flags they must reach the metadata
|
|
394
|
+
* store verbatim, including an explicit empty array used to set a deny-all
|
|
395
|
+
* policy.
|
|
396
|
+
*/
|
|
397
|
+
// GIVEN a prompt request that supplies an allowed-tools list and a
|
|
398
|
+
// deny-all (empty) allowed-domains list
|
|
399
|
+
// WHEN the route handles it
|
|
400
|
+
const result = (await promptRoute!.handler({
|
|
401
|
+
body: {
|
|
402
|
+
service: "stripe",
|
|
403
|
+
field: "api_key",
|
|
404
|
+
label: "Stripe API Key",
|
|
405
|
+
allowedTools: ["make_authenticated_request"],
|
|
406
|
+
allowedDomains: [],
|
|
407
|
+
},
|
|
408
|
+
})) as PromptResponse;
|
|
409
|
+
|
|
410
|
+
// THEN the metadata upsert receives the supplied lists verbatim
|
|
411
|
+
expect(result.ok).toBe(true);
|
|
412
|
+
expect(capturedMetadata!.allowedTools).toEqual([
|
|
413
|
+
"make_authenticated_request",
|
|
414
|
+
]);
|
|
415
|
+
expect(capturedMetadata!.allowedDomains).toEqual([]);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
@@ -166,6 +166,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
166
166
|
// Any new import must be reviewed for secret-leak risk and added here.
|
|
167
167
|
const ALLOWED_IMPORTERS = new Set([
|
|
168
168
|
"tools/credentials/vault.ts", // credential store tool
|
|
169
|
+
"credential-execution/prompted-credential.ts", // shared prompt-action persistence (stores secret via setSecureKeyAsync)
|
|
169
170
|
"tools/credentials/broker.ts", // brokered credential access
|
|
170
171
|
"tools/network/web-search.ts", // web search API key lookup
|
|
171
172
|
"daemon/handlers/config-telegram.ts", // Telegram bot token management
|
|
@@ -842,6 +842,43 @@ describe("credential_store tool", () => {
|
|
|
842
842
|
expect(metadata!.allowedDomains).toEqual([]);
|
|
843
843
|
});
|
|
844
844
|
|
|
845
|
+
test("store rotation without policy fields preserves the existing policy", async () => {
|
|
846
|
+
/**
|
|
847
|
+
* Re-storing (rotating) a credential without policy fields must keep the
|
|
848
|
+
* policy it was originally stored with, rather than resetting it to a
|
|
849
|
+
* deny-all empty policy.
|
|
850
|
+
*/
|
|
851
|
+
// GIVEN a credential stored with an allowed-tools/-domains policy
|
|
852
|
+
await credentialStoreTool.execute(
|
|
853
|
+
{
|
|
854
|
+
action: "store",
|
|
855
|
+
service: "rotation-svc",
|
|
856
|
+
field: "token",
|
|
857
|
+
value: "v1",
|
|
858
|
+
allowed_tools: ["browser_fill_credential"],
|
|
859
|
+
allowed_domains: ["example.com"],
|
|
860
|
+
},
|
|
861
|
+
_ctx,
|
|
862
|
+
);
|
|
863
|
+
|
|
864
|
+
// WHEN it is re-stored with a new value but no policy fields
|
|
865
|
+
const result = await credentialStoreTool.execute(
|
|
866
|
+
{
|
|
867
|
+
action: "store",
|
|
868
|
+
service: "rotation-svc",
|
|
869
|
+
field: "token",
|
|
870
|
+
value: "v2",
|
|
871
|
+
},
|
|
872
|
+
_ctx,
|
|
873
|
+
);
|
|
874
|
+
|
|
875
|
+
// THEN the original policy is preserved
|
|
876
|
+
expect(result.isError).toBe(false);
|
|
877
|
+
const metadata = getCredentialMetadata("rotation-svc", "token");
|
|
878
|
+
expect(metadata!.allowedTools).toEqual(["browser_fill_credential"]);
|
|
879
|
+
expect(metadata!.allowedDomains).toEqual(["example.com"]);
|
|
880
|
+
});
|
|
881
|
+
|
|
845
882
|
test("store rejects invalid policy input", async () => {
|
|
846
883
|
const result = await credentialStoreTool.execute(
|
|
847
884
|
{
|
|
@@ -50,7 +50,11 @@ describe("schedule_syntax column migration", () => {
|
|
|
50
50
|
reuse_conversation INTEGER NOT NULL DEFAULT 0,
|
|
51
51
|
script TEXT,
|
|
52
52
|
wake_conversation_id TEXT,
|
|
53
|
+
workflow_name TEXT,
|
|
54
|
+
workflow_args_json TEXT,
|
|
53
55
|
timeout_ms INTEGER,
|
|
56
|
+
inference_profile TEXT,
|
|
57
|
+
capabilities_json TEXT,
|
|
54
58
|
description TEXT NOT NULL DEFAULT '',
|
|
55
59
|
created_at INTEGER NOT NULL,
|
|
56
60
|
updated_at INTEGER NOT NULL
|
|
@@ -138,6 +142,16 @@ describe("schedule_syntax column migration", () => {
|
|
|
138
142
|
} catch {
|
|
139
143
|
/* already exists */
|
|
140
144
|
}
|
|
145
|
+
try {
|
|
146
|
+
raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_name TEXT`);
|
|
147
|
+
} catch {
|
|
148
|
+
/* already exists */
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_args_json TEXT`);
|
|
152
|
+
} catch {
|
|
153
|
+
/* already exists */
|
|
154
|
+
}
|
|
141
155
|
migrateScheduleSourceConversation(db);
|
|
142
156
|
migrateScheduleDescription(db);
|
|
143
157
|
|
|
@@ -203,6 +217,16 @@ describe("schedule_syntax column migration", () => {
|
|
|
203
217
|
} catch {
|
|
204
218
|
/* ok */
|
|
205
219
|
}
|
|
220
|
+
try {
|
|
221
|
+
raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_name TEXT`);
|
|
222
|
+
} catch {
|
|
223
|
+
/* ok */
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
raw.exec(`ALTER TABLE cron_jobs ADD COLUMN workflow_args_json TEXT`);
|
|
227
|
+
} catch {
|
|
228
|
+
/* ok */
|
|
229
|
+
}
|
|
206
230
|
migrateScheduleSourceConversation(db);
|
|
207
231
|
migrateScheduleSourceConversation(db);
|
|
208
232
|
migrateScheduleDescription(db);
|