@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
package/src/util/platform.ts
CHANGED
|
@@ -72,7 +72,7 @@ export function normalizeAssistantId(assistantId: string): string {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* Returns the internal data directory (
|
|
75
|
+
* Returns the internal data directory ($VELLUM_WORKSPACE_DIR/data). Runtime
|
|
76
76
|
* databases, logs, memory indices, and other internal state live here.
|
|
77
77
|
*/
|
|
78
78
|
export function getDataDir(): string {
|
|
@@ -96,7 +96,7 @@ export function getConfigQuarantineNoticePath(): string {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
* Returns the embedding models directory (
|
|
99
|
+
* Returns the embedding models directory ($VELLUM_WORKSPACE_DIR/embedding-models).
|
|
100
100
|
* Downloaded embedding runtime (onnxruntime-node, transformers bundle, model weights)
|
|
101
101
|
* is stored here, downloaded post-hatch rather than shipped with the app.
|
|
102
102
|
*/
|
|
@@ -113,7 +113,7 @@ export function getSandboxRootDir(): string {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
|
-
* Returns the default sandbox working directory (
|
|
116
|
+
* Returns the default sandbox working directory ($VELLUM_WORKSPACE_DIR).
|
|
117
117
|
* This is the workspace root — tool working directories should use this
|
|
118
118
|
* path unless explicitly overridden.
|
|
119
119
|
*/
|
|
@@ -122,7 +122,7 @@ export function getSandboxWorkingDir(): string {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
* Returns the sounds directory (
|
|
125
|
+
* Returns the sounds directory ($VELLUM_WORKSPACE_DIR/data/sounds).
|
|
126
126
|
* Custom sound files and sound configuration live here.
|
|
127
127
|
*/
|
|
128
128
|
export function getSoundsDir(): string {
|
|
@@ -137,7 +137,7 @@ export function getAvatarDir(): string {
|
|
|
137
137
|
/** Canonical filename for the custom avatar PNG. */
|
|
138
138
|
export const AVATAR_IMAGE_FILENAME = "avatar-image.png";
|
|
139
139
|
|
|
140
|
-
/** Returns the canonical avatar image path (
|
|
140
|
+
/** Returns the canonical avatar image path ($VELLUM_WORKSPACE_DIR/data/avatar/avatar-image.png). */
|
|
141
141
|
export function getAvatarImagePath(): string {
|
|
142
142
|
return join(getAvatarDir(), AVATAR_IMAGE_FILENAME);
|
|
143
143
|
}
|
|
@@ -145,7 +145,7 @@ export function getAvatarImagePath(): string {
|
|
|
145
145
|
/** Canonical filename for the avatar state manifest. */
|
|
146
146
|
export const AVATAR_MANIFEST_FILENAME = "avatar.json";
|
|
147
147
|
|
|
148
|
-
/** Returns the canonical avatar manifest path (
|
|
148
|
+
/** Returns the canonical avatar manifest path ($VELLUM_WORKSPACE_DIR/data/avatar/avatar.json). */
|
|
149
149
|
export function getAvatarManifestPath(): string {
|
|
150
150
|
return join(getAvatarDir(), AVATAR_MANIFEST_FILENAME);
|
|
151
151
|
}
|
|
@@ -203,7 +203,7 @@ export function getProtectedDir(): string {
|
|
|
203
203
|
return join(vellumRoot(), "protected");
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
/** Returns
|
|
206
|
+
/** Returns $VELLUM_WORKSPACE_DIR/signals — the directory for IPC signal files. */
|
|
207
207
|
export function getSignalsDir(): string {
|
|
208
208
|
return join(getWorkspaceDir(), "signals");
|
|
209
209
|
}
|
|
@@ -212,22 +212,22 @@ export function getSignalsDir(): string {
|
|
|
212
212
|
// These expose specific root-level file paths so callers don't need to
|
|
213
213
|
// import getRootDir() directly. getRootDir() is intentionally unexported.
|
|
214
214
|
|
|
215
|
-
/** Returns the path to the daemon stderr log (
|
|
215
|
+
/** Returns the path to the daemon stderr log ($VELLUM_WORKSPACE_DIR/logs/daemon-stderr.log). */
|
|
216
216
|
export function getDaemonStderrLogPath(): string {
|
|
217
217
|
return join(getWorkspaceDir(), "logs", "daemon-stderr.log");
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
/** Returns the path to the daemon startup lock file (
|
|
220
|
+
/** Returns the path to the daemon startup lock file ($VELLUM_WORKSPACE_DIR/daemon-startup.lock). */
|
|
221
221
|
export function getDaemonStartupLockPath(): string {
|
|
222
222
|
return join(getWorkspaceDir(), "daemon-startup.lock");
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
/** Returns the directory for externally-installed packages (
|
|
225
|
+
/** Returns the directory for externally-installed packages ($VELLUM_WORKSPACE_DIR/external). */
|
|
226
226
|
export function getExternalDir(): string {
|
|
227
227
|
return join(getWorkspaceDir(), "external");
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
/** Returns the directory for installed binaries (
|
|
230
|
+
/** Returns the directory for installed binaries ($VELLUM_WORKSPACE_DIR/bin). */
|
|
231
231
|
export function getBinDir(): string {
|
|
232
232
|
return join(getWorkspaceDir(), "bin");
|
|
233
233
|
}
|
|
@@ -237,7 +237,7 @@ export function getDotEnvPath(): string {
|
|
|
237
237
|
return join(vellumRoot(), ".env");
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
/** Returns the path to the embed-worker PID file (
|
|
240
|
+
/** Returns the path to the embed-worker PID file ($VELLUM_WORKSPACE_DIR/embed-worker.pid). */
|
|
241
241
|
export function getEmbedWorkerPidPath(): string {
|
|
242
242
|
return join(getWorkspaceDir(), "embed-worker.pid");
|
|
243
243
|
}
|
|
@@ -273,17 +273,17 @@ export function getWorkspaceDirDisplay(): string {
|
|
|
273
273
|
return abs;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
/** Returns
|
|
276
|
+
/** Returns $VELLUM_WORKSPACE_DIR/config.json */
|
|
277
277
|
export function getWorkspaceConfigPath(): string {
|
|
278
278
|
return join(getWorkspaceDir(), "config.json");
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
/** Returns
|
|
281
|
+
/** Returns $VELLUM_WORKSPACE_DIR/skills */
|
|
282
282
|
export function getWorkspaceSkillsDir(): string {
|
|
283
283
|
return join(getWorkspaceDir(), "skills");
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
/** Returns
|
|
286
|
+
/** Returns $VELLUM_WORKSPACE_DIR/hooks */
|
|
287
287
|
export function getWorkspaceHooksDir(): string {
|
|
288
288
|
return join(getWorkspaceDir(), "hooks");
|
|
289
289
|
}
|
|
@@ -319,12 +319,12 @@ export function getWorkspaceRoutesDir(): string {
|
|
|
319
319
|
return join(getWorkspaceDir(), "routes");
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
/** Returns
|
|
322
|
+
/** Returns $VELLUM_WORKSPACE_DIR/deprecated — transitional files slated for removal. */
|
|
323
323
|
export function getDeprecatedDir(): string {
|
|
324
324
|
return join(getWorkspaceDir(), "deprecated");
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
/** Returns
|
|
327
|
+
/** Returns $VELLUM_WORKSPACE_DIR/conversations */
|
|
328
328
|
export function getConversationsDir(): string {
|
|
329
329
|
return join(getWorkspaceDir(), "conversations");
|
|
330
330
|
}
|
|
@@ -105,6 +105,21 @@ mock.module("../../runtime/background-job-runner.js", () => ({
|
|
|
105
105
|
},
|
|
106
106
|
}));
|
|
107
107
|
|
|
108
|
+
const inventoryCalls: number[] = [];
|
|
109
|
+
const llmProcessedCalls: Array<{
|
|
110
|
+
providerId: string;
|
|
111
|
+
conversationId: string;
|
|
112
|
+
}> = [];
|
|
113
|
+
|
|
114
|
+
mock.module("../telemetry.js", () => ({
|
|
115
|
+
recordWatcherInventoryIfDue: (now: number) => {
|
|
116
|
+
inventoryCalls.push(now);
|
|
117
|
+
},
|
|
118
|
+
recordWatcherLlmProcessed: (providerId: string, conversationId: string) => {
|
|
119
|
+
llmProcessedCalls.push({ providerId, conversationId });
|
|
120
|
+
},
|
|
121
|
+
}));
|
|
122
|
+
|
|
108
123
|
// Import after mocks are in place.
|
|
109
124
|
const { runWatchersOnce } = await import("../engine.js");
|
|
110
125
|
|
|
@@ -156,6 +171,8 @@ beforeEach(() => {
|
|
|
156
171
|
setConvCalls.length = 0;
|
|
157
172
|
dispositionCalls.length = 0;
|
|
158
173
|
runJobCalls.length = 0;
|
|
174
|
+
inventoryCalls.length = 0;
|
|
175
|
+
llmProcessedCalls.length = 0;
|
|
159
176
|
runJobImpl = async () => ({ conversationId: "conv-stub", ok: true });
|
|
160
177
|
});
|
|
161
178
|
|
|
@@ -223,6 +240,9 @@ describe("runWatchersOnce — Phase 2 runBackgroundJob integration", () => {
|
|
|
223
240
|
expect(setConvCalls).toEqual([
|
|
224
241
|
{ watcherId: "watcher-1", conversationId: "conv-success" },
|
|
225
242
|
]);
|
|
243
|
+
expect(llmProcessedCalls).toEqual([
|
|
244
|
+
{ providerId: "linear", conversationId: "conv-success" },
|
|
245
|
+
]);
|
|
226
246
|
expect(dispositionCalls).toHaveLength(2);
|
|
227
247
|
for (const call of dispositionCalls) {
|
|
228
248
|
expect(call.disposition).toBe("silent");
|
|
@@ -267,6 +287,8 @@ describe("runWatchersOnce — Phase 2 runBackgroundJob integration", () => {
|
|
|
267
287
|
// Critical: we must NOT have called setWatcherConversationId with "",
|
|
268
288
|
// which would clobber a valid prior conversation id in the DB.
|
|
269
289
|
expect(setConvCalls).toEqual([]);
|
|
290
|
+
// No conversation was bootstrapped, so no usage breadcrumb either.
|
|
291
|
+
expect(llmProcessedCalls).toEqual([]);
|
|
270
292
|
// Failure path still updates event dispositions.
|
|
271
293
|
expect(dispositionCalls).toHaveLength(1);
|
|
272
294
|
expect(dispositionCalls[0].disposition).toBe("error");
|
|
@@ -280,6 +302,8 @@ describe("runWatchersOnce — Phase 2 runBackgroundJob integration", () => {
|
|
|
280
302
|
|
|
281
303
|
expect(runJobCalls).toHaveLength(0);
|
|
282
304
|
expect(setConvCalls).toHaveLength(0);
|
|
305
|
+
// Inventory telemetry runs on every tick regardless of pending work.
|
|
306
|
+
expect(inventoryCalls).toHaveLength(1);
|
|
283
307
|
});
|
|
284
308
|
|
|
285
309
|
test("malicious payload reaches the runner only inside assistant-role sandwich.content", async () => {
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for watcher usage telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Strategy: stub the checkpoint store, lifecycle-event store, and
|
|
5
|
+
* watcher store via `mock.module()` so we can drive the inventory
|
|
6
|
+
* throttle and event naming without touching the DB.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
const checkpoints = new Map<string, string>();
|
|
12
|
+
const recordedEvents: string[] = [];
|
|
13
|
+
let fakeEnabledWatchers: Array<{ providerId: string }> = [];
|
|
14
|
+
let recordImpl: (name: string) => void = (name) => {
|
|
15
|
+
recordedEvents.push(name);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
mock.module("../../memory/checkpoints.js", () => ({
|
|
19
|
+
getMemoryCheckpoint: (key: string) => checkpoints.get(key) ?? null,
|
|
20
|
+
setMemoryCheckpoint: (key: string, value: string) => {
|
|
21
|
+
checkpoints.set(key, value);
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
mock.module("../../memory/lifecycle-events-store.js", () => ({
|
|
26
|
+
recordLifecycleEvent: (name: string) => {
|
|
27
|
+
recordImpl(name);
|
|
28
|
+
return null;
|
|
29
|
+
},
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
mock.module("../watcher-store.js", () => ({
|
|
33
|
+
listWatchers: () => fakeEnabledWatchers,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
recordWatcherInventoryIfDue,
|
|
38
|
+
recordWatcherLlmProcessed,
|
|
39
|
+
WATCHER_INVENTORY_INTERVAL_MS,
|
|
40
|
+
} = await import("../telemetry.js");
|
|
41
|
+
|
|
42
|
+
const INVENTORY_KEY = "telemetry:watchers:inventory_last_recorded";
|
|
43
|
+
// Realistic epoch base: an absent checkpoint reads as 0, so `now` must be
|
|
44
|
+
// at least one interval past the epoch for the inventory to be due.
|
|
45
|
+
const BASE = 1_700_000_000_000;
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
checkpoints.clear();
|
|
49
|
+
recordedEvents.length = 0;
|
|
50
|
+
fakeEnabledWatchers = [];
|
|
51
|
+
recordImpl = (name) => {
|
|
52
|
+
recordedEvents.push(name);
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("recordWatcherInventoryIfDue", () => {
|
|
57
|
+
test("records one event per enabled watcher when due", () => {
|
|
58
|
+
fakeEnabledWatchers = [
|
|
59
|
+
{ providerId: "gmail" },
|
|
60
|
+
{ providerId: "linear" },
|
|
61
|
+
{ providerId: "gmail" },
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
recordWatcherInventoryIfDue(BASE);
|
|
65
|
+
|
|
66
|
+
expect(recordedEvents).toEqual([
|
|
67
|
+
"watcher_enabled:gmail",
|
|
68
|
+
"watcher_enabled:linear",
|
|
69
|
+
"watcher_enabled:gmail",
|
|
70
|
+
]);
|
|
71
|
+
expect(checkpoints.get(INVENTORY_KEY)).toBe(String(BASE));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("skips when the last inventory is within the interval", () => {
|
|
75
|
+
fakeEnabledWatchers = [{ providerId: "gmail" }];
|
|
76
|
+
checkpoints.set(INVENTORY_KEY, String(BASE));
|
|
77
|
+
|
|
78
|
+
recordWatcherInventoryIfDue(BASE + WATCHER_INVENTORY_INTERVAL_MS - 1);
|
|
79
|
+
|
|
80
|
+
expect(recordedEvents).toEqual([]);
|
|
81
|
+
expect(checkpoints.get(INVENTORY_KEY)).toBe(String(BASE));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("records again once the interval has elapsed", () => {
|
|
85
|
+
fakeEnabledWatchers = [{ providerId: "github" }];
|
|
86
|
+
checkpoints.set(INVENTORY_KEY, String(BASE));
|
|
87
|
+
|
|
88
|
+
recordWatcherInventoryIfDue(BASE + WATCHER_INVENTORY_INTERVAL_MS);
|
|
89
|
+
|
|
90
|
+
expect(recordedEvents).toEqual(["watcher_enabled:github"]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("advances the checkpoint even when no watchers are enabled", () => {
|
|
94
|
+
recordWatcherInventoryIfDue(BASE + 1);
|
|
95
|
+
|
|
96
|
+
expect(recordedEvents).toEqual([]);
|
|
97
|
+
expect(checkpoints.get(INVENTORY_KEY)).toBe(String(BASE + 1));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("swallows storage errors and leaves the checkpoint for retry", () => {
|
|
101
|
+
fakeEnabledWatchers = [{ providerId: "gmail" }];
|
|
102
|
+
recordImpl = () => {
|
|
103
|
+
throw new Error("db unavailable");
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
expect(() => recordWatcherInventoryIfDue(BASE + 2)).not.toThrow();
|
|
107
|
+
// Checkpoint must not advance on failure: the next tick retries
|
|
108
|
+
// instead of skipping a day of inventory.
|
|
109
|
+
expect(checkpoints.get(INVENTORY_KEY)).toBeUndefined();
|
|
110
|
+
|
|
111
|
+
recordImpl = (name) => {
|
|
112
|
+
recordedEvents.push(name);
|
|
113
|
+
};
|
|
114
|
+
recordWatcherInventoryIfDue(BASE + 3);
|
|
115
|
+
|
|
116
|
+
expect(recordedEvents).toEqual(["watcher_enabled:gmail"]);
|
|
117
|
+
expect(checkpoints.get(INVENTORY_KEY)).toBe(String(BASE + 3));
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("recordWatcherLlmProcessed", () => {
|
|
122
|
+
test("embeds provider and conversation id in the event name", () => {
|
|
123
|
+
recordWatcherLlmProcessed("linear", "conv-123");
|
|
124
|
+
|
|
125
|
+
expect(recordedEvents).toEqual(["watcher_llm_processed:linear:conv-123"]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("swallows storage errors", () => {
|
|
129
|
+
recordImpl = () => {
|
|
130
|
+
throw new Error("db unavailable");
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
expect(() => recordWatcherLlmProcessed("gmail", "conv-456")).not.toThrow();
|
|
134
|
+
});
|
|
135
|
+
});
|
package/src/watcher/engine.ts
CHANGED
|
@@ -12,6 +12,10 @@ import { checkForSequenceReplies } from "../sequence/reply-matcher.js";
|
|
|
12
12
|
import { getLogger } from "../util/logger.js";
|
|
13
13
|
import { MAX_CONSECUTIVE_ERRORS, WATCHER_JOB_TIMEOUT_MS } from "./constants.js";
|
|
14
14
|
import { getWatcherProvider } from "./provider-registry.js";
|
|
15
|
+
import {
|
|
16
|
+
recordWatcherInventoryIfDue,
|
|
17
|
+
recordWatcherLlmProcessed,
|
|
18
|
+
} from "./telemetry.js";
|
|
15
19
|
import {
|
|
16
20
|
claimDueWatchers,
|
|
17
21
|
completeWatcherPoll,
|
|
@@ -69,6 +73,8 @@ export async function runWatchersOnce(
|
|
|
69
73
|
const now = Date.now();
|
|
70
74
|
let processed = 0;
|
|
71
75
|
|
|
76
|
+
recordWatcherInventoryIfDue(now);
|
|
77
|
+
|
|
72
78
|
// ── Phase 1: Poll providers for new events ──────────────────────
|
|
73
79
|
const claimed = claimDueWatchers(now);
|
|
74
80
|
for (const watcher of claimed) {
|
|
@@ -269,6 +275,7 @@ export async function runWatchersOnce(
|
|
|
269
275
|
// is empty) — otherwise we'd overwrite a valid prior id with "".
|
|
270
276
|
if (result.conversationId !== "") {
|
|
271
277
|
setWatcherConversationId(watcher.id, result.conversationId);
|
|
278
|
+
recordWatcherLlmProcessed(watcher.providerId, result.conversationId);
|
|
272
279
|
}
|
|
273
280
|
|
|
274
281
|
if (result.ok) {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watcher usage telemetry — lifecycle-event breadcrumbs that let fleet
|
|
3
|
+
* analytics answer "is anyone using watchers, and what do they cost?"
|
|
4
|
+
* ahead of the planned watcher → skills-and-schedules migration.
|
|
5
|
+
*
|
|
6
|
+
* Both event shapes ship through the existing lifecycle-event telemetry
|
|
7
|
+
* pipeline, so they inherit its `collectUsageData` opt-out gate and need
|
|
8
|
+
* no wire-contract or platform-side changes:
|
|
9
|
+
*
|
|
10
|
+
* - `watcher_enabled:<providerId>` — one per enabled watcher, at most
|
|
11
|
+
* once per 24h per daemon. Counts devices that have watchers
|
|
12
|
+
* configured, including ones whose providers rarely produce events.
|
|
13
|
+
* - `watcher_llm_processed:<providerId>:<conversationId>` — one per
|
|
14
|
+
* watcher tick that ran an LLM background job. The conversation id is
|
|
15
|
+
* embedded so analytics can join against `llm_usage` events for exact
|
|
16
|
+
* cost attribution.
|
|
17
|
+
*
|
|
18
|
+
* Telemetry must never break the polling loop, so every entry point
|
|
19
|
+
* swallows storage errors.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
getMemoryCheckpoint,
|
|
24
|
+
setMemoryCheckpoint,
|
|
25
|
+
} from "../memory/checkpoints.js";
|
|
26
|
+
import { recordLifecycleEvent } from "../memory/lifecycle-events-store.js";
|
|
27
|
+
import { getLogger } from "../util/logger.js";
|
|
28
|
+
import { listWatchers } from "./watcher-store.js";
|
|
29
|
+
|
|
30
|
+
const log = getLogger("watcher-telemetry");
|
|
31
|
+
|
|
32
|
+
const INVENTORY_CHECKPOINT_KEY = "telemetry:watchers:inventory_last_recorded";
|
|
33
|
+
export const WATCHER_INVENTORY_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Record one `watcher_enabled:<providerId>` lifecycle event per enabled
|
|
37
|
+
* watcher, at most once per 24h. Called from every watcher tick; the
|
|
38
|
+
* checkpoint advances only after the events are recorded, so a transient
|
|
39
|
+
* storage failure retries on the next tick instead of skipping a day.
|
|
40
|
+
* Retries can duplicate events for watchers recorded before the failure
|
|
41
|
+
* point — an acceptable overcount for best-effort telemetry, where losing
|
|
42
|
+
* a full day's inventory would not be.
|
|
43
|
+
*/
|
|
44
|
+
export function recordWatcherInventoryIfDue(now: number): void {
|
|
45
|
+
try {
|
|
46
|
+
const last = Number(getMemoryCheckpoint(INVENTORY_CHECKPOINT_KEY) ?? "0");
|
|
47
|
+
if (now - last < WATCHER_INVENTORY_INTERVAL_MS) return;
|
|
48
|
+
for (const watcher of listWatchers({ enabledOnly: true })) {
|
|
49
|
+
recordLifecycleEvent(`watcher_enabled:${watcher.providerId}`);
|
|
50
|
+
}
|
|
51
|
+
setMemoryCheckpoint(INVENTORY_CHECKPOINT_KEY, String(now));
|
|
52
|
+
} catch (err) {
|
|
53
|
+
log.warn({ err }, "Failed to record watcher inventory telemetry");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Record a `watcher_llm_processed:<providerId>:<conversationId>`
|
|
59
|
+
* lifecycle event for a watcher tick that bootstrapped an LLM background
|
|
60
|
+
* job. Recorded whenever a conversation was created — even if the job
|
|
61
|
+
* later failed — because tokens may already have been spent.
|
|
62
|
+
*/
|
|
63
|
+
export function recordWatcherLlmProcessed(
|
|
64
|
+
providerId: string,
|
|
65
|
+
conversationId: string,
|
|
66
|
+
): void {
|
|
67
|
+
try {
|
|
68
|
+
recordLifecycleEvent(
|
|
69
|
+
`watcher_llm_processed:${providerId}:${conversationId}`,
|
|
70
|
+
);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
log.warn({ err }, "Failed to record watcher LLM-processed telemetry");
|
|
73
|
+
}
|
|
74
|
+
}
|