@vellumai/assistant 0.8.7 → 0.8.8
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/Dockerfile +20 -4
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/docs/plugins.md +75 -79
- package/examples/plugins/echo/README.md +6 -12
- package/examples/plugins/echo/register.ts +0 -41
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/openapi.yaml +3381 -348
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +34 -39
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-pipeline.test.ts +1 -1
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +313 -1136
- package/src/__tests__/conversation-agent-loop.test.ts +596 -1616
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +26 -5
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +170 -229
- package/src/__tests__/conversation-runtime-workspace.test.ts +3 -24
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -1
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +2 -8
- package/src/__tests__/injector-chain.test.ts +106 -270
- package/src/__tests__/injector-disk-pressure.test.ts +3 -12
- package/src/__tests__/injector-document-comments.test.ts +2 -2
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pipeline-runner.test.ts +29 -39
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-bootstrap.test.ts +13 -28
- package/src/__tests__/plugin-registry.test.ts +0 -27
- package/src/__tests__/plugin-types.test.ts +2 -125
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +2 -2
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/agent/loop.ts +346 -442
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +368 -0
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -20
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +35 -3
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +122 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +3 -6
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +605 -153
- package/src/daemon/conversation-agent-loop.ts +281 -760
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-runtime-assembly.ts +130 -347
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation.ts +32 -14
- package/src/daemon/external-plugins-bootstrap.ts +9 -10
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +39 -10
- package/src/daemon/lifecycle.ts +4 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +4 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +31 -4
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -1
- package/src/memory/v2/consolidation-job.ts +1 -1
- package/src/memory/v3/__tests__/health.test.ts +16 -0
- package/src/memory/v3/__tests__/orchestrate.test.ts +45 -9
- package/src/memory/v3/__tests__/provider-blocks.test.ts +13 -0
- package/src/memory/v3/__tests__/router.test.ts +101 -29
- package/src/memory/v3/__tests__/selector.test.ts +93 -27
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +23 -5
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/llm-retry.ts +32 -0
- package/src/memory/v3/orchestrate.ts +26 -14
- package/src/memory/v3/provider-blocks.ts +15 -5
- package/src/memory/v3/router.ts +48 -42
- package/src/memory/v3/selector.ts +57 -42
- package/src/memory/v3/shadow-plugin.ts +47 -15
- package/src/memory/v3/types.ts +8 -0
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +8 -1
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +1 -15
- package/src/plugins/defaults/injectors/register.ts +243 -74
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +91 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/pipeline.ts +6 -18
- package/src/plugins/registry.ts +8 -25
- package/src/plugins/types.ts +43 -474
- package/src/proactive-artifact/aux-message-injector.ts +3 -3
- package/src/proactive-artifact/job.test.ts +7 -12
- package/src/prompts/__tests__/system-prompt.test.ts +36 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +62 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +5 -1
- package/src/runtime/agent-wake.ts +2 -2
- package/src/runtime/assistant-event-hub.ts +36 -6
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- package/src/runtime/routes/conversation-query-routes.ts +2 -0
- package/src/runtime/routes/conversation-routes.ts +358 -184
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- package/src/runtime/routes/identity-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +208 -17
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +50 -28
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/executor.ts +1 -53
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +9 -1
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +93 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/package.json +0 -15
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* with the platform for this assistant.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
|
|
13
15
|
import { getIsPlatform } from "../../config/env-registry.js";
|
|
14
16
|
import { getConfig } from "../../config/loader.js";
|
|
15
17
|
import {
|
|
@@ -25,6 +27,38 @@ import {
|
|
|
25
27
|
} from "./errors.js";
|
|
26
28
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
27
29
|
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Schemas
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
const WebhooksRegisterRequestSchema = z.object({
|
|
35
|
+
type: z.string(),
|
|
36
|
+
path: z.string().optional(),
|
|
37
|
+
source: z.string().optional(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const WebhooksRegisterResponseSchema = z.object({
|
|
41
|
+
callbackUrl: z.string(),
|
|
42
|
+
type: z.string(),
|
|
43
|
+
path: z.string(),
|
|
44
|
+
mode: z.enum(["platform", "self-hosted"]),
|
|
45
|
+
});
|
|
46
|
+
type WebhooksRegisterResponse = z.infer<typeof WebhooksRegisterResponseSchema>;
|
|
47
|
+
|
|
48
|
+
const WebhookCallbackRouteSchema = z.object({
|
|
49
|
+
id: z.string(),
|
|
50
|
+
assistant_id: z.string(),
|
|
51
|
+
type: z.string(),
|
|
52
|
+
callback_path: z.string(),
|
|
53
|
+
callback_url: z.string(),
|
|
54
|
+
source_identifier: z.string().nullable(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const WebhooksListResponseSchema = z.object({
|
|
58
|
+
routes: z.array(WebhookCallbackRouteSchema),
|
|
59
|
+
});
|
|
60
|
+
type WebhooksListResponse = z.infer<typeof WebhooksListResponseSchema>;
|
|
61
|
+
|
|
28
62
|
// ---------------------------------------------------------------------------
|
|
29
63
|
// Helpers
|
|
30
64
|
// ---------------------------------------------------------------------------
|
|
@@ -49,7 +83,7 @@ function deriveWebhookPath(type: string): string {
|
|
|
49
83
|
|
|
50
84
|
async function handleWebhooksRegister(
|
|
51
85
|
args: RouteHandlerArgs,
|
|
52
|
-
): Promise<
|
|
86
|
+
): Promise<WebhooksRegisterResponse> {
|
|
53
87
|
const { type, path: pathOverride, source } = args.body ?? {};
|
|
54
88
|
|
|
55
89
|
if (!type || typeof type !== "string") {
|
|
@@ -93,7 +127,9 @@ async function handleWebhooksRegister(
|
|
|
93
127
|
};
|
|
94
128
|
}
|
|
95
129
|
|
|
96
|
-
async function handleWebhooksList(
|
|
130
|
+
async function handleWebhooksList(
|
|
131
|
+
_args: RouteHandlerArgs,
|
|
132
|
+
): Promise<WebhooksListResponse> {
|
|
97
133
|
const context = await resolvePlatformCallbackRegistrationContext();
|
|
98
134
|
|
|
99
135
|
if (!context.platformBaseUrl || !context.authHeader) {
|
|
@@ -155,6 +191,8 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
155
191
|
description:
|
|
156
192
|
"Resolves a stable callback URL for a webhook type. On platform-managed assistants, registers the route with the platform gateway. On self-hosted assistants, uses the configured ingress.publicBaseUrl.",
|
|
157
193
|
tags: ["webhooks"],
|
|
194
|
+
requestBody: WebhooksRegisterRequestSchema,
|
|
195
|
+
responseBody: WebhooksRegisterResponseSchema,
|
|
158
196
|
handler: handleWebhooksRegister,
|
|
159
197
|
},
|
|
160
198
|
{
|
|
@@ -169,6 +207,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
169
207
|
description:
|
|
170
208
|
"Lists all webhook callback routes registered with the platform for this assistant.",
|
|
171
209
|
tags: ["webhooks"],
|
|
210
|
+
responseBody: WebhooksListResponseSchema,
|
|
172
211
|
handler: handleWebhooksList,
|
|
173
212
|
},
|
|
174
213
|
];
|
|
@@ -663,6 +663,10 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
663
663
|
description: "Allow hidden files (true/false)",
|
|
664
664
|
},
|
|
665
665
|
],
|
|
666
|
+
responseBody: {
|
|
667
|
+
contentType: "application/octet-stream",
|
|
668
|
+
schema: { type: "string", format: "binary" },
|
|
669
|
+
},
|
|
666
670
|
responseStatus: ({ headers }) => (headers?.["range"] ? "206" : "200"),
|
|
667
671
|
additionalResponses: {
|
|
668
672
|
"416": { description: "Range Not Satisfiable" },
|
|
@@ -61,6 +61,12 @@ function makeConversation() {
|
|
|
61
61
|
setSubagentAllowedTools: mock(() => {}),
|
|
62
62
|
updateClient: mock(() => {}),
|
|
63
63
|
processing: false,
|
|
64
|
+
isProcessing(this: { processing: boolean }) {
|
|
65
|
+
return this.processing;
|
|
66
|
+
},
|
|
67
|
+
setProcessing(this: { processing: boolean }, value: boolean) {
|
|
68
|
+
this.processing = value;
|
|
69
|
+
},
|
|
64
70
|
abortController: null as AbortController | null,
|
|
65
71
|
currentRequestId: null as string | null,
|
|
66
72
|
loadedHistoryTrustClass: undefined as string | undefined,
|
|
@@ -198,7 +198,7 @@ export async function analyzeConversation(
|
|
|
198
198
|
// `currentRequestId` and let two loops mutate the same Conversation
|
|
199
199
|
// state. Skip this run instead — the next upstream trigger will
|
|
200
200
|
// re-enqueue once the in-flight loop finishes.
|
|
201
|
-
if (opts.trigger === "auto" && analysisConversation.
|
|
201
|
+
if (opts.trigger === "auto" && analysisConversation.isProcessing()) {
|
|
202
202
|
log.info(
|
|
203
203
|
{
|
|
204
204
|
sourceConversationId: resolvedId,
|
|
@@ -247,7 +247,7 @@ export async function analyzeConversation(
|
|
|
247
247
|
analysisConversation.updateClient(broadcastMessage, !hasLiveSubscriber);
|
|
248
248
|
|
|
249
249
|
// k. Set up processing state (required by runAgentLoop guard)
|
|
250
|
-
analysisConversation.
|
|
250
|
+
analysisConversation.setProcessing(true);
|
|
251
251
|
analysisConversation.abortController = new AbortController();
|
|
252
252
|
analysisConversation.currentRequestId = crypto.randomUUID();
|
|
253
253
|
|
|
@@ -49,6 +49,7 @@ export interface ScheduleJob {
|
|
|
49
49
|
retryBackoffMs: number;
|
|
50
50
|
/** Script-mode execution timeout override (ms); null = use the default. */
|
|
51
51
|
timeoutMs: number | null;
|
|
52
|
+
createdFromConversationId: string | null;
|
|
52
53
|
createdBy: string;
|
|
53
54
|
mode: ScheduleMode;
|
|
54
55
|
routingIntent: RoutingIntent;
|
|
@@ -102,6 +103,7 @@ export function createSchedule(params: {
|
|
|
102
103
|
maxRetries?: number;
|
|
103
104
|
retryBackoffMs?: number;
|
|
104
105
|
timeoutMs?: number | null;
|
|
106
|
+
createdFromConversationId?: string | null;
|
|
105
107
|
}): ScheduleJob {
|
|
106
108
|
const expression = params.expression ?? params.cronExpression ?? null;
|
|
107
109
|
const isOneShot = expression == null;
|
|
@@ -138,6 +140,7 @@ export function createSchedule(params: {
|
|
|
138
140
|
const maxRetries = params.maxRetries ?? 3;
|
|
139
141
|
const retryBackoffMs = params.retryBackoffMs ?? 60000;
|
|
140
142
|
const timeoutMs = params.timeoutMs ?? null;
|
|
143
|
+
const createdFromConversationId = params.createdFromConversationId ?? null;
|
|
141
144
|
|
|
142
145
|
let nextRunAt: number;
|
|
143
146
|
if (isOneShot) {
|
|
@@ -165,6 +168,7 @@ export function createSchedule(params: {
|
|
|
165
168
|
maxRetries,
|
|
166
169
|
retryBackoffMs,
|
|
167
170
|
timeoutMs,
|
|
171
|
+
createdFromConversationId,
|
|
168
172
|
createdBy: params.createdBy ?? "agent",
|
|
169
173
|
mode,
|
|
170
174
|
routingIntent,
|
|
@@ -264,6 +268,7 @@ export function updateSchedule(
|
|
|
264
268
|
maxRetries?: number;
|
|
265
269
|
retryBackoffMs?: number;
|
|
266
270
|
timeoutMs?: number | null;
|
|
271
|
+
createdFromConversationId?: string | null;
|
|
267
272
|
},
|
|
268
273
|
): ScheduleJob | null {
|
|
269
274
|
const db = getDb();
|
|
@@ -329,6 +334,8 @@ export function updateSchedule(
|
|
|
329
334
|
if (updates.retryBackoffMs !== undefined)
|
|
330
335
|
set.retryBackoffMs = updates.retryBackoffMs;
|
|
331
336
|
if (updates.timeoutMs !== undefined) set.timeoutMs = updates.timeoutMs;
|
|
337
|
+
if (updates.createdFromConversationId !== undefined)
|
|
338
|
+
set.createdFromConversationId = updates.createdFromConversationId;
|
|
332
339
|
|
|
333
340
|
// Recompute nextRunAt if schedule timing may have changed (only for recurring)
|
|
334
341
|
if (
|
|
@@ -591,7 +598,7 @@ export function cancelSchedule(id: string): boolean {
|
|
|
591
598
|
|
|
592
599
|
export function createScheduleRun(
|
|
593
600
|
jobId: string,
|
|
594
|
-
conversationId: string,
|
|
601
|
+
conversationId: string | null,
|
|
595
602
|
): string {
|
|
596
603
|
const db = getDb();
|
|
597
604
|
const id = uuid();
|
|
@@ -613,6 +620,17 @@ export function createScheduleRun(
|
|
|
613
620
|
return id;
|
|
614
621
|
}
|
|
615
622
|
|
|
623
|
+
export function setScheduleRunConversationId(
|
|
624
|
+
runId: string,
|
|
625
|
+
conversationId: string,
|
|
626
|
+
): void {
|
|
627
|
+
const db = getDb();
|
|
628
|
+
db.update(scheduleRuns)
|
|
629
|
+
.set({ conversationId })
|
|
630
|
+
.where(eq(scheduleRuns.id, runId))
|
|
631
|
+
.run();
|
|
632
|
+
}
|
|
633
|
+
|
|
616
634
|
export function completeScheduleRun(
|
|
617
635
|
runId: string,
|
|
618
636
|
result: { status: "ok" | "error"; output?: string; error?: string },
|
|
@@ -977,6 +995,7 @@ function parseJobRow(row: typeof scheduleJobs.$inferSelect): ScheduleJob {
|
|
|
977
995
|
maxRetries: row.maxRetries ?? 3,
|
|
978
996
|
retryBackoffMs: row.retryBackoffMs ?? 60000,
|
|
979
997
|
timeoutMs: row.timeoutMs ?? null,
|
|
998
|
+
createdFromConversationId: row.createdFromConversationId ?? null,
|
|
980
999
|
createdBy: row.createdBy,
|
|
981
1000
|
mode: (row.mode ?? "execute") as ScheduleMode,
|
|
982
1001
|
routingIntent: (row.routingIntent ?? "all_channels") as RoutingIntent,
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { rawAll } from "../memory/raw-query.js";
|
|
2
|
+
import { buildScheduleAttributionSubquery } from "../memory/schedule-attribution-sql.js";
|
|
3
|
+
|
|
4
|
+
export interface ScheduleUsageSummary {
|
|
5
|
+
scheduleId: string;
|
|
6
|
+
runCount: number;
|
|
7
|
+
totalEstimatedCostUsd: number;
|
|
8
|
+
eventCount: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ScheduleUsageSummaryRow {
|
|
12
|
+
schedule_id: string;
|
|
13
|
+
run_count: number;
|
|
14
|
+
total_estimated_cost_usd: number | null;
|
|
15
|
+
event_count: number | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getScheduleUsageSummaries({
|
|
19
|
+
from,
|
|
20
|
+
to,
|
|
21
|
+
}: {
|
|
22
|
+
from: number;
|
|
23
|
+
to: number;
|
|
24
|
+
}): ScheduleUsageSummary[] {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const scheduleAttribution = buildScheduleAttributionSubquery({
|
|
27
|
+
eventAlias: "e",
|
|
28
|
+
now,
|
|
29
|
+
selectExpression: "schedule_attr_runs.job_id",
|
|
30
|
+
});
|
|
31
|
+
const rows = rawAll<ScheduleUsageSummaryRow>(
|
|
32
|
+
/*sql*/ `
|
|
33
|
+
WITH run_counts AS (
|
|
34
|
+
SELECT
|
|
35
|
+
job_id AS schedule_id,
|
|
36
|
+
COUNT(*) AS run_count
|
|
37
|
+
FROM cron_runs
|
|
38
|
+
WHERE started_at >= ?
|
|
39
|
+
AND started_at <= ?
|
|
40
|
+
GROUP BY job_id
|
|
41
|
+
),
|
|
42
|
+
attributed_usage AS (
|
|
43
|
+
SELECT
|
|
44
|
+
e.estimated_cost_usd,
|
|
45
|
+
COALESCE(e.llm_call_count, 1) AS event_count,
|
|
46
|
+
${scheduleAttribution.sql} AS schedule_id
|
|
47
|
+
FROM llm_usage_events e
|
|
48
|
+
WHERE e.created_at >= ?
|
|
49
|
+
AND e.created_at <= ?
|
|
50
|
+
),
|
|
51
|
+
usage_totals AS (
|
|
52
|
+
SELECT
|
|
53
|
+
schedule_id,
|
|
54
|
+
COALESCE(SUM(estimated_cost_usd), 0) AS total_estimated_cost_usd,
|
|
55
|
+
COALESCE(SUM(event_count), 0) AS event_count
|
|
56
|
+
FROM attributed_usage
|
|
57
|
+
WHERE schedule_id IS NOT NULL
|
|
58
|
+
GROUP BY schedule_id
|
|
59
|
+
)
|
|
60
|
+
SELECT
|
|
61
|
+
cron_jobs.id AS schedule_id,
|
|
62
|
+
COALESCE(run_counts.run_count, 0) AS run_count,
|
|
63
|
+
COALESCE(usage_totals.total_estimated_cost_usd, 0) AS total_estimated_cost_usd,
|
|
64
|
+
COALESCE(usage_totals.event_count, 0) AS event_count
|
|
65
|
+
FROM cron_jobs
|
|
66
|
+
LEFT JOIN run_counts ON run_counts.schedule_id = cron_jobs.id
|
|
67
|
+
LEFT JOIN usage_totals ON usage_totals.schedule_id = cron_jobs.id
|
|
68
|
+
ORDER BY cron_jobs.created_at ASC, cron_jobs.id ASC
|
|
69
|
+
`,
|
|
70
|
+
from,
|
|
71
|
+
to,
|
|
72
|
+
...scheduleAttribution.params,
|
|
73
|
+
from,
|
|
74
|
+
to,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return rows.map((row) => ({
|
|
78
|
+
scheduleId: row.schedule_id,
|
|
79
|
+
runCount: row.run_count,
|
|
80
|
+
totalEstimatedCostUsd: row.total_estimated_cost_usd ?? 0,
|
|
81
|
+
eventCount: row.event_count ?? 0,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
type RoutingIntent,
|
|
29
29
|
type ScheduleJob,
|
|
30
30
|
scheduleRetry,
|
|
31
|
+
setScheduleRunConversationId,
|
|
31
32
|
} from "./schedule-store.js";
|
|
32
33
|
|
|
33
34
|
const log = getLogger("scheduler");
|
|
@@ -442,6 +443,7 @@ export async function runScheduleDueWorkOnce(
|
|
|
442
443
|
job.syntax === "rrule" &&
|
|
443
444
|
job.expression != null &&
|
|
444
445
|
hasSetConstructs(job.expression);
|
|
446
|
+
const runId = createScheduleRun(job.id, null);
|
|
445
447
|
let failed = false;
|
|
446
448
|
try {
|
|
447
449
|
log.info(
|
|
@@ -472,14 +474,13 @@ export async function runScheduleDueWorkOnce(
|
|
|
472
474
|
},
|
|
473
475
|
);
|
|
474
476
|
|
|
477
|
+
setScheduleRunConversationId(runId, result.conversationId);
|
|
475
478
|
onScheduleConversationCreated?.({
|
|
476
479
|
conversationId: result.conversationId,
|
|
477
480
|
scheduleJobId: job.id,
|
|
478
481
|
title: result.status === "failed" ? `${job.name}: Error` : job.name,
|
|
479
482
|
});
|
|
480
483
|
|
|
481
|
-
// Track the schedule run using the task's conversation
|
|
482
|
-
const runId = createScheduleRun(job.id, result.conversationId);
|
|
483
484
|
if (result.status === "failed") {
|
|
484
485
|
const errorMessage = result.error ?? "Task run failed";
|
|
485
486
|
completeScheduleRun(runId, {
|
|
@@ -530,7 +531,7 @@ export async function runScheduleDueWorkOnce(
|
|
|
530
531
|
scheduleJobId: job.id,
|
|
531
532
|
title: `${job.name}: Error`,
|
|
532
533
|
});
|
|
533
|
-
|
|
534
|
+
setScheduleRunConversationId(runId, fallbackConversation.id);
|
|
534
535
|
completeScheduleRun(runId, { status: "error", error: message });
|
|
535
536
|
emitTaskActivityFailed({
|
|
536
537
|
taskId,
|
|
@@ -584,6 +585,8 @@ export async function runScheduleDueWorkOnce(
|
|
|
584
585
|
let ok: boolean;
|
|
585
586
|
let errorMsg: string | undefined;
|
|
586
587
|
const conversationReused = reusedConversationId != null;
|
|
588
|
+
let runConversationId = reusedConversationId;
|
|
589
|
+
const runId = createScheduleRun(job.id, reusedConversationId);
|
|
587
590
|
|
|
588
591
|
if (reusedConversationId) {
|
|
589
592
|
// Reuse path: keep using the injected `processMessage` callback so the
|
|
@@ -625,6 +628,8 @@ export async function runScheduleDueWorkOnce(
|
|
|
625
628
|
scheduleJobId: job.id,
|
|
626
629
|
suppressFailureNotifications: job.quiet === true,
|
|
627
630
|
onConversationCreated: (newConversationId) => {
|
|
631
|
+
runConversationId = newConversationId;
|
|
632
|
+
setScheduleRunConversationId(runId, newConversationId);
|
|
628
633
|
onScheduleConversationCreated?.({
|
|
629
634
|
conversationId: newConversationId,
|
|
630
635
|
scheduleJobId: job.id,
|
|
@@ -641,12 +646,14 @@ export async function runScheduleDueWorkOnce(
|
|
|
641
646
|
!result.ok && result.conversationId === ""
|
|
642
647
|
? `bootstrap-error:${job.id}`
|
|
643
648
|
: result.conversationId;
|
|
649
|
+
if (runConversationId !== conversationId) {
|
|
650
|
+
runConversationId = conversationId;
|
|
651
|
+
setScheduleRunConversationId(runId, conversationId);
|
|
652
|
+
}
|
|
644
653
|
ok = result.ok;
|
|
645
654
|
errorMsg = result.error?.message;
|
|
646
655
|
}
|
|
647
656
|
|
|
648
|
-
const runId = createScheduleRun(job.id, conversationId);
|
|
649
|
-
|
|
650
657
|
if (ok) {
|
|
651
658
|
completeScheduleRun(runId, { status: "ok" });
|
|
652
659
|
if (isOneShot) completeOneShot(job.id);
|
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
import { getLogger } from "../util/logger.js";
|
|
38
38
|
import { getCachedCatalogSync, getCatalog } from "./catalog-cache.js";
|
|
39
39
|
import { type CatalogSkill, getRepoSkillsDir } from "./catalog-install.js";
|
|
40
|
-
import { inferCategory } from "./category-inference.js";
|
|
41
40
|
import type { SkillFileProvider } from "./skill-file-provider.js";
|
|
42
41
|
|
|
43
42
|
const log = getLogger("catalog-files");
|
|
@@ -503,11 +502,12 @@ export function catalogSkillToSlim(cs: CatalogSkill): SlimSkillResponse {
|
|
|
503
502
|
id: cs.id,
|
|
504
503
|
name,
|
|
505
504
|
description: cs.description,
|
|
505
|
+
icon: cs.icon ?? cs.metadata?.icon,
|
|
506
506
|
emoji: cs.emoji ?? cs.metadata?.emoji,
|
|
507
507
|
kind: "catalog",
|
|
508
508
|
origin: "vellum",
|
|
509
509
|
status: "available",
|
|
510
|
-
category:
|
|
510
|
+
category: cs.metadata?.vellum?.category ?? "system",
|
|
511
511
|
};
|
|
512
512
|
}
|
|
513
513
|
|
|
@@ -29,17 +29,20 @@ export interface CatalogSkill {
|
|
|
29
29
|
id: string;
|
|
30
30
|
name: string;
|
|
31
31
|
description: string;
|
|
32
|
+
icon?: string;
|
|
32
33
|
emoji?: string;
|
|
33
34
|
includes?: string[];
|
|
34
35
|
version?: string;
|
|
35
36
|
updatedAt?: string;
|
|
36
37
|
metadata?: {
|
|
38
|
+
icon?: string;
|
|
37
39
|
emoji?: string;
|
|
38
40
|
vellum?: {
|
|
39
41
|
"display-name"?: string;
|
|
40
42
|
"activation-hints"?: string[];
|
|
41
43
|
"avoid-when"?: string[];
|
|
42
44
|
"feature-flag"?: string;
|
|
45
|
+
category?: string;
|
|
43
46
|
};
|
|
44
47
|
};
|
|
45
48
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
|
|
6
|
+
import { getPlatformBaseUrl } from "../config/env.js";
|
|
7
|
+
import { getLogger } from "../util/logger.js";
|
|
8
|
+
import { getRepoSkillsDir } from "./catalog-install.js";
|
|
9
|
+
|
|
10
|
+
const log = getLogger("categories-cache");
|
|
11
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
12
|
+
|
|
13
|
+
export interface SkillCategoryDef {
|
|
14
|
+
slug: string;
|
|
15
|
+
label: string;
|
|
16
|
+
description: string;
|
|
17
|
+
icon: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let cachedCategories: SkillCategoryDef[] | null = null;
|
|
21
|
+
let cacheTimestamp = 0;
|
|
22
|
+
|
|
23
|
+
async function fetchCategories(): Promise<SkillCategoryDef[]> {
|
|
24
|
+
const platformUrl = getPlatformBaseUrl();
|
|
25
|
+
const url = `${platformUrl}/v1/skills/categories/`;
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
signal: AbortSignal.timeout(10000),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Platform API error ${response.status}: ${response.statusText}`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const data = (await response.json()) as {
|
|
37
|
+
categories: SkillCategoryDef[];
|
|
38
|
+
};
|
|
39
|
+
if (!Array.isArray(data.categories)) {
|
|
40
|
+
throw new Error("Platform categories response has invalid categories array");
|
|
41
|
+
}
|
|
42
|
+
return data.categories.filter(
|
|
43
|
+
(c): c is SkillCategoryDef =>
|
|
44
|
+
!!c && typeof c.slug === "string" && typeof c.label === "string",
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readLocalCategories(repoSkillsDir: string): SkillCategoryDef[] {
|
|
49
|
+
try {
|
|
50
|
+
const raw = readFileSync(
|
|
51
|
+
join(repoSkillsDir, "skill-categories-catalog.yaml"),
|
|
52
|
+
"utf-8",
|
|
53
|
+
);
|
|
54
|
+
const parsed = parseYaml(raw) as { categories?: SkillCategoryDef[] };
|
|
55
|
+
if (!Array.isArray(parsed?.categories)) return [];
|
|
56
|
+
return parsed.categories.filter(
|
|
57
|
+
(c): c is SkillCategoryDef =>
|
|
58
|
+
!!c && typeof c.slug === "string" && typeof c.label === "string",
|
|
59
|
+
);
|
|
60
|
+
} catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function getCategories(): Promise<SkillCategoryDef[]> {
|
|
66
|
+
if (cachedCategories && Date.now() - cacheTimestamp < CACHE_TTL_MS) {
|
|
67
|
+
return cachedCategories;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const repoSkillsDir = getRepoSkillsDir();
|
|
71
|
+
const local = repoSkillsDir ? readLocalCategories(repoSkillsDir) : [];
|
|
72
|
+
let categories: SkillCategoryDef[];
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const remote = await fetchCategories();
|
|
76
|
+
if (local.length > 0) {
|
|
77
|
+
const localSlugs = new Set(local.map((c) => c.slug));
|
|
78
|
+
categories = [
|
|
79
|
+
...local,
|
|
80
|
+
...remote.filter((c) => !localSlugs.has(c.slug)),
|
|
81
|
+
];
|
|
82
|
+
} else {
|
|
83
|
+
categories = remote;
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (cachedCategories) {
|
|
87
|
+
log.warn(
|
|
88
|
+
{ err },
|
|
89
|
+
"Failed to fetch skill categories, keeping stale cache",
|
|
90
|
+
);
|
|
91
|
+
cacheTimestamp = Date.now();
|
|
92
|
+
return cachedCategories;
|
|
93
|
+
}
|
|
94
|
+
if (local.length > 0) {
|
|
95
|
+
log.warn(
|
|
96
|
+
{ err },
|
|
97
|
+
"Failed to fetch skill categories, falling back to bundled local catalog",
|
|
98
|
+
);
|
|
99
|
+
categories = local;
|
|
100
|
+
} else {
|
|
101
|
+
log.warn({ err }, "Failed to fetch skill categories, returning empty");
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
cachedCategories = categories;
|
|
107
|
+
cacheTimestamp = Date.now();
|
|
108
|
+
return categories;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getCachedCategoriesSync(): SkillCategoryDef[] {
|
|
112
|
+
return cachedCategories ?? [];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function invalidateCategoriesCache(): void {
|
|
116
|
+
cachedCategories = null;
|
|
117
|
+
cacheTimestamp = 0;
|
|
118
|
+
}
|
|
@@ -17,7 +17,6 @@ import type { SlimSkillResponse } from "../daemon/message-types/skills.js";
|
|
|
17
17
|
import { isTextMimeType as isTextMime } from "../runtime/routes/workspace-utils.js";
|
|
18
18
|
import { getLogger } from "../util/logger.js";
|
|
19
19
|
import type { SkillFileEntry } from "./catalog-files.js";
|
|
20
|
-
import { inferCategory } from "./category-inference.js";
|
|
21
20
|
import {
|
|
22
21
|
clawhubInspect,
|
|
23
22
|
clawhubInspectFile,
|
|
@@ -199,7 +198,7 @@ export function createClawhubProvider(): SkillFileProvider {
|
|
|
199
198
|
kind: "catalog",
|
|
200
199
|
status: "available",
|
|
201
200
|
origin: "clawhub",
|
|
202
|
-
category:
|
|
201
|
+
category: "integrations",
|
|
203
202
|
slug: data.skill.slug,
|
|
204
203
|
author: data.owner?.handle ?? "",
|
|
205
204
|
stars: data.stats?.stars ?? 0,
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
sanitizeRelativePath,
|
|
25
25
|
SKIP_DIRS,
|
|
26
26
|
} from "./catalog-files.js";
|
|
27
|
-
import { inferCategory } from "./category-inference.js";
|
|
28
27
|
import type { SkillFileProvider } from "./skill-file-provider.js";
|
|
29
28
|
import type { GitHubContentsEntry } from "./skillssh-registry.js";
|
|
30
29
|
import {
|
|
@@ -384,7 +383,7 @@ export function createSkillsShProvider(): SkillFileProvider {
|
|
|
384
383
|
kind: "catalog",
|
|
385
384
|
status: "available",
|
|
386
385
|
origin: "skillssh",
|
|
387
|
-
category:
|
|
386
|
+
category: "integrations",
|
|
388
387
|
slug: skillId,
|
|
389
388
|
sourceRepo: `${source.owner}/${source.repo}`,
|
|
390
389
|
installs: 0,
|
package/src/telemetry/types.ts
CHANGED
|
@@ -185,9 +185,37 @@ export interface OnboardingTelemetryEvent extends TelemetryEventBase {
|
|
|
185
185
|
ab_variant?: string;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Auth-fallback event — aggregated count of requests served via the legacy
|
|
190
|
+
* loopback auth fallback. One event per (guard, path, failure_kind) per flush
|
|
191
|
+
* window. Lets the platform see which deployments still rely on the loopback
|
|
192
|
+
* exemption instead of sending a bearer token.
|
|
193
|
+
*/
|
|
194
|
+
export interface AuthFallbackTelemetryEvent extends TelemetryEventBase {
|
|
195
|
+
type: "auth_fallback";
|
|
196
|
+
/** Which auth guard fell back: `"edge"` | `"edge-scoped"` | `"edge-guardian"`. */
|
|
197
|
+
guard: string;
|
|
198
|
+
/** Request pathname that fell back. */
|
|
199
|
+
path: string;
|
|
200
|
+
/**
|
|
201
|
+
* Why the bearer-token check did not succeed before the fallback:
|
|
202
|
+
* `"missing_authorization"` | `"malformed_authorization"` |
|
|
203
|
+
* `"token_validation_failed"` | `"insufficient_scope"` |
|
|
204
|
+
* `"non_actor_principal"` | `"guardian_mismatch"`.
|
|
205
|
+
*/
|
|
206
|
+
failure_kind: string;
|
|
207
|
+
/** Number of requests that fell back for this key during the window. */
|
|
208
|
+
count: number;
|
|
209
|
+
/** Window start (epoch ms) the count was accumulated over. */
|
|
210
|
+
window_start: number;
|
|
211
|
+
/** Window end (epoch ms) the count was accumulated over. */
|
|
212
|
+
window_end: number;
|
|
213
|
+
}
|
|
214
|
+
|
|
188
215
|
/** Discriminated union of all telemetry event types. */
|
|
189
216
|
export type TelemetryEvent =
|
|
190
217
|
| LlmUsageTelemetryEvent
|
|
191
218
|
| TurnTelemetryEvent
|
|
192
219
|
| LifecycleTelemetryEvent
|
|
193
|
-
| OnboardingTelemetryEvent
|
|
220
|
+
| OnboardingTelemetryEvent
|
|
221
|
+
| AuthFallbackTelemetryEvent;
|