@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
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
formatSlackChannelStatus,
|
|
3
|
+
isSlackChannelCredential,
|
|
4
|
+
persistPromptedCredential,
|
|
5
|
+
storeSlackChannelCredential,
|
|
6
|
+
} from "../../credential-execution/prompted-credential.js";
|
|
2
7
|
import {
|
|
3
8
|
clearSlackUserToken,
|
|
4
|
-
setSlackChannelConfig,
|
|
5
9
|
type SlackChannelConfigResult,
|
|
6
10
|
} from "../../daemon/handlers/config-slack-channel.js";
|
|
7
11
|
import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
|
|
@@ -22,7 +26,6 @@ import type {
|
|
|
22
26
|
ToolDefinition,
|
|
23
27
|
ToolExecutionResult,
|
|
24
28
|
} from "../types.js";
|
|
25
|
-
import { credentialBroker } from "./broker.js";
|
|
26
29
|
import {
|
|
27
30
|
assertMetadataWritable,
|
|
28
31
|
deleteCredentialMetadata,
|
|
@@ -38,41 +41,6 @@ import { toPolicyFromInput, validatePolicyInput } from "./policy-validate.js";
|
|
|
38
41
|
|
|
39
42
|
const log = getLogger("credential-vault");
|
|
40
43
|
|
|
41
|
-
function isSlackChannelCredential(
|
|
42
|
-
service: string,
|
|
43
|
-
field: string,
|
|
44
|
-
): field is "bot_token" | "app_token" | "user_token" {
|
|
45
|
-
return (
|
|
46
|
-
service === "slack_channel" &&
|
|
47
|
-
(field === "bot_token" || field === "app_token" || field === "user_token")
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function storeSlackChannelCredential(
|
|
52
|
-
field: "bot_token" | "app_token" | "user_token",
|
|
53
|
-
value: string,
|
|
54
|
-
): Promise<SlackChannelConfigResult> {
|
|
55
|
-
if (field === "bot_token") {
|
|
56
|
-
return setSlackChannelConfig(value, undefined);
|
|
57
|
-
}
|
|
58
|
-
if (field === "app_token") {
|
|
59
|
-
return setSlackChannelConfig(undefined, value);
|
|
60
|
-
}
|
|
61
|
-
return setSlackChannelConfig(undefined, undefined, value);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function formatSlackChannelStatus(result: SlackChannelConfigResult): string {
|
|
65
|
-
if (result.connected) {
|
|
66
|
-
const teamLabel = result.teamName ?? "Slack";
|
|
67
|
-
const botLabel = result.botUsername ? ` (@${result.botUsername})` : "";
|
|
68
|
-
return ` Slack channel connected to ${teamLabel}${botLabel}.`;
|
|
69
|
-
}
|
|
70
|
-
if (result.warning) {
|
|
71
|
-
return ` ${result.warning}`;
|
|
72
|
-
}
|
|
73
|
-
return "";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
44
|
export const credentialStoreTool = {
|
|
77
45
|
name: "credential_store",
|
|
78
46
|
description:
|
|
@@ -347,8 +315,8 @@ export const credentialStoreTool = {
|
|
|
347
315
|
}
|
|
348
316
|
try {
|
|
349
317
|
upsertCredentialMetadata(service, field, {
|
|
350
|
-
allowedTools:
|
|
351
|
-
allowedDomains:
|
|
318
|
+
allowedTools: policyInput.allowed_tools,
|
|
319
|
+
allowedDomains: policyInput.allowed_domains,
|
|
352
320
|
usageDescription: policy.usageDescription,
|
|
353
321
|
alias,
|
|
354
322
|
injectionTemplates,
|
|
@@ -701,106 +669,27 @@ export const credentialStoreTool = {
|
|
|
701
669
|
};
|
|
702
670
|
}
|
|
703
671
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
content:
|
|
721
|
-
"Error: one-time send is not enabled. Set secretDetection.allowOneTimeSend to true in config.",
|
|
722
|
-
isError: true,
|
|
723
|
-
};
|
|
724
|
-
}
|
|
725
|
-
// Ensure metadata exists so broker policy checks work, but don't
|
|
726
|
-
// overwrite an existing record - a stored credential's policy should
|
|
727
|
-
// not be silently replaced by the transient prompt's policy.
|
|
728
|
-
// Metadata must be written before injecting the transient value so
|
|
729
|
-
// we never leave a dangling value that fails policy checks.
|
|
730
|
-
if (!getCredentialMetadata(service, field)) {
|
|
731
|
-
try {
|
|
732
|
-
upsertCredentialMetadata(service, field, {
|
|
733
|
-
allowedTools: promptPolicy.allowedTools,
|
|
734
|
-
allowedDomains: promptPolicy.allowedDomains,
|
|
735
|
-
usageDescription: promptPolicy.usageDescription,
|
|
736
|
-
injectionTemplates: promptInjectionTemplates,
|
|
737
|
-
});
|
|
738
|
-
} catch (err) {
|
|
739
|
-
// Without metadata the broker's policy checks will reject usage,
|
|
740
|
-
// so the transient value would be silently unusable. Fail loudly.
|
|
741
|
-
log.error(
|
|
742
|
-
{ service, field, err },
|
|
743
|
-
"metadata write failed for transient credential",
|
|
744
|
-
);
|
|
745
|
-
return {
|
|
746
|
-
content: `Error: failed to write credential metadata for ${service}/${field}; the one-time value was discarded.`,
|
|
747
|
-
isError: true,
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
// Inject into broker for one-time use by the next tool call, then discard
|
|
752
|
-
credentialBroker.injectTransient(service, field, result.value);
|
|
753
|
-
log.info(
|
|
754
|
-
{ service, field, delivery: "transient_send" },
|
|
755
|
-
"One-time secret delivery used",
|
|
756
|
-
);
|
|
672
|
+
const persisted = await persistPromptedCredential({
|
|
673
|
+
service,
|
|
674
|
+
field,
|
|
675
|
+
value: result.value,
|
|
676
|
+
delivery: result.delivery,
|
|
677
|
+
policy: {
|
|
678
|
+
allowedTools: promptPolicyInput.allowed_tools,
|
|
679
|
+
allowedDomains: promptPolicyInput.allowed_domains,
|
|
680
|
+
usageDescription: promptPolicy.usageDescription,
|
|
681
|
+
injectionTemplates: promptInjectionTemplates,
|
|
682
|
+
},
|
|
683
|
+
});
|
|
684
|
+
if (persisted.outcome === "error") {
|
|
685
|
+
return { content: `Error: ${persisted.message}`, isError: true };
|
|
686
|
+
}
|
|
687
|
+
if (persisted.outcome === "transient") {
|
|
757
688
|
return {
|
|
758
689
|
content: `One-time credential provided for ${service}/${field}. The value was NOT saved to the vault and will be consumed by the next operation.`,
|
|
759
690
|
isError: false,
|
|
760
691
|
};
|
|
761
692
|
}
|
|
762
|
-
|
|
763
|
-
let slackChannelResult: SlackChannelConfigResult | undefined;
|
|
764
|
-
if (isSlackChannelCredential(service, field)) {
|
|
765
|
-
slackChannelResult = await storeSlackChannelCredential(
|
|
766
|
-
field,
|
|
767
|
-
result.value,
|
|
768
|
-
);
|
|
769
|
-
if (!slackChannelResult.success) {
|
|
770
|
-
return {
|
|
771
|
-
content: `Error: ${
|
|
772
|
-
slackChannelResult.error ?? "failed to configure Slack channel"
|
|
773
|
-
}`,
|
|
774
|
-
isError: true,
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
} else {
|
|
778
|
-
// Default: persist to credential store
|
|
779
|
-
const key = credentialKey(service, field);
|
|
780
|
-
const ok = await setSecureKeyAsync(key, result.value);
|
|
781
|
-
if (!ok) {
|
|
782
|
-
return {
|
|
783
|
-
content: "Error: failed to store credential",
|
|
784
|
-
isError: true,
|
|
785
|
-
};
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
try {
|
|
789
|
-
upsertCredentialMetadata(service, field, {
|
|
790
|
-
allowedTools: promptPolicy.allowedTools,
|
|
791
|
-
allowedDomains: promptPolicy.allowedDomains,
|
|
792
|
-
usageDescription: promptPolicy.usageDescription,
|
|
793
|
-
injectionTemplates: promptInjectionTemplates,
|
|
794
|
-
});
|
|
795
|
-
} catch (err) {
|
|
796
|
-
log.warn(
|
|
797
|
-
{ service, field, err },
|
|
798
|
-
"metadata write failed after storing credential",
|
|
799
|
-
);
|
|
800
|
-
}
|
|
801
|
-
if (!isSlackChannelCredential(service, field)) {
|
|
802
|
-
await syncManualTokenConnection(service);
|
|
803
|
-
}
|
|
804
693
|
const promptMeta = getCredentialMetadata(service, field);
|
|
805
694
|
const promptCredIdSuffix = promptMeta
|
|
806
695
|
? ` (credential_id: ${promptMeta.credentialId})`
|
|
@@ -808,8 +697,8 @@ export const credentialStoreTool = {
|
|
|
808
697
|
const promptRetrieveHint = ` Retrieve with: \`assistant credentials reveal --service ${service} --field ${field}\``;
|
|
809
698
|
return {
|
|
810
699
|
content: `Credential stored for ${service}/${field}.${promptCredIdSuffix}${promptRetrieveHint}${
|
|
811
|
-
|
|
812
|
-
? formatSlackChannelStatus(
|
|
700
|
+
persisted.slackChannel
|
|
701
|
+
? formatSlackChannelStatus(persisted.slackChannel)
|
|
813
702
|
: ""
|
|
814
703
|
}`,
|
|
815
704
|
isError: false,
|
package/src/tools/executor.ts
CHANGED
|
@@ -12,6 +12,11 @@ import { TokenExpiredError } from "../security/token-manager.js";
|
|
|
12
12
|
import { PermissionDeniedError, ToolError } from "../util/errors.js";
|
|
13
13
|
import { pathExists, safeStatSync } from "../util/fs.js";
|
|
14
14
|
import { getLogger } from "../util/logger.js";
|
|
15
|
+
import {
|
|
16
|
+
callerOwnsWorkflowRun,
|
|
17
|
+
manifestGrantsSideEffects,
|
|
18
|
+
} from "../workflows/capabilities.js";
|
|
19
|
+
import { getWorkflowRunManager } from "../workflows/run-manager.js";
|
|
15
20
|
import { resolveExecutionTarget } from "./execution-target.js";
|
|
16
21
|
import { executeWithTimeout, safeTimeoutMs } from "./execution-timeout.js";
|
|
17
22
|
import { PermissionChecker } from "./permission-checker.js";
|
|
@@ -138,6 +143,70 @@ export class ToolExecutor {
|
|
|
138
143
|
context.requireFreshApproval = true;
|
|
139
144
|
}
|
|
140
145
|
|
|
146
|
+
// A workflow run whose capability manifest grants side-effecting tools or
|
|
147
|
+
// host functions (beyond the read-only baseline) must prompt at LAUNCH.
|
|
148
|
+
// The manifest is authored and declared by the model, and the run's
|
|
149
|
+
// leaves execute granted tools DIRECTLY (no per-call permission check) -
|
|
150
|
+
// so the launch is the single point at which the user can consent to the
|
|
151
|
+
// grant, which would otherwise bypass the gate those tools hit when the
|
|
152
|
+
// main agent calls them. requireFreshApproval promotes the otherwise
|
|
153
|
+
// low-risk launch to an interactive prompt that cached grants/trust rules
|
|
154
|
+
// cannot silently bypass (run_workflow is not itself a SIDE_EFFECT tool,
|
|
155
|
+
// so forcePromptSideEffects would not fire — requireFreshApproval is the
|
|
156
|
+
// self-sufficient promotion). Read-only runs stay low-risk and silent.
|
|
157
|
+
if (
|
|
158
|
+
name === "run_workflow" &&
|
|
159
|
+
manifestGrantsSideEffects(input.capabilities)
|
|
160
|
+
) {
|
|
161
|
+
context.requireFreshApproval = true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Creating a workflow-mode schedule whose capability manifest grants
|
|
165
|
+
// side-effecting tools or host functions persists that grant for an
|
|
166
|
+
// unattended future run — so the user consents to it at CREATION, the
|
|
167
|
+
// single interactive point in the flow (a triggered run later fires with
|
|
168
|
+
// no live conversation). Same rationale as run_workflow above: the
|
|
169
|
+
// manifest is model-declared and the eventual run's leaves execute granted
|
|
170
|
+
// tools directly (no per-call prompt). Read-only or absent manifests stay
|
|
171
|
+
// low-risk and silent. `schedule_create` is not a SIDE_EFFECT tool, so
|
|
172
|
+
// requireFreshApproval is the self-sufficient promotion.
|
|
173
|
+
if (
|
|
174
|
+
name === "schedule_create" &&
|
|
175
|
+
input.mode === "workflow" &&
|
|
176
|
+
manifestGrantsSideEffects(input.capabilities)
|
|
177
|
+
) {
|
|
178
|
+
context.requireFreshApproval = true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Resuming a workflow whose STORED manifest granted side-effecting tools /
|
|
182
|
+
// host functions restarts unfinished leaves that perform those side
|
|
183
|
+
// effects. The original consent was given at LAUNCH (run_workflow above),
|
|
184
|
+
// but resume is reachable by any actor who can list or guess the run id —
|
|
185
|
+
// so re-require a fresh interactive approval when the target run's stored
|
|
186
|
+
// manifest grants side effects. The other manage_workflows actions
|
|
187
|
+
// (status/abort/list_runs) and resumes of read-only runs stay low-risk and
|
|
188
|
+
// silent. `manage_workflows` is not a SIDE_EFFECT tool, so (like
|
|
189
|
+
// run_workflow) requireFreshApproval is the self-sufficient promotion.
|
|
190
|
+
if (name === "manage_workflows" && input.action === "resume") {
|
|
191
|
+
const targetRunId =
|
|
192
|
+
typeof input.run_id === "string" ? input.run_id : undefined;
|
|
193
|
+
const targetRun = targetRunId
|
|
194
|
+
? getWorkflowRunManager().status(targetRunId)
|
|
195
|
+
: null;
|
|
196
|
+
// Only promote to fresh approval for a run the caller actually OWNS. The
|
|
197
|
+
// tool hides others' runs as not-found, so prompting here for a
|
|
198
|
+
// non-owned run would both leak that the run exists and nag the guardian
|
|
199
|
+
// for a resume that will return not-found. Uses the same ownership scope
|
|
200
|
+
// the tool applies (callerOwnsWorkflowRun) so the gate and the tool agree.
|
|
201
|
+
if (
|
|
202
|
+
targetRun &&
|
|
203
|
+
callerOwnsWorkflowRun(targetRun, context) &&
|
|
204
|
+
manifestGrantsSideEffects(targetRun.capabilities)
|
|
205
|
+
) {
|
|
206
|
+
context.requireFreshApproval = true;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
141
210
|
// A consumed scoped grant is a complete authorization - skip the
|
|
142
211
|
// interactive permission/prompt flow so non-interactive sessions
|
|
143
212
|
// don't auto-deny prompt-gated tools and burn the one-time grant.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `syncFlagGatedTools()` — the follow-up registration that runs after
|
|
3
|
+
* feature-flag overrides are fetched from the gateway. `initializeTools()` runs
|
|
4
|
+
* during startup BEFORE that async fetch resolves, so a flag-enabled assistant
|
|
5
|
+
* would otherwise never expose the gated tools until a restart (which can lose
|
|
6
|
+
* the same race). These tests drive the gated-tool sets directly by mocking
|
|
7
|
+
* `tool-manifest.js`, so they verify the registry behavior in isolation without
|
|
8
|
+
* loading the full tool manifest.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
mock.module("../util/logger.js", () => ({
|
|
14
|
+
getLogger: () =>
|
|
15
|
+
new Proxy({} as Record<string, unknown>, {
|
|
16
|
+
get: () => () => {},
|
|
17
|
+
}),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
// Controllable gated-tool set. `syncFlagGatedTools()` dynamically imports
|
|
21
|
+
// `./tool-manifest.js` and reads only this helper, so mocking it lets a test
|
|
22
|
+
// simulate "flag enabled" (non-empty) vs "flag off / not yet loaded" ([]).
|
|
23
|
+
let cesEnabled: Array<{ name: string }> = [];
|
|
24
|
+
mock.module("./tool-manifest.js", () => ({
|
|
25
|
+
getCesToolsIfEnabled: () => cesEnabled,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
__clearRegistryForTesting,
|
|
30
|
+
getTool,
|
|
31
|
+
syncFlagGatedTools,
|
|
32
|
+
} from "./registry.js";
|
|
33
|
+
|
|
34
|
+
function fakeTool(name: string) {
|
|
35
|
+
return {
|
|
36
|
+
name,
|
|
37
|
+
description: `fake ${name}`,
|
|
38
|
+
category: "test",
|
|
39
|
+
defaultRiskLevel: "low" as never,
|
|
40
|
+
executionTarget: "sandbox" as const,
|
|
41
|
+
input_schema: { type: "object", properties: {}, required: [] },
|
|
42
|
+
execute: async () => ({ content: "ok", isError: false }),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe("syncFlagGatedTools", () => {
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
__clearRegistryForTesting();
|
|
49
|
+
cesEnabled = [];
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("registers a CES tool that startup registration missed (the race)", async () => {
|
|
53
|
+
cesEnabled = [fakeTool("make_authenticated_request")];
|
|
54
|
+
// Simulates initializeTools() having run before the flag was known.
|
|
55
|
+
expect(getTool("make_authenticated_request")).toBeUndefined();
|
|
56
|
+
|
|
57
|
+
await syncFlagGatedTools();
|
|
58
|
+
|
|
59
|
+
expect(getTool("make_authenticated_request")).toBeDefined();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("is idempotent — re-running does not throw or duplicate", async () => {
|
|
63
|
+
cesEnabled = [fakeTool("make_authenticated_request")];
|
|
64
|
+
|
|
65
|
+
await syncFlagGatedTools();
|
|
66
|
+
await syncFlagGatedTools();
|
|
67
|
+
|
|
68
|
+
expect(getTool("make_authenticated_request")).toBeDefined();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("registers nothing when no gated flag is enabled", async () => {
|
|
72
|
+
await syncFlagGatedTools();
|
|
73
|
+
|
|
74
|
+
expect(getTool("make_authenticated_request")).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "../permissions/checker.js";
|
|
10
10
|
import { getAutoApproveThreshold } from "../permissions/gateway-threshold-reader.js";
|
|
11
11
|
import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
12
|
+
import { isFullAccessThreshold } from "../permissions/threshold.js";
|
|
12
13
|
import type {
|
|
13
14
|
ApprovalMode,
|
|
14
15
|
ApprovalReason,
|
|
@@ -183,7 +184,13 @@ export class PermissionChecker {
|
|
|
183
184
|
// cannot bypass the interactive prompt. This is separate from the
|
|
184
185
|
// forcePromptSideEffects path above to ensure requireFreshApproval
|
|
185
186
|
// is self-sufficient without relying on SIDE_EFFECT_TOOLS membership.
|
|
186
|
-
|
|
187
|
+
// At a full-access posture the user has opted into auto-approving even
|
|
188
|
+
// high-risk tools, so the promotion is skipped.
|
|
189
|
+
if (
|
|
190
|
+
context.requireFreshApproval &&
|
|
191
|
+
result.decision === "allow" &&
|
|
192
|
+
!isFullAccessThreshold(riskThreshold)
|
|
193
|
+
) {
|
|
187
194
|
result.decision = "prompt";
|
|
188
195
|
result.reason =
|
|
189
196
|
"Fresh approval required: per-invocation human review enforced";
|
package/src/tools/registry.ts
CHANGED
|
@@ -199,6 +199,17 @@ export function getTool(name: string): Tool | undefined {
|
|
|
199
199
|
return tools.get(name);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* True once {@link initializeTools} has populated the core registry (the core
|
|
204
|
+
* snapshot is captured at the end of init). Callers that must not run before the
|
|
205
|
+
* read-only baseline (`file_read`/`web_fetch`/etc.) exists — e.g. the scheduler
|
|
206
|
+
* deferring boot-time workflow triggers — gate on this. It only ever flips
|
|
207
|
+
* false→true, so a true reading is stable for the process lifetime.
|
|
208
|
+
*/
|
|
209
|
+
export function areCoreToolsInitialized(): boolean {
|
|
210
|
+
return coreToolsSnapshot !== null;
|
|
211
|
+
}
|
|
212
|
+
|
|
202
213
|
export function getAllTools(): Tool[] {
|
|
203
214
|
return Array.from(tools.values());
|
|
204
215
|
}
|
|
@@ -944,6 +955,46 @@ export async function initializeTools(): Promise<void> {
|
|
|
944
955
|
await loadWorkspaceTools();
|
|
945
956
|
}
|
|
946
957
|
|
|
958
|
+
/**
|
|
959
|
+
* Register any feature-flag-gated tools (CES `ces-tools`) that are enabled but
|
|
960
|
+
* not yet in the registry. Idempotent and safe to call repeatedly.
|
|
961
|
+
*
|
|
962
|
+
* `initializeTools()` registers the gated tools once at startup, but feature-flag
|
|
963
|
+
* overrides are fetched from the gateway ASYNCHRONOUSLY and non-blocking (see
|
|
964
|
+
* `lifecycle.ts` — `initFeatureFlagOverrides()` is fired with `void`), so
|
|
965
|
+
* `initializeTools()` can run BEFORE the flag value is known and register
|
|
966
|
+
* nothing. The daemon receives ALL overrides (local `protected/feature-flags.json`
|
|
967
|
+
* and remote/LaunchDarkly alike) through that async fetch, so this race affects
|
|
968
|
+
* every install — without this follow-up sync a flag-enabled assistant would
|
|
969
|
+
* never see the gated tools until a restart, which can lose the same race again.
|
|
970
|
+
* Call this once the override fetch resolves.
|
|
971
|
+
*
|
|
972
|
+
* Enable-direction only: it does not unregister tools whose flag was turned OFF
|
|
973
|
+
* (a live flip to OFF still requires a restart to drop them — the expected
|
|
974
|
+
* behavior for daemon-gating flags). Never throws.
|
|
975
|
+
*/
|
|
976
|
+
export async function syncFlagGatedTools(): Promise<void> {
|
|
977
|
+
try {
|
|
978
|
+
const { getCesToolsIfEnabled } = await import("./tool-manifest.js");
|
|
979
|
+
const enabled = [...getCesToolsIfEnabled()];
|
|
980
|
+
const added: string[] = [];
|
|
981
|
+
for (const tool of enabled) {
|
|
982
|
+
if (tool.name && !tools.has(tool.name)) {
|
|
983
|
+
registerTool(tool);
|
|
984
|
+
added.push(tool.name);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (added.length > 0) {
|
|
988
|
+
log.info({ tools: added }, "Registered flag-gated tools after flag load");
|
|
989
|
+
}
|
|
990
|
+
} catch (err) {
|
|
991
|
+
log.warn(
|
|
992
|
+
{ err },
|
|
993
|
+
"syncFlagGatedTools failed; flag-gated tools may stay unregistered until restart",
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
947
998
|
/**
|
|
948
999
|
* Reset registry to its post-initializeTools() baseline. Exposed
|
|
949
1000
|
* exclusively for test isolation - prevents cross-file contamination
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
2
|
+
import { getConfig } from "../../config/loader.js";
|
|
3
|
+
import { validateScheduleInferenceProfile } from "../../schedule/inference-profile.js";
|
|
1
4
|
import { formatIntegrationSummary } from "../../schedule/integration-status.js";
|
|
2
5
|
import { validateRruleSetLines } from "../../schedule/recurrence-engine.js";
|
|
3
6
|
import { normalizeScheduleSyntax } from "../../schedule/recurrence-types.js";
|
|
@@ -12,9 +15,13 @@ import {
|
|
|
12
15
|
formatLocalDate,
|
|
13
16
|
isValidCronExpression,
|
|
14
17
|
} from "../../schedule/schedule-store.js";
|
|
18
|
+
import {
|
|
19
|
+
CapabilityManifestSchema,
|
|
20
|
+
resolveCapabilities,
|
|
21
|
+
} from "../../workflows/capabilities.js";
|
|
15
22
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
16
23
|
|
|
17
|
-
const VALID_MODES: ScheduleMode[] = ["notify", "execute", "script"];
|
|
24
|
+
const VALID_MODES: ScheduleMode[] = ["notify", "execute", "script", "workflow"];
|
|
18
25
|
const VALID_ROUTING_INTENTS: RoutingIntent[] = [
|
|
19
26
|
"single_channel",
|
|
20
27
|
"multi_channel",
|
|
@@ -49,6 +56,14 @@ export async function executeScheduleCreate(
|
|
|
49
56
|
const maxRetries = input.max_retries as number | undefined;
|
|
50
57
|
const retryBackoffMs = input.retry_backoff_ms as number | undefined;
|
|
51
58
|
const timeoutMs = input.timeout_ms as number | undefined;
|
|
59
|
+
const workflowName =
|
|
60
|
+
typeof input.workflow_name === "string" ? input.workflow_name.trim() : null;
|
|
61
|
+
const workflowArgs = input.workflow_args;
|
|
62
|
+
const inferenceProfile = input.inference_profile as string | undefined;
|
|
63
|
+
|
|
64
|
+
// Validated workflow capability manifest, resolved only for workflow mode.
|
|
65
|
+
// Left null for non-workflow modes so `createSchedule` persists no manifest.
|
|
66
|
+
let capabilities: unknown = null;
|
|
52
67
|
|
|
53
68
|
if (timeoutMs !== undefined) {
|
|
54
69
|
const timeoutError = validateScriptTimeoutMs(timeoutMs);
|
|
@@ -57,6 +72,19 @@ export async function executeScheduleCreate(
|
|
|
57
72
|
}
|
|
58
73
|
}
|
|
59
74
|
|
|
75
|
+
if (inferenceProfile !== undefined) {
|
|
76
|
+
if (typeof inferenceProfile !== "string") {
|
|
77
|
+
return {
|
|
78
|
+
content: "Error: inference_profile must be a string",
|
|
79
|
+
isError: true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const profileError = validateScheduleInferenceProfile(inferenceProfile);
|
|
83
|
+
if (profileError) {
|
|
84
|
+
return { content: `Error: ${profileError}`, isError: true };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
60
88
|
if (!name || typeof name !== "string") {
|
|
61
89
|
return {
|
|
62
90
|
content: "Error: name is required and must be a string",
|
|
@@ -92,6 +120,40 @@ export async function executeScheduleCreate(
|
|
|
92
120
|
isError: true,
|
|
93
121
|
};
|
|
94
122
|
}
|
|
123
|
+
} else if (mode === "workflow") {
|
|
124
|
+
// Workflow mode is gated by the `workflows` flag (a scheduled run would
|
|
125
|
+
// otherwise hard-fail at trigger time) and requires a saved workflow name —
|
|
126
|
+
// mirrors the HTTP route's create-side validation so the assistant-facing
|
|
127
|
+
// path and the settings route enforce the same shape.
|
|
128
|
+
if (!isAssistantFeatureFlagEnabled("workflows", getConfig())) {
|
|
129
|
+
return { content: "Error: workflows are not enabled.", isError: true };
|
|
130
|
+
}
|
|
131
|
+
if (!workflowName) {
|
|
132
|
+
return {
|
|
133
|
+
content:
|
|
134
|
+
"Error: workflow_name is required for workflow mode and must be a non-empty string",
|
|
135
|
+
isError: true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// A workflow schedule may carry a capability manifest — the single consent
|
|
139
|
+
// point for its eventual unattended run. Validate and normalize it here so a
|
|
140
|
+
// schedule can never persist a malformed or forbidden manifest: parse the
|
|
141
|
+
// declared shape, then run the same forbidden/unknown/host-tool checks
|
|
142
|
+
// resolveCapabilities applies at launch. A side-effecting manifest forces a
|
|
143
|
+
// fresh approval at CREATION (see executor.ts).
|
|
144
|
+
if (input.capabilities !== undefined) {
|
|
145
|
+
try {
|
|
146
|
+
const manifest = CapabilityManifestSchema.parse(input.capabilities);
|
|
147
|
+
resolveCapabilities(manifest);
|
|
148
|
+
capabilities = manifest;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
151
|
+
return {
|
|
152
|
+
content: `Error: invalid capabilities manifest: ${msg}`,
|
|
153
|
+
isError: true,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
95
157
|
} else {
|
|
96
158
|
if (!message || typeof message !== "string") {
|
|
97
159
|
return {
|
|
@@ -157,6 +219,10 @@ export async function executeScheduleCreate(
|
|
|
157
219
|
maxRetries,
|
|
158
220
|
retryBackoffMs,
|
|
159
221
|
timeoutMs,
|
|
222
|
+
workflowName,
|
|
223
|
+
workflowArgs,
|
|
224
|
+
capabilities,
|
|
225
|
+
inferenceProfile,
|
|
160
226
|
createdFromConversationId: context.conversationId,
|
|
161
227
|
});
|
|
162
228
|
|
|
@@ -170,6 +236,9 @@ export async function executeScheduleCreate(
|
|
|
170
236
|
` Description: ${job.description}`,
|
|
171
237
|
` Type: one-shot`,
|
|
172
238
|
` Mode: ${job.mode}`,
|
|
239
|
+
...(job.inferenceProfile
|
|
240
|
+
? [` Inference profile: ${job.inferenceProfile}`]
|
|
241
|
+
: []),
|
|
173
242
|
` Fire at: ${fireDate}`,
|
|
174
243
|
` Enabled: ${job.enabled}`,
|
|
175
244
|
` Status: ${job.status}`,
|
|
@@ -241,6 +310,10 @@ export async function executeScheduleCreate(
|
|
|
241
310
|
maxRetries,
|
|
242
311
|
retryBackoffMs,
|
|
243
312
|
timeoutMs,
|
|
313
|
+
workflowName,
|
|
314
|
+
workflowArgs,
|
|
315
|
+
capabilities,
|
|
316
|
+
inferenceProfile,
|
|
244
317
|
createdFromConversationId: context.conversationId,
|
|
245
318
|
});
|
|
246
319
|
|
|
@@ -261,6 +334,9 @@ export async function executeScheduleCreate(
|
|
|
261
334
|
` Description: ${job.description}`,
|
|
262
335
|
` Syntax: ${job.syntax}`,
|
|
263
336
|
` Mode: ${job.mode}`,
|
|
337
|
+
...(job.inferenceProfile
|
|
338
|
+
? [` Inference profile: ${job.inferenceProfile}`]
|
|
339
|
+
: []),
|
|
264
340
|
` Schedule: ${scheduleDescription}${
|
|
265
341
|
job.timezone ? ` (${job.timezone})` : ""
|
|
266
342
|
}`,
|
|
@@ -71,6 +71,7 @@ export async function executeScheduleList(
|
|
|
71
71
|
` Enabled: ${job.enabled}`,
|
|
72
72
|
` Quiet: ${job.quiet}`,
|
|
73
73
|
` Reuse conversation: ${job.reuseConversation}`,
|
|
74
|
+
` Inference profile: ${job.inferenceProfile ?? "default (mainAgent)"}`,
|
|
74
75
|
` Message: ${job.message}`,
|
|
75
76
|
);
|
|
76
77
|
|