@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
|
@@ -243,6 +243,10 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
243
243
|
summary: "Export a document as PDF",
|
|
244
244
|
description: "Render a document to PDF and return the binary content.",
|
|
245
245
|
tags: ["documents"],
|
|
246
|
+
responseBody: {
|
|
247
|
+
contentType: "application/pdf",
|
|
248
|
+
schema: { type: "string", format: "binary" },
|
|
249
|
+
},
|
|
246
250
|
handler: async ({ pathParams }) => {
|
|
247
251
|
const doc = getDocumentById(pathParams!.id);
|
|
248
252
|
if (!doc) {
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* the subdomain to local config so getAssistantDomain() can use it.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
8
10
|
import { getApexDomain } from "../../config/env.js";
|
|
9
11
|
import {
|
|
10
12
|
loadRawConfig,
|
|
@@ -19,18 +21,36 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
|
19
21
|
|
|
20
22
|
const log = getLogger("domain-routes");
|
|
21
23
|
|
|
22
|
-
// ──
|
|
24
|
+
// ── Schemas ───────────────────────────────────────────────────────────
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
const DomainEntrySchema = z.object({
|
|
27
|
+
id: z.string(),
|
|
28
|
+
subdomain: z.string().optional(),
|
|
29
|
+
domain: z.string().optional(),
|
|
30
|
+
created_at: z.string().optional(),
|
|
31
|
+
created: z.string().optional(),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const DomainListResponseSchema = z.object({
|
|
35
|
+
count: z.number(),
|
|
36
|
+
next: z.string().nullable().optional(),
|
|
37
|
+
previous: z.string().nullable().optional(),
|
|
38
|
+
results: z.array(DomainEntrySchema),
|
|
39
|
+
});
|
|
40
|
+
type DomainListResponse = z.infer<typeof DomainListResponseSchema>;
|
|
41
|
+
|
|
42
|
+
const DomainRegisterResponseSchema = DomainEntrySchema.extend({
|
|
43
|
+
email_error: z.object({ detail: z.string(), code: z.string() }).optional(),
|
|
44
|
+
});
|
|
45
|
+
type DomainRegisterResponse = z.infer<typeof DomainRegisterResponseSchema>;
|
|
46
|
+
|
|
47
|
+
const DomainVerificationStatusResponseSchema = z.object({
|
|
48
|
+
domain: z.string(),
|
|
49
|
+
status: z.string(),
|
|
50
|
+
message: z.string(),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
34
54
|
|
|
35
55
|
async function requireClient(): Promise<VellumPlatformClient> {
|
|
36
56
|
const client = await VellumPlatformClient.create();
|
|
@@ -113,20 +133,11 @@ async function handleDomainRegister({ body = {} }: RouteHandlerArgs) {
|
|
|
113
133
|
throw new BadRequestError(String(detail));
|
|
114
134
|
}
|
|
115
135
|
|
|
116
|
-
const data = (await response.json()) as
|
|
117
|
-
id: string;
|
|
118
|
-
subdomain?: string;
|
|
119
|
-
domain?: string;
|
|
120
|
-
created_at?: string;
|
|
121
|
-
created?: string;
|
|
122
|
-
email_error?: { detail: string; code: string };
|
|
123
|
-
};
|
|
136
|
+
const data = (await response.json()) as DomainRegisterResponse;
|
|
124
137
|
|
|
125
138
|
// Persist the subdomain to config so getAssistantDomain() can use it
|
|
126
139
|
const registeredSubdomain =
|
|
127
|
-
data.subdomain ??
|
|
128
|
-
data.domain?.replace(`.${apexDomain}`, "") ??
|
|
129
|
-
subdomain;
|
|
140
|
+
data.subdomain ?? data.domain?.replace(`.${apexDomain}`, "") ?? subdomain;
|
|
130
141
|
if (registeredSubdomain) {
|
|
131
142
|
const raw = loadRawConfig();
|
|
132
143
|
setNestedValue(raw, "platform.subdomain", registeredSubdomain);
|
|
@@ -146,12 +157,11 @@ async function handleDomainStatus(_args: RouteHandlerArgs) {
|
|
|
146
157
|
// Sync subdomain to config if not already cached
|
|
147
158
|
if (domains.length > 0) {
|
|
148
159
|
const first = domains[0];
|
|
149
|
-
const sub =
|
|
150
|
-
first.subdomain ?? first.domain?.replace(`.${apexDomain}`, "");
|
|
160
|
+
const sub = first.subdomain ?? first.domain?.replace(`.${apexDomain}`, "");
|
|
151
161
|
if (sub) {
|
|
152
162
|
const raw = loadRawConfig();
|
|
153
|
-
const existing = (raw as Record<string, Record<string, unknown>>)
|
|
154
|
-
|
|
163
|
+
const existing = (raw as Record<string, Record<string, unknown>>).platform
|
|
164
|
+
?.subdomain;
|
|
155
165
|
if (existing !== sub) {
|
|
156
166
|
setNestedValue(raw, "platform.subdomain", sub);
|
|
157
167
|
saveRawConfig(raw);
|
|
@@ -162,9 +172,7 @@ async function handleDomainStatus(_args: RouteHandlerArgs) {
|
|
|
162
172
|
return data;
|
|
163
173
|
}
|
|
164
174
|
|
|
165
|
-
async function handleDomainVerificationStatus({
|
|
166
|
-
body = {},
|
|
167
|
-
}: RouteHandlerArgs) {
|
|
175
|
+
async function handleDomainVerificationStatus({ body = {} }: RouteHandlerArgs) {
|
|
168
176
|
const { domain_id } = body as { domain_id?: string };
|
|
169
177
|
if (!domain_id) {
|
|
170
178
|
throw new BadRequestError("domain_id is required");
|
|
@@ -179,9 +187,7 @@ async function handleDomainVerificationStatus({
|
|
|
179
187
|
);
|
|
180
188
|
}
|
|
181
189
|
if (!results?.some((d) => d.id === domain_id)) {
|
|
182
|
-
throw new BadRequestError(
|
|
183
|
-
"domain_id is not registered for this assistant",
|
|
184
|
-
);
|
|
190
|
+
throw new BadRequestError("domain_id is not registered for this assistant");
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
const response = await client.fetch(
|
|
@@ -201,11 +207,9 @@ async function handleDomainVerificationStatus({
|
|
|
201
207
|
);
|
|
202
208
|
}
|
|
203
209
|
|
|
204
|
-
return (await response.json()) as
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
message: string;
|
|
208
|
-
};
|
|
210
|
+
return (await response.json()) as z.infer<
|
|
211
|
+
typeof DomainVerificationStatusResponseSchema
|
|
212
|
+
>;
|
|
209
213
|
}
|
|
210
214
|
|
|
211
215
|
// ── Route definitions ─────────────────────────────────────────────────
|
|
@@ -222,6 +226,11 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
222
226
|
handler: handleDomainRegister,
|
|
223
227
|
summary: "Register a subdomain for this assistant",
|
|
224
228
|
tags: ["domain"],
|
|
229
|
+
requestBody: z.object({
|
|
230
|
+
subdomain: z.string().optional(),
|
|
231
|
+
email_username: z.string().optional(),
|
|
232
|
+
}),
|
|
233
|
+
responseBody: DomainRegisterResponseSchema,
|
|
225
234
|
},
|
|
226
235
|
{
|
|
227
236
|
operationId: "domain_status",
|
|
@@ -234,6 +243,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
234
243
|
handler: handleDomainStatus,
|
|
235
244
|
summary: "Show domain registration and health",
|
|
236
245
|
tags: ["domain"],
|
|
246
|
+
responseBody: DomainListResponseSchema,
|
|
237
247
|
},
|
|
238
248
|
{
|
|
239
249
|
operationId: "domain_verification_status",
|
|
@@ -246,5 +256,9 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
246
256
|
handler: handleDomainVerificationStatus,
|
|
247
257
|
summary: "Get live DNS verification status for a domain",
|
|
248
258
|
tags: ["domain"],
|
|
259
|
+
requestBody: z.object({
|
|
260
|
+
domain_id: z.string(),
|
|
261
|
+
}),
|
|
262
|
+
responseBody: DomainVerificationStatusResponseSchema,
|
|
249
263
|
},
|
|
250
264
|
];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { BadRequestError } from "./errors.js";
|
|
2
|
+
|
|
3
|
+
export interface EpochMillisRange {
|
|
4
|
+
from: number;
|
|
5
|
+
to: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function parseEpochMillisRange(
|
|
9
|
+
queryParams: Record<string, string>,
|
|
10
|
+
): EpochMillisRange {
|
|
11
|
+
const fromRaw = queryParams.from;
|
|
12
|
+
const toRaw = queryParams.to;
|
|
13
|
+
|
|
14
|
+
if (!fromRaw || !toRaw) {
|
|
15
|
+
throw new BadRequestError(
|
|
16
|
+
'Missing required query parameters: "from" and "to" (epoch milliseconds)',
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const from = Number(fromRaw);
|
|
21
|
+
const to = Number(toRaw);
|
|
22
|
+
|
|
23
|
+
if (!Number.isFinite(from) || !Number.isFinite(to)) {
|
|
24
|
+
throw new BadRequestError(
|
|
25
|
+
'"from" and "to" must be valid numbers (epoch milliseconds)',
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (from > to) {
|
|
30
|
+
throw new BadRequestError('"from" must be less than or equal to "to"');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return { from, to };
|
|
34
|
+
}
|
|
@@ -39,9 +39,9 @@ import {
|
|
|
39
39
|
AssistantEventHub,
|
|
40
40
|
assistantEventHub,
|
|
41
41
|
} from "../assistant-event-hub.js";
|
|
42
|
+
import type { ReplaySubscriber } from "../assistant-stream-state.js";
|
|
43
|
+
import { getReplayWindow } from "../assistant-stream-state.js";
|
|
42
44
|
import { ACTOR_PRINCIPALS, GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
|
|
43
|
-
import type { ReplaySubscriber } from "../conversation-stream-state.js";
|
|
44
|
-
import { getReplayWindow } from "../conversation-stream-state.js";
|
|
45
45
|
import { resolveActorPrincipalIdForLocalGuardian } from "../local-actor-identity.js";
|
|
46
46
|
import {
|
|
47
47
|
BadRequestError,
|
|
@@ -143,6 +143,12 @@ export interface SseSubscriberInstrumentation {
|
|
|
143
143
|
clientId: string | null;
|
|
144
144
|
interfaceId: string | null;
|
|
145
145
|
conversationKey: string | null;
|
|
146
|
+
/**
|
|
147
|
+
* Per-connection id assigned by the hub. Distinguishes connections
|
|
148
|
+
* sharing a `clientId` (old vs reconnected) so a shed can be attributed
|
|
149
|
+
* to a specific connection. `null` until the hub subscription is created.
|
|
150
|
+
*/
|
|
151
|
+
connectionId: string | null;
|
|
146
152
|
}
|
|
147
153
|
|
|
148
154
|
export type SseShedReason = "callback_backpressure" | "heartbeat_backpressure";
|
|
@@ -177,6 +183,7 @@ export function buildSseShedSentryContext(
|
|
|
177
183
|
heartbeats_sent: inst.heartbeatsSent,
|
|
178
184
|
client_id: inst.clientId,
|
|
179
185
|
interface_id: inst.interfaceId,
|
|
186
|
+
connection_id: inst.connectionId,
|
|
180
187
|
event_loop_delay_mean_ms: elDelay.mean_ms,
|
|
181
188
|
event_loop_delay_p99_ms: elDelay.p99_ms,
|
|
182
189
|
event_loop_delay_max_ms: elDelay.max_ms,
|
|
@@ -216,6 +223,7 @@ const defaultSseShedReporter: SseShedReporter = (reason, inst) => {
|
|
|
216
223
|
scope.setTag("sse_shed_reason", reason);
|
|
217
224
|
if (inst.clientId) scope.setTag("client_id", inst.clientId);
|
|
218
225
|
if (inst.interfaceId) scope.setTag("interface_id", inst.interfaceId);
|
|
226
|
+
if (inst.connectionId) scope.setTag("connection_id", inst.connectionId);
|
|
219
227
|
scope.setContext("sse_shed", sentryContext);
|
|
220
228
|
Sentry.captureMessage(`sse_subscriber_shed:${reason}`);
|
|
221
229
|
});
|
|
@@ -371,6 +379,7 @@ export function handleSubscribeAssistantEvents(
|
|
|
371
379
|
clientId,
|
|
372
380
|
interfaceId,
|
|
373
381
|
conversationKey: scopeConversationKey,
|
|
382
|
+
connectionId: null,
|
|
374
383
|
};
|
|
375
384
|
|
|
376
385
|
ensureEventLoopDelayMonitorStarted();
|
|
@@ -394,19 +403,6 @@ export function handleSubscribeAssistantEvents(
|
|
|
394
403
|
// replay window we just drained.
|
|
395
404
|
let highWaterReplaySeq = -1;
|
|
396
405
|
|
|
397
|
-
// Per-conversation subscriber-filtered sequence counters. Incremented
|
|
398
|
-
// for each conversation-scoped event this specific subscriber receives
|
|
399
|
-
// (after capability/client/interface targeting), producing a gap-free
|
|
400
|
-
// sequence from the subscriber's perspective. Clients use `clientSeq`
|
|
401
|
-
// for gap detection instead of the global `seq` to avoid false
|
|
402
|
-
// positives from targeted events they never receive.
|
|
403
|
-
const clientSeqCounters = new Map<string, number>();
|
|
404
|
-
function nextClientSeqFor(conversationId: string): number {
|
|
405
|
-
const next = (clientSeqCounters.get(conversationId) ?? 0) + 1;
|
|
406
|
-
clientSeqCounters.set(conversationId, next);
|
|
407
|
-
return next;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
406
|
const callback: AssistantEventCallback = (event) => {
|
|
411
407
|
const controller = controllerRef;
|
|
412
408
|
if (!controller) return;
|
|
@@ -425,11 +421,7 @@ export function handleSubscribeAssistantEvents(
|
|
|
425
421
|
cleanup();
|
|
426
422
|
return;
|
|
427
423
|
}
|
|
428
|
-
|
|
429
|
-
event.conversationId != null && event.seq != null
|
|
430
|
-
? { ...event, clientSeq: nextClientSeqFor(event.conversationId) }
|
|
431
|
-
: event;
|
|
432
|
-
controller.enqueue(encoder.encode(formatSseFrame(frame)));
|
|
424
|
+
controller.enqueue(encoder.encode(formatSseFrame(event)));
|
|
433
425
|
instrumentation.eventsDelivered += 1;
|
|
434
426
|
} catch {
|
|
435
427
|
sub.dispose();
|
|
@@ -461,6 +453,9 @@ export function handleSubscribeAssistantEvents(
|
|
|
461
453
|
...subscriberBase,
|
|
462
454
|
type: "process" as const,
|
|
463
455
|
});
|
|
456
|
+
// Stamp the hub-assigned connection id so a later backpressure shed can be
|
|
457
|
+
// tied back to this specific connection in logs and Sentry.
|
|
458
|
+
instrumentation.connectionId = sub.connectionId;
|
|
464
459
|
} catch (err) {
|
|
465
460
|
if (err instanceof RangeError) {
|
|
466
461
|
throw new ServiceUnavailableError("Too many concurrent connections");
|
|
@@ -479,16 +474,22 @@ export function handleSubscribeAssistantEvents(
|
|
|
479
474
|
return;
|
|
480
475
|
}
|
|
481
476
|
|
|
482
|
-
// Reconnect replay: when the caller passed lastSeenSeq
|
|
483
|
-
//
|
|
484
|
-
//
|
|
477
|
+
// Reconnect replay: when the caller passed lastSeenSeq, deliver
|
|
478
|
+
// any buffered events the client missed before the first
|
|
479
|
+
// heartbeat. `seq` is a single global per-assistant counter, so
|
|
480
|
+
// one cursor resumes the stream regardless of how many
|
|
481
|
+
// conversations are multiplexed on an unfiltered connection.
|
|
482
|
+
// Replay re-applies the subscriber's targeting filter; a
|
|
483
|
+
// conversation-scoped subscription additionally scopes replay to
|
|
484
|
+
// its own conversation (other conversations are never delivered
|
|
485
|
+
// live on that connection, so replaying them would be wrong).
|
|
485
486
|
//
|
|
486
487
|
// If the cursor is older than the ring's oldest entry,
|
|
487
488
|
// `getReplayWindow` returns `null`. We do not surface that to
|
|
488
489
|
// the client over the wire -- the connection just goes live.
|
|
489
490
|
// The client detects the gap from the seq jump on its first
|
|
490
491
|
// live event and refetches via the existing messages API.
|
|
491
|
-
if (lastSeenSeq != null
|
|
492
|
+
if (lastSeenSeq != null) {
|
|
492
493
|
const replaySubscriber: ReplaySubscriber =
|
|
493
494
|
clientId && interfaceId
|
|
494
495
|
? {
|
|
@@ -501,20 +502,13 @@ export function handleSubscribeAssistantEvents(
|
|
|
501
502
|
}
|
|
502
503
|
: { type: "process" };
|
|
503
504
|
const window = getReplayWindow(
|
|
504
|
-
filter.conversationId,
|
|
505
505
|
lastSeenSeq,
|
|
506
506
|
replaySubscriber,
|
|
507
|
+
filter.conversationId,
|
|
507
508
|
);
|
|
508
509
|
if (window !== null) {
|
|
509
510
|
for (const replayed of window) {
|
|
510
|
-
|
|
511
|
-
replayed.conversationId != null && replayed.seq != null
|
|
512
|
-
? {
|
|
513
|
-
...replayed,
|
|
514
|
-
clientSeq: nextClientSeqFor(replayed.conversationId),
|
|
515
|
-
}
|
|
516
|
-
: replayed;
|
|
517
|
-
controller.enqueue(encoder.encode(formatSseFrame(frame)));
|
|
511
|
+
controller.enqueue(encoder.encode(formatSseFrame(replayed)));
|
|
518
512
|
instrumentation.eventsDelivered += 1;
|
|
519
513
|
if (replayed.seq != null && replayed.seq > highWaterReplaySeq) {
|
|
520
514
|
highWaterReplaySeq = replayed.seq;
|
|
@@ -621,7 +615,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
621
615
|
{
|
|
622
616
|
name: "lastSeenSeq",
|
|
623
617
|
description:
|
|
624
|
-
"Optional reconnect cursor: the highest
|
|
618
|
+
"Optional reconnect cursor: the highest global event seq the client has already applied. `seq` is a single per-assistant counter shared across all conversations, so one cursor resumes the stream regardless of how many conversations are multiplexed on the connection. When set, the daemon replays any buffered events with seq > lastSeenSeq (re-applying the subscriber's targeting/scope filter) before going live. If the cursor is older than the ring buffer's oldest entry the connection simply goes live; the client is expected to detect the gap from the next event's seq and refetch via the messages API. Must be a non-negative integer.",
|
|
625
619
|
},
|
|
626
620
|
],
|
|
627
621
|
responseHeaders: {
|
|
@@ -35,7 +35,14 @@ async function gatewayFetch(
|
|
|
35
35
|
|
|
36
36
|
// ── Schemas ─────────────────────────────────────────────────────────────
|
|
37
37
|
|
|
38
|
-
const LEVEL_NAMES = [
|
|
38
|
+
const LEVEL_NAMES = [
|
|
39
|
+
"trace",
|
|
40
|
+
"debug",
|
|
41
|
+
"info",
|
|
42
|
+
"warn",
|
|
43
|
+
"error",
|
|
44
|
+
"fatal",
|
|
45
|
+
] as const;
|
|
39
46
|
|
|
40
47
|
const GatewayLogsTailParams = z
|
|
41
48
|
.object({
|
|
@@ -45,9 +52,18 @@ const GatewayLogsTailParams = z
|
|
|
45
52
|
})
|
|
46
53
|
.strict();
|
|
47
54
|
|
|
55
|
+
const GatewayLogsTailResponseSchema = z.object({
|
|
56
|
+
lines: z.array(z.record(z.string(), z.unknown())),
|
|
57
|
+
truncated: z.boolean(),
|
|
58
|
+
});
|
|
59
|
+
type GatewayLogsTailResponse = z.infer<typeof GatewayLogsTailResponseSchema>;
|
|
60
|
+
|
|
48
61
|
// ── Handlers ────────────────────────────────────────────────────────────
|
|
49
62
|
|
|
50
|
-
async function handleGatewayLogsTail({
|
|
63
|
+
async function handleGatewayLogsTail({
|
|
64
|
+
queryParams = {},
|
|
65
|
+
body = {},
|
|
66
|
+
}: RouteHandlerArgs): Promise<GatewayLogsTailResponse> {
|
|
51
67
|
// HTTP GET delivers filters via queryParams; CLI IPC puts them in body.
|
|
52
68
|
const source = Object.keys(queryParams).length > 0 ? queryParams : body;
|
|
53
69
|
const p = GatewayLogsTailParams.parse(source);
|
|
@@ -56,7 +72,9 @@ async function handleGatewayLogsTail({ queryParams = {}, body = {} }: RouteHandl
|
|
|
56
72
|
if (p.level !== undefined) qs.set("level", p.level);
|
|
57
73
|
if (p.module !== undefined) qs.set("module", p.module);
|
|
58
74
|
const query = qs.toString();
|
|
59
|
-
return gatewayFetch(
|
|
75
|
+
return gatewayFetch(
|
|
76
|
+
`/v1/logs/tail${query ? `?${query}` : ""}`,
|
|
77
|
+
) as Promise<GatewayLogsTailResponse>;
|
|
60
78
|
}
|
|
61
79
|
|
|
62
80
|
// ── Route definitions ───────────────────────────────────────────────────
|
|
@@ -75,8 +93,12 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
75
93
|
description:
|
|
76
94
|
"Return the last N structured log entries from the gateway log files.",
|
|
77
95
|
tags: ["gateway-logs"],
|
|
96
|
+
responseBody: GatewayLogsTailResponseSchema,
|
|
78
97
|
queryParams: [
|
|
79
|
-
{
|
|
98
|
+
{
|
|
99
|
+
name: "n",
|
|
100
|
+
description: "Number of lines to return (1–1000, default: 10)",
|
|
101
|
+
},
|
|
80
102
|
{ name: "level", description: "Minimum pino level name (default: info)" },
|
|
81
103
|
{ name: "module", description: "Filter to exact pino module name" },
|
|
82
104
|
],
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
} from "../../config/loader.js";
|
|
19
19
|
import { listHeartbeatRuns } from "../../heartbeat/heartbeat-run-store.js";
|
|
20
20
|
import { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
|
|
21
|
+
import { getConversation } from "../../memory/conversation-crud.js";
|
|
22
|
+
import { getUsageCostForConversationWindow } from "../../memory/llm-usage-store.js";
|
|
21
23
|
import { readTextFileSync } from "../../util/fs.js";
|
|
22
24
|
import { getLogger } from "../../util/logger.js";
|
|
23
25
|
import { getWorkspacePromptPath } from "../../util/platform.js";
|
|
@@ -38,19 +40,34 @@ function handleListRuns(queryParams: Record<string, string>) {
|
|
|
38
40
|
: 20;
|
|
39
41
|
|
|
40
42
|
const runs = listHeartbeatRuns(limit);
|
|
43
|
+
const now = Date.now();
|
|
41
44
|
return {
|
|
42
|
-
runs: runs.map((r) =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
runs: runs.map((r) => {
|
|
46
|
+
const conversation = r.conversationId
|
|
47
|
+
? getConversation(r.conversationId)
|
|
48
|
+
: null;
|
|
49
|
+
return {
|
|
50
|
+
id: r.id,
|
|
51
|
+
scheduledFor: r.scheduledFor,
|
|
52
|
+
startedAt: r.startedAt,
|
|
53
|
+
finishedAt: r.finishedAt,
|
|
54
|
+
durationMs: r.durationMs,
|
|
55
|
+
status: r.status,
|
|
56
|
+
skipReason: r.skipReason,
|
|
57
|
+
error: r.error,
|
|
58
|
+
conversationId: r.conversationId,
|
|
59
|
+
conversationExists: conversation != null,
|
|
60
|
+
conversationArchivedAt: conversation?.archivedAt ?? null,
|
|
61
|
+
estimatedCostUsd: r.conversationId
|
|
62
|
+
? getUsageCostForConversationWindow({
|
|
63
|
+
conversationId: r.conversationId,
|
|
64
|
+
from: r.startedAt ?? r.scheduledFor,
|
|
65
|
+
to: r.finishedAt ?? now,
|
|
66
|
+
})
|
|
67
|
+
: 0,
|
|
68
|
+
createdAt: r.createdAt,
|
|
69
|
+
};
|
|
70
|
+
}),
|
|
54
71
|
};
|
|
55
72
|
}
|
|
56
73
|
|
|
@@ -116,6 +133,9 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
116
133
|
skipReason: z.string().nullable(),
|
|
117
134
|
error: z.string().nullable(),
|
|
118
135
|
conversationId: z.string().nullable(),
|
|
136
|
+
conversationExists: z.boolean(),
|
|
137
|
+
conversationArchivedAt: z.number().nullable(),
|
|
138
|
+
estimatedCostUsd: z.number(),
|
|
119
139
|
createdAt: z.number(),
|
|
120
140
|
}),
|
|
121
141
|
)
|
|
@@ -3,38 +3,33 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Greetings are sourced from (in priority order):
|
|
5
5
|
* 1. A `## Greetings` section in SOUL.md (user-defined bullet list)
|
|
6
|
-
* 2. A cached greetings array (populated by the
|
|
7
|
-
* 3.
|
|
6
|
+
* 2. A cached greetings array (populated by the empty-state greeting callsite)
|
|
7
|
+
* 3. A generic fallback when generation is unavailable
|
|
8
8
|
*
|
|
9
|
-
* Cache
|
|
10
|
-
*
|
|
9
|
+
* Cache invalidation is intentionally TTL-only. User-authored SOUL.md
|
|
10
|
+
* greetings are read before cache, so manual overrides still take priority
|
|
11
|
+
* without coupling cache validity to prompt-file edits.
|
|
11
12
|
*
|
|
12
13
|
* Storage uses the existing `memory_checkpoints` table (simple key-value store).
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
|
-
import { createHash } from "node:crypto";
|
|
16
16
|
import { existsSync, readFileSync } from "node:fs";
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
19
|
getMemoryCheckpoint,
|
|
20
20
|
setMemoryCheckpoint,
|
|
21
21
|
} from "../../memory/checkpoints.js";
|
|
22
|
-
import { resolveGuardianPersona } from "../../prompts/persona-resolver.js";
|
|
23
22
|
import { getWorkspacePromptPath } from "../../util/platform.js";
|
|
24
23
|
|
|
25
24
|
// ---------------------------------------------------------------------------
|
|
26
25
|
// Constants
|
|
27
26
|
// ---------------------------------------------------------------------------
|
|
28
27
|
|
|
29
|
-
const CACHE_TTL_MS =
|
|
28
|
+
const CACHE_TTL_MS = 2 * 24 * 60 * 60 * 1000; // 2 days
|
|
30
29
|
|
|
31
30
|
const CHECKPOINT_KEY_GREETINGS = "identity:intro:greetings";
|
|
32
|
-
const CHECKPOINT_KEY_HASH = "identity:intro:content_hash";
|
|
33
31
|
const CHECKPOINT_KEY_TIMESTAMP = "identity:intro:cached_at";
|
|
34
32
|
|
|
35
|
-
/** Workspace files whose content influences the identity intro. */
|
|
36
|
-
const IDENTITY_FILES = ["IDENTITY.md", "SOUL.md"] as const;
|
|
37
|
-
|
|
38
33
|
// ---------------------------------------------------------------------------
|
|
39
34
|
// Helpers
|
|
40
35
|
// ---------------------------------------------------------------------------
|
|
@@ -125,14 +120,6 @@ export function readWorkspaceGreetings(): string[] | null {
|
|
|
125
120
|
return parseGreetingsSection(soulContent);
|
|
126
121
|
}
|
|
127
122
|
|
|
128
|
-
/** Compute a SHA-256 hex hash of the concatenated identity file contents. */
|
|
129
|
-
export function computeIdentityContentHash(): string {
|
|
130
|
-
const staticFiles = IDENTITY_FILES.map(readWorkspaceFile).join("\n---\n");
|
|
131
|
-
const guardianPersona = resolveGuardianPersona() ?? "";
|
|
132
|
-
const combined = staticFiles + "\n---\n" + guardianPersona;
|
|
133
|
-
return createHash("sha256").update(combined).digest("hex");
|
|
134
|
-
}
|
|
135
|
-
|
|
136
123
|
// ---------------------------------------------------------------------------
|
|
137
124
|
// Public API
|
|
138
125
|
// ---------------------------------------------------------------------------
|
|
@@ -142,27 +129,22 @@ export interface CachedIntro {
|
|
|
142
129
|
}
|
|
143
130
|
|
|
144
131
|
/**
|
|
145
|
-
* Retrieve the cached greetings array if it exists
|
|
146
|
-
*
|
|
132
|
+
* Retrieve the cached greetings array if it exists and is within the TTL
|
|
133
|
+
* window.
|
|
147
134
|
*
|
|
148
|
-
* Returns `null` when the cache is missing
|
|
135
|
+
* Returns `null` when the cache is missing or expired.
|
|
149
136
|
*/
|
|
150
137
|
export function getCachedIntro(): CachedIntro | null {
|
|
151
138
|
try {
|
|
152
139
|
const raw = getMemoryCheckpoint(CHECKPOINT_KEY_GREETINGS);
|
|
153
|
-
const hash = getMemoryCheckpoint(CHECKPOINT_KEY_HASH);
|
|
154
140
|
const timestampStr = getMemoryCheckpoint(CHECKPOINT_KEY_TIMESTAMP);
|
|
155
141
|
|
|
156
|
-
if (!raw || !
|
|
142
|
+
if (!raw || !timestampStr) return null;
|
|
157
143
|
|
|
158
144
|
// TTL check
|
|
159
145
|
const cachedAt = Number(timestampStr);
|
|
160
146
|
if (isNaN(cachedAt) || Date.now() - cachedAt > CACHE_TTL_MS) return null;
|
|
161
147
|
|
|
162
|
-
// Content-hash check — bust cache when identity files change
|
|
163
|
-
const currentHash = computeIdentityContentHash();
|
|
164
|
-
if (currentHash !== hash) return null;
|
|
165
|
-
|
|
166
148
|
// Parse stored value — handles both JSON array and legacy single string
|
|
167
149
|
let greetings: string[];
|
|
168
150
|
try {
|
|
@@ -178,16 +160,11 @@ export function getCachedIntro(): CachedIntro | null {
|
|
|
178
160
|
}
|
|
179
161
|
}
|
|
180
162
|
|
|
181
|
-
/**
|
|
182
|
-
* Store a greetings array in the cache along with the current content hash
|
|
183
|
-
* and timestamp.
|
|
184
|
-
*/
|
|
163
|
+
/** Store a greetings array in the cache along with the current timestamp. */
|
|
185
164
|
export function setCachedIntro(greetings: string[]): void {
|
|
186
165
|
try {
|
|
187
|
-
const hash = computeIdentityContentHash();
|
|
188
166
|
const now = String(Date.now());
|
|
189
167
|
setMemoryCheckpoint(CHECKPOINT_KEY_GREETINGS, JSON.stringify(greetings));
|
|
190
|
-
setMemoryCheckpoint(CHECKPOINT_KEY_HASH, hash);
|
|
191
168
|
setMemoryCheckpoint(CHECKPOINT_KEY_TIMESTAMP, now);
|
|
192
169
|
} catch {
|
|
193
170
|
// Cache write failure is non-fatal — next request will regenerate.
|