@vellumai/assistant 0.6.4 → 0.6.6
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/.prettierignore +5 -0
- package/AGENTS.md +9 -1
- package/ARCHITECTURE.md +43 -49
- package/Dockerfile +17 -3
- package/README.md +3 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +33 -59
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +19 -18
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/error-handling.md +111 -0
- package/docs/plugins.md +761 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- package/knip.json +9 -2
- package/node_modules/@vellumai/ces-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
- package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
- package/node_modules/@vellumai/credential-storage/package.json +2 -2
- package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
- package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
- package/node_modules/@vellumai/egress-proxy/package.json +2 -2
- package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +334 -78
- package/package.json +6 -3
- package/scripts/generate-openapi.ts +50 -11
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
- package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
- package/src/__tests__/agent-loop.test.ts +112 -1
- package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
- package/src/__tests__/anthropic-provider.test.ts +171 -2
- package/src/__tests__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +36 -10
- package/src/__tests__/approval-routes-http.test.ts +134 -10
- package/src/__tests__/assistant-attachments.test.ts +44 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
- package/src/__tests__/browser-skill-endstate.test.ts +51 -182
- package/src/__tests__/btw-routes.test.ts +47 -1
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/call-controller.test.ts +1 -2
- package/src/__tests__/call-site-routing-provider.test.ts +214 -0
- package/src/__tests__/catalog-cache.test.ts +96 -4
- package/src/__tests__/channel-approval-routes.test.ts +4 -4
- package/src/__tests__/channel-reply-delivery.test.ts +300 -2
- package/src/__tests__/checker.test.ts +870 -655
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/config-analysis.test.ts +11 -28
- package/src/__tests__/config-loader-backfill.test.ts +174 -0
- package/src/__tests__/config-loader-corrupt.test.ts +183 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
- package/src/__tests__/config-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +440 -114
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +883 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +32 -15
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +86 -46
- package/src/__tests__/conversation-agent-loop.test.ts +435 -216
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +36 -10
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +34 -12
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +32 -15
- package/src/__tests__/conversation-process-callsite.test.ts +309 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +44 -21
- package/src/__tests__/conversation-queue.test.ts +68 -38
- package/src/__tests__/conversation-routes-disk-view.test.ts +36 -7
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2877 -152
- package/src/__tests__/conversation-runtime-workspace.test.ts +35 -50
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +39 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +53 -16
- package/src/__tests__/conversation-speed-override.test.ts +36 -12
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
- package/src/__tests__/conversation-title-service.test.ts +118 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
- package/src/__tests__/conversation-unread-route.test.ts +2 -2
- package/src/__tests__/conversation-usage.test.ts +4 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +33 -9
- package/src/__tests__/conversation-workspace-injection.test.ts +46 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -15
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +5 -2
- package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
- package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
- package/src/__tests__/credential-vault-unit.test.ts +135 -19
- package/src/__tests__/credentials-cli.test.ts +1 -9
- package/src/__tests__/cross-provider-web-search.test.ts +84 -0
- package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
- package/src/__tests__/delete-propagation.test.ts +437 -0
- package/src/__tests__/dm-backfill.test.ts +417 -0
- package/src/__tests__/dm-persistence.test.ts +227 -0
- package/src/__tests__/edit-propagation.test.ts +280 -0
- package/src/__tests__/empty-response-pipeline.test.ts +305 -0
- package/src/__tests__/ephemeral-permissions.test.ts +93 -3
- package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
- package/src/__tests__/estimator-calibration.test.ts +213 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +29 -10
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/first-greeting.test.ts +247 -5
- package/src/__tests__/gemini-provider.test.ts +0 -3
- package/src/__tests__/guardian-grant-minting.test.ts +8 -0
- package/src/__tests__/headless-browser-interactions.test.ts +1 -1
- package/src/__tests__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +1 -66
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- package/src/__tests__/llm-catalog-parity.test.ts +174 -0
- package/src/__tests__/llm-context-normalization.test.ts +121 -0
- package/src/__tests__/llm-resolver.test.ts +214 -0
- package/src/__tests__/llm-schema.test.ts +223 -0
- package/src/__tests__/managed-proxy-context.test.ts +6 -2
- package/src/__tests__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +621 -0
- package/src/__tests__/model-intents.test.ts +11 -83
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- package/src/__tests__/notification-decision-fallback.test.ts +0 -10
- package/src/__tests__/notification-decision-identity.test.ts +0 -9
- package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
- package/src/__tests__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +46 -78
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -0
- package/src/__tests__/openai-provider.test.ts +7 -0
- package/src/__tests__/openai-responses-provider.test.ts +396 -0
- package/src/__tests__/openrouter-provider-only.test.ts +135 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
- package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -25
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +93 -14
- package/src/__tests__/profiler-routes.test.ts +1 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
- package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
- package/src/__tests__/provider-error-scenarios.test.ts +135 -6
- package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
- package/src/__tests__/provider-registry-ollama.test.ts +1 -2
- package/src/__tests__/proxy-approval-callback.test.ts +69 -9
- package/src/__tests__/reaction-persistence.test.ts +561 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
- package/src/__tests__/risk-classifier-parity.test.ts +230 -0
- package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
- package/src/__tests__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- package/src/__tests__/secret-ingress-http.test.ts +28 -0
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
- package/src/__tests__/secret-scanner-executor.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +29 -1
- package/src/__tests__/server-history-render.test.ts +31 -0
- package/src/__tests__/shell-identity.test.ts +0 -134
- package/src/__tests__/shell-parser-property.test.ts +13 -13
- package/src/__tests__/skill-cache-store.test.ts +182 -0
- package/src/__tests__/skills.test.ts +19 -33
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-skill.test.ts +3 -8
- package/src/__tests__/starter-bundle.test.ts +35 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
- package/src/__tests__/suggestion-routes.test.ts +259 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -8
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +201 -94
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- package/src/__tests__/trust-store.test.ts +442 -109
- package/src/__tests__/update-bulletin-job.test.ts +389 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
- package/src/__tests__/user-plugin-loader.test.ts +191 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
- package/src/__tests__/voice-session-bridge.test.ts +39 -0
- package/src/__tests__/volume-security-guard.test.ts +3 -2
- package/src/__tests__/web-search-history.test.ts +337 -0
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
- package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
- package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
- package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +22 -16
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +545 -115
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- package/src/avatar/resvg-lazy.test.ts +136 -0
- package/src/avatar/resvg-lazy.ts +82 -9
- package/src/avatar/traits-png-sync.ts +21 -1
- package/src/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- package/src/browser/__tests__/operations.test.ts +163 -0
- package/src/browser/identifiers.ts +51 -0
- package/src/browser/operations.ts +660 -0
- package/src/browser/types.ts +81 -0
- package/src/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- package/src/calls/guardian-question-copy.ts +2 -2
- package/src/calls/telephony-stt-routing.ts +1 -1
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- package/src/cli/commands/__tests__/browser.test.ts +554 -0
- package/src/cli/commands/__tests__/cache.test.ts +623 -0
- package/src/cli/commands/__tests__/email-list.test.ts +6 -0
- package/src/cli/commands/__tests__/email-send.test.ts +93 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +886 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +463 -0
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
- package/src/cli/commands/__tests__/task.test.ts +913 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +606 -0
- package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
- package/src/cli/commands/__tests__/ui.test.ts +1215 -0
- package/src/cli/commands/__tests__/watchers.test.ts +716 -0
- package/src/cli/commands/attachment.ts +182 -0
- package/src/cli/commands/backup.ts +2 -2
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -12
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +69 -8
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +299 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
- package/src/cli/commands/skills.ts +3 -4
- package/src/cli/commands/stt.ts +339 -0
- package/src/cli/commands/task.ts +795 -0
- package/src/cli/commands/trust.ts +50 -19
- package/src/cli/commands/tts.ts +273 -0
- package/src/cli/commands/ui.ts +670 -0
- package/src/cli/commands/watchers.ts +509 -0
- package/src/cli/lib/daemon-credential-client.ts +0 -19
- package/src/cli/program.ts +39 -24
- package/src/cli.ts +0 -37
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +20 -1
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +69 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -190
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +42 -10
- package/src/config/llm-resolver.ts +128 -0
- package/src/config/loader.ts +194 -10
- package/src/config/raw-config-utils.ts +30 -2
- package/src/config/sanitize-for-transfer.ts +35 -0
- package/src/config/schema.ts +49 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/conversations.ts +16 -0
- package/src/config/schemas/filing.ts +2 -7
- package/src/config/schemas/heartbeat.ts +0 -5
- package/src/config/schemas/inference.ts +3 -23
- package/src/config/schemas/llm.ts +317 -0
- package/src/config/schemas/memory-processing.ts +1 -9
- package/src/config/schemas/notifications.ts +4 -11
- package/src/config/schemas/platform.ts +3 -9
- package/src/config/schemas/security.ts +33 -0
- package/src/config/schemas/services.ts +9 -4
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/schemas/tts.ts +64 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +96 -7
- package/src/context/__tests__/compact-prompt.test.ts +63 -0
- package/src/context/__tests__/microcompact.test.ts +805 -0
- package/src/context/estimator-calibration.ts +136 -0
- package/src/context/microcompact.ts +443 -0
- package/src/context/prompts/compact.md +26 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +417 -39
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/executable-discovery.ts +19 -8
- package/src/credential-execution/process-manager.test.ts +109 -0
- package/src/credential-execution/process-manager.ts +65 -2
- package/src/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- package/src/daemon/approval-generators.ts +29 -4
- package/src/daemon/assistant-attachments.ts +24 -13
- package/src/daemon/classifier.ts +2 -2
- package/src/daemon/config-watcher.ts +0 -3
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +162 -34
- package/src/daemon/conversation-agent-loop.ts +1282 -599
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +59 -17
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +24 -11
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +1063 -211
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +51 -9
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +197 -64
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/first-greeting.ts +191 -14
- package/src/daemon/guardian-action-generators.ts +34 -14
- package/src/daemon/handlers/config-model.test.ts +86 -0
- package/src/daemon/handlers/config-model.ts +65 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +7 -3
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +109 -82
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +63 -0
- package/src/daemon/message-types/messages.ts +21 -1
- package/src/daemon/message-types/trust.ts +0 -2
- package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
- package/src/daemon/pkb-context-tracker.test.ts +169 -0
- package/src/daemon/pkb-context-tracker.ts +125 -0
- package/src/daemon/pkb-reminder-builder.test.ts +70 -0
- package/src/daemon/pkb-reminder-builder.ts +31 -0
- package/src/daemon/providers-setup.ts +6 -0
- package/src/daemon/server.ts +122 -12
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -65
- package/src/daemon/web-search-history.ts +126 -0
- package/src/events/domain-events.ts +0 -1
- package/src/filing/filing-service.ts +9 -10
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +99 -28
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- package/src/home/__tests__/feed-scheduler.test.ts +39 -11
- package/src/home/__tests__/rollup-producer.test.ts +44 -0
- package/src/home/assistant-feed-authoring.ts +4 -0
- package/src/home/emit-feed-event.ts +11 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +97 -4
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- package/src/home/rollup-producer.ts +34 -5
- package/src/home/suggested-prompts.ts +101 -0
- package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
- package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
- package/src/ipc/__tests__/socket-path.test.ts +34 -0
- package/src/ipc/__tests__/task-ipc.test.ts +577 -0
- package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
- package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
- package/src/ipc/cli-client.ts +2 -1
- package/src/ipc/cli-server.ts +26 -8
- package/src/ipc/gateway-client.ts +6 -3
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +63 -0
- package/src/ipc/routes/browser.ts +97 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +31 -1
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- package/src/ipc/routes/task-queue.ts +226 -0
- package/src/ipc/routes/task.ts +173 -0
- package/src/ipc/routes/ui-request.ts +50 -0
- package/src/ipc/routes/upsert-contact.ts +25 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +76 -0
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
- package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
- package/src/memory/admin.ts +18 -0
- package/src/memory/conversation-analyze-job.ts +14 -13
- package/src/memory/conversation-attention-store.ts +13 -6
- package/src/memory/conversation-crud.ts +133 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +32 -4
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- package/src/memory/graph/compaction.ts +299 -0
- package/src/memory/graph/consolidation.ts +4 -4
- package/src/memory/graph/conversation-graph-memory.ts +89 -29
- package/src/memory/graph/extraction.test.ts +272 -2
- package/src/memory/graph/extraction.ts +183 -53
- package/src/memory/graph/graph-search.test.ts +93 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/inspect.ts +2 -2
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.test.ts +459 -0
- package/src/memory/graph/retriever.ts +237 -48
- package/src/memory/graph/store.ts +41 -0
- package/src/memory/graph/tool-handlers.ts +27 -0
- package/src/memory/graph/tools.ts +6 -1
- package/src/memory/indexer.ts +5 -5
- package/src/memory/job-handlers/conversation-starters.ts +23 -20
- package/src/memory/job-handlers/summarization.ts +2 -2
- package/src/memory/job-utils.ts +7 -1
- package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
- package/src/memory/jobs/embed-pkb-file.ts +54 -0
- package/src/memory/jobs-store.ts +44 -3
- package/src/memory/jobs-worker.ts +4 -0
- package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
- package/src/memory/migrations/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/pkb/pkb-index.test.ts +369 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +252 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +499 -0
- package/src/memory/pkb/pkb-search.ts +159 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +147 -1
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -1
- package/src/memory/slack-thread-store.ts +37 -0
- package/src/messaging/providers/gmail/adapter.ts +6 -16
- package/src/messaging/providers/gmail/client.ts +22 -0
- package/src/messaging/providers/gmail/types.ts +7 -0
- package/src/messaging/providers/slack/adapter.ts +14 -2
- package/src/messaging/providers/slack/backfill.test.ts +257 -0
- package/src/messaging/providers/slack/backfill.ts +101 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
- package/src/messaging/providers/slack/message-metadata.ts +123 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1421 -0
- package/src/messaging/providers/slack/render-transcript.ts +501 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +31 -14
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -106
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +1223 -0
- package/src/permissions/approval-policy.ts +309 -0
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +1620 -0
- package/src/permissions/bash-risk-classifier.ts +950 -0
- package/src/permissions/checker.ts +348 -711
- package/src/permissions/command-registry.test.ts +774 -0
- package/src/permissions/command-registry.ts +1005 -0
- package/src/permissions/defaults.ts +28 -79
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +262 -0
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/shell-identity.ts +2 -42
- package/src/permissions/skill-risk-classifier.test.ts +311 -0
- package/src/permissions/skill-risk-classifier.ts +214 -0
- package/src/permissions/trust-client.ts +52 -25
- package/src/permissions/trust-store-interface.ts +1 -6
- package/src/permissions/trust-store.ts +161 -62
- package/src/permissions/types.ts +25 -14
- package/src/permissions/web-risk-classifier.test.ts +170 -0
- package/src/permissions/web-risk-classifier.ts +89 -0
- package/src/permissions/workspace-policy.ts +9 -19
- package/src/platform/client.ts +19 -1
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- package/src/prompts/templates/SOUL.md +2 -2
- package/src/prompts/update-bulletin-job.ts +190 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
- package/src/providers/__tests__/retry-callsite.test.ts +424 -0
- package/src/providers/anthropic/client.ts +183 -14
- package/src/providers/call-site-routing.ts +71 -0
- package/src/providers/gemini/client.ts +65 -2
- package/src/providers/managed-proxy/constants.ts +2 -1
- package/src/providers/model-catalog.ts +524 -33
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +57 -1
- package/src/providers/openai/responses-provider.ts +86 -9
- package/src/providers/openrouter/client.ts +80 -9
- package/src/providers/provider-env-vars.ts +56 -0
- package/src/providers/provider-send-message.ts +22 -5
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/registry.ts +19 -8
- package/src/providers/retry.ts +174 -39
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
- package/src/providers/speech-to-text/provider-catalog.ts +17 -0
- package/src/providers/speech-to-text/resolve.ts +7 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +646 -0
- package/src/providers/speech-to-text/xai-realtime.ts +821 -0
- package/src/providers/speech-to-text/xai.test.ts +155 -0
- package/src/providers/speech-to-text/xai.ts +97 -0
- package/src/providers/types.ts +93 -3
- package/src/runtime/AGENTS.md +27 -18
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
- package/src/runtime/agent-wake.ts +63 -22
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/btw-sidechain.ts +13 -3
- package/src/runtime/channel-reply-delivery.ts +106 -2
- package/src/runtime/client-registry.ts +261 -0
- package/src/runtime/decision-token.ts +116 -0
- package/src/runtime/gateway-client.ts +2 -2
- package/src/runtime/http-router.ts +32 -0
- package/src/runtime/http-server.ts +129 -9
- package/src/runtime/http-types.ts +23 -3
- package/src/runtime/interactive-ui.ts +362 -0
- package/src/runtime/invite-instruction-generator.ts +2 -2
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
- package/src/runtime/migrations/gcs-signed-url.ts +162 -0
- package/src/runtime/migrations/vbundle-builder.ts +1 -22
- package/src/runtime/migrations/vbundle-importer.ts +154 -9
- package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
- package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
- package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
- package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
- package/src/runtime/migrations/vbundle-validator.ts +15 -6
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +78 -0
- package/src/runtime/routes/approval-routes.ts +29 -17
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
- package/src/runtime/routes/avatar-routes.ts +20 -4
- package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
- package/src/runtime/routes/btw-routes.ts +1 -4
- package/src/runtime/routes/conversation-management-routes.ts +20 -2
- package/src/runtime/routes/conversation-routes.ts +351 -138
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +6 -4
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/guardian-approval-interception.ts +33 -3
- package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
- package/src/runtime/routes/home-feed-routes.ts +120 -2
- package/src/runtime/routes/inbound-message-handler.ts +987 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
- package/src/runtime/routes/integrations/slack/channel.ts +25 -3
- package/src/runtime/routes/llm-context-normalization.ts +23 -1
- package/src/runtime/routes/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +720 -127
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- package/src/runtime/routes/settings-routes.ts +4 -2
- package/src/runtime/routes/trust-rules-routes.ts +30 -14
- package/src/runtime/routes/work-items-routes.test.ts +1 -1
- package/src/runtime/routes/work-items-routes.ts +3 -2
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
- package/src/runtime/services/analyze-conversation.ts +12 -16
- package/src/runtime/skill-route-registry.ts +97 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +56 -8
- package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
- package/src/security/__tests__/untrusted-content.test.ts +109 -0
- package/src/security/oauth2.ts +98 -35
- package/src/security/secure-keys.ts +7 -8
- package/src/security/token-manager.ts +27 -13
- package/src/security/untrusted-content.ts +102 -0
- package/src/skills/catalog-cache.ts +35 -9
- package/src/skills/catalog-install.ts +31 -3
- package/src/skills/skill-cache-store.ts +97 -0
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
- package/src/stt/daemon-batch-transcriber.ts +33 -0
- package/src/stt/stt-stream-session.ts +8 -1
- package/src/stt/types.ts +5 -1
- package/src/subagent/manager.ts +41 -13
- package/src/tasks/ephemeral-permissions.ts +9 -4
- package/src/telemetry/usage-telemetry-reporter.ts +27 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +234 -2
- package/src/tools/browser/browser-execution.ts +150 -54
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +129 -73
- package/src/tools/filesystem/write.ts +52 -0
- package/src/tools/host-terminal/host-shell.ts +45 -5
- package/src/tools/memory/register.test.ts +185 -0
- package/src/tools/memory/register.ts +3 -1
- package/src/tools/network/script-proxy/session-manager.ts +37 -1
- package/src/tools/network/web-fetch.ts +20 -10
- package/src/tools/network/web-search.ts +19 -4
- package/src/tools/permission-checker.ts +116 -46
- package/src/tools/policy-context.ts +29 -8
- package/src/tools/registry.ts +195 -6
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- package/src/tools/side-effects.ts +0 -11
- package/src/tools/skills/execute.ts +2 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/system/avatar-generator.ts +6 -2
- package/src/tools/terminal/backends/native.ts +51 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +1 -0
- package/src/tools/tool-manifest.ts +6 -21
- package/src/tools/types.ts +40 -5
- package/src/tools/verification-control-plane-policy.ts +1 -1
- package/src/tts/__tests__/provider-adapters.test.ts +240 -13
- package/src/tts/provider-catalog.ts +18 -0
- package/src/tts/providers/index.ts +2 -0
- package/src/tts/providers/xai-provider.ts +224 -0
- package/src/tts/types.ts +46 -0
- package/src/types/tar-stream.d.ts +66 -0
- package/src/util/json.ts +17 -0
- package/src/util/platform.ts +9 -4
- package/src/util/pricing.ts +41 -8
- package/src/watcher/engine.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +134 -8
- package/src/watcher/providers/outlook-calendar.ts +42 -2
- package/src/workspace/git-service.ts +23 -4
- package/src/workspace/migrations/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
- package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +56 -0
- package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
- package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
- package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
- package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +28 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
- package/src/__tests__/gmail-archive-gate.test.ts +0 -246
- package/src/__tests__/gmail-preferences.test.ts +0 -117
- package/src/__tests__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- package/src/__tests__/outlook-attachments.test.ts +0 -301
- package/src/__tests__/outlook-automation-tools.test.ts +0 -425
- package/src/__tests__/outlook-categories.test.ts +0 -212
- package/src/__tests__/outlook-compose-tools.test.ts +0 -325
- package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
- package/src/__tests__/outlook-follow-up.test.ts +0 -196
- package/src/__tests__/outlook-trash.test.ts +0 -77
- package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
- package/src/__tests__/send-notification-tool.test.ts +0 -83
- package/src/__tests__/update-bulletin-format.test.ts +0 -181
- package/src/__tests__/update-bulletin-state.test.ts +0 -135
- package/src/__tests__/update-bulletin.test.ts +0 -478
- package/src/__tests__/update-template-contract.test.ts +0 -29
- package/src/cli/commands/doctor.ts +0 -341
- package/src/cli/commands/shotgun.ts +0 -266
- package/src/config/bundled-skills/browser/SKILL.md +0 -88
- package/src/config/bundled-skills/browser/TOOLS.json +0 -516
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
- package/src/config/bundled-skills/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -66
- package/src/config/bundled-skills/gmail/SKILL.md +0 -221
- package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
- package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
- package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
- package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/google-calendar/types.ts +0 -97
- package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- package/src/config/bundled-skills/outlook/SKILL.md +0 -196
- package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
- package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
- package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- package/src/config/bundled-skills/slack/SKILL.md +0 -108
- package/src/config/bundled-skills/tasks/SKILL.md +0 -37
- package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
- package/src/config/bundled-skills/tasks/icon.svg +0 -34
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
- package/src/config/bundled-skills/watcher/SKILL.md +0 -31
- package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
- package/src/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- package/src/prompts/templates/UPDATES.md +0 -50
- package/src/prompts/update-bulletin-format.ts +0 -85
- package/src/prompts/update-bulletin-state.ts +0 -58
- package/src/prompts/update-bulletin-template-path.ts +0 -13
- package/src/prompts/update-bulletin.ts +0 -139
- package/src/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
- package/src/tools/watcher/create.ts +0 -86
- package/src/tools/watcher/delete.ts +0 -36
- package/src/tools/watcher/digest.ts +0 -54
- package/src/tools/watcher/list.ts +0 -83
- package/src/tools/watcher/update.ts +0 -71
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming validation primitives for `.vbundle` archives.
|
|
3
|
+
*
|
|
4
|
+
* The non-streaming `validateVBundle` decompresses the entire archive into
|
|
5
|
+
* memory and walks the tar buffer to compute per-file SHA-256s. That is fine
|
|
6
|
+
* for small bundles but peaks at 2x the decompressed size in RAM — an 8 GB
|
|
7
|
+
* bundle OOMs a 3 GB pod.
|
|
8
|
+
*
|
|
9
|
+
* This module lets a caller validate a bundle while streaming:
|
|
10
|
+
* - `readAndValidateManifest` consumes the first tar entry (which must be
|
|
11
|
+
* `manifest.json`), validates the schema, and verifies the self-referencing
|
|
12
|
+
* `manifest_sha256` against the canonicalized JSON.
|
|
13
|
+
* - `createHashVerifier` returns a passthrough `Transform` that hashes bytes
|
|
14
|
+
* flowing through it and errors the pipeline if the final digest or byte
|
|
15
|
+
* count does not match the expected values from the manifest.
|
|
16
|
+
*
|
|
17
|
+
* Together, these let a consumer pipe every subsequent tar entry through a
|
|
18
|
+
* hash verifier before writing it to disk, without ever buffering the full
|
|
19
|
+
* bundle.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { createHash } from "node:crypto";
|
|
23
|
+
import { Transform, type TransformCallback } from "node:stream";
|
|
24
|
+
|
|
25
|
+
import type { StreamedTarEntry } from "./vbundle-tar-stream.js";
|
|
26
|
+
import {
|
|
27
|
+
computeManifestSha256,
|
|
28
|
+
ManifestSchema,
|
|
29
|
+
type ManifestType,
|
|
30
|
+
} from "./vbundle-validator.js";
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Public types
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
export interface ManifestReadResult {
|
|
37
|
+
manifest: ManifestType;
|
|
38
|
+
/** Fast lookup from archive path -> expected sha256 + size (from manifest.files). */
|
|
39
|
+
expected: Map<string, { sha256: string; size: number }>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* All failure modes produced by this module. Every throw/error includes a
|
|
44
|
+
* stable `code` string so callers can branch on the failure kind without
|
|
45
|
+
* string-matching the message.
|
|
46
|
+
*/
|
|
47
|
+
export class StreamingValidationError extends Error {
|
|
48
|
+
public readonly code: string;
|
|
49
|
+
public readonly archivePath?: string;
|
|
50
|
+
|
|
51
|
+
constructor(code: string, message: string, archivePath?: string) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = "StreamingValidationError";
|
|
54
|
+
this.code = code;
|
|
55
|
+
if (archivePath !== undefined) {
|
|
56
|
+
this.archivePath = archivePath;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Manifest validation
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
// Manifests are metadata only — typically tens to hundreds of KB even for
|
|
66
|
+
// huge bundles. A 1 MiB cap is comfortably above realistic sizes and
|
|
67
|
+
// protects against a malicious archive whose "manifest" is actually a
|
|
68
|
+
// multi-GB stream intended to OOM the validator.
|
|
69
|
+
const MANIFEST_MAX_BYTES = 1 * 1024 * 1024;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Drain the first tar entry — which MUST be `manifest.json` — and run the
|
|
73
|
+
* full manifest-level validation pipeline:
|
|
74
|
+
* 1. Entry name check.
|
|
75
|
+
* 2. Size cap (1 MiB).
|
|
76
|
+
* 3. JSON parse.
|
|
77
|
+
* 4. Zod schema validation.
|
|
78
|
+
* 5. Self-referencing `manifest_sha256` verification against the
|
|
79
|
+
* canonicalized JSON (minus that field).
|
|
80
|
+
*
|
|
81
|
+
* On success, returns the parsed manifest plus a `Map` keyed by archive
|
|
82
|
+
* path that callers consult as each subsequent entry streams past.
|
|
83
|
+
*
|
|
84
|
+
* On failure, throws a `StreamingValidationError` with a distinct `code`
|
|
85
|
+
* for every failure mode.
|
|
86
|
+
*/
|
|
87
|
+
export async function readAndValidateManifest(
|
|
88
|
+
first: StreamedTarEntry,
|
|
89
|
+
): Promise<ManifestReadResult> {
|
|
90
|
+
if (first.header.name !== "manifest.json") {
|
|
91
|
+
// Drain the body so the underlying tar extractor isn't left dangling
|
|
92
|
+
// on backpressure before the caller reports the error.
|
|
93
|
+
first.body.resume();
|
|
94
|
+
throw new StreamingValidationError(
|
|
95
|
+
"manifest_not_first",
|
|
96
|
+
`Expected manifest.json as the first tar entry, got "${first.header.name}"`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Drain the entry body into a Buffer, enforcing the size cap as we go.
|
|
101
|
+
// The moment we cross the cap we destroy the entry stream — this signals
|
|
102
|
+
// the tar extractor (and therefore gunzip + upstream source) to abort,
|
|
103
|
+
// so a malicious archive whose "manifest" is a multi-GB decompressed
|
|
104
|
+
// stream can't force us to read through all of it before rejecting.
|
|
105
|
+
const chunks: Buffer[] = [];
|
|
106
|
+
let total = 0;
|
|
107
|
+
for await (const chunk of first.body) {
|
|
108
|
+
const buf = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
|
|
109
|
+
total += buf.length;
|
|
110
|
+
if (total > MANIFEST_MAX_BYTES) {
|
|
111
|
+
first.body.destroy();
|
|
112
|
+
throw new StreamingValidationError(
|
|
113
|
+
"manifest_too_large",
|
|
114
|
+
`manifest.json exceeds ${MANIFEST_MAX_BYTES} byte limit (read ${total} bytes before aborting)`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
chunks.push(buf);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const bodyBuf = Buffer.concat(chunks, total);
|
|
121
|
+
|
|
122
|
+
let manifestRaw: unknown;
|
|
123
|
+
try {
|
|
124
|
+
manifestRaw = JSON.parse(bodyBuf.toString("utf8"));
|
|
125
|
+
} catch (err) {
|
|
126
|
+
throw new StreamingValidationError(
|
|
127
|
+
"manifest_malformed",
|
|
128
|
+
`manifest.json is not valid JSON: ${
|
|
129
|
+
err instanceof Error ? err.message : String(err)
|
|
130
|
+
}`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const parseResult = ManifestSchema.safeParse(manifestRaw);
|
|
135
|
+
if (!parseResult.success) {
|
|
136
|
+
const issues = parseResult.error.issues
|
|
137
|
+
.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
138
|
+
.join("; ");
|
|
139
|
+
throw new StreamingValidationError(
|
|
140
|
+
"manifest_schema",
|
|
141
|
+
`manifest.json failed schema validation: ${issues}`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const manifest = parseResult.data;
|
|
146
|
+
|
|
147
|
+
// Recompute the self-referencing checksum using the exact canonicalization
|
|
148
|
+
// that vbundle-validator.ts uses. Any drift here would silently reject
|
|
149
|
+
// valid bundles produced by buildVBundle.
|
|
150
|
+
const computed = computeManifestSha256(manifestRaw);
|
|
151
|
+
if (computed !== manifest.manifest_sha256) {
|
|
152
|
+
throw new StreamingValidationError(
|
|
153
|
+
"manifest_sha256",
|
|
154
|
+
`Manifest checksum mismatch: expected ${manifest.manifest_sha256}, computed ${computed}`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const expected = new Map<string, { sha256: string; size: number }>();
|
|
159
|
+
for (const file of manifest.files) {
|
|
160
|
+
if (expected.has(file.path)) {
|
|
161
|
+
throw new StreamingValidationError(
|
|
162
|
+
"manifest_duplicate_path",
|
|
163
|
+
`Manifest contains duplicate entry for path: ${file.path}`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
expected.set(file.path, { sha256: file.sha256, size: file.size });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return { manifest, expected };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Per-entry hash + size verifier
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Create a passthrough `Transform` that:
|
|
178
|
+
* - forwards every chunk unchanged (identity transform for correct input),
|
|
179
|
+
* - incrementally SHA-256s the byte stream,
|
|
180
|
+
* - on `_flush`, errors the pipeline if the final digest or total byte
|
|
181
|
+
* count differs from `expected`.
|
|
182
|
+
*
|
|
183
|
+
* Errors are emitted as `StreamingValidationError` with `code` set to
|
|
184
|
+
* `"entry_hash"` or `"entry_size"` and `archivePath` populated so callers
|
|
185
|
+
* can surface which file failed.
|
|
186
|
+
*
|
|
187
|
+
* Consumers should pipe the entry body through this transform before
|
|
188
|
+
* writing to disk — that way a bad payload is caught before the byte
|
|
189
|
+
* reaches storage rather than after a whole 8 GB write completes.
|
|
190
|
+
*/
|
|
191
|
+
export function createHashVerifier(expected: {
|
|
192
|
+
sha256: string;
|
|
193
|
+
size: number;
|
|
194
|
+
archivePath: string;
|
|
195
|
+
}): Transform {
|
|
196
|
+
const hash = createHash("sha256");
|
|
197
|
+
let bytes = 0;
|
|
198
|
+
|
|
199
|
+
return new Transform({
|
|
200
|
+
transform(
|
|
201
|
+
chunk: Buffer | string,
|
|
202
|
+
encoding: BufferEncoding,
|
|
203
|
+
callback: TransformCallback,
|
|
204
|
+
) {
|
|
205
|
+
try {
|
|
206
|
+
const buf =
|
|
207
|
+
typeof chunk === "string" ? Buffer.from(chunk, encoding) : chunk;
|
|
208
|
+
hash.update(buf);
|
|
209
|
+
bytes += buf.length;
|
|
210
|
+
callback(null, buf);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
callback(err instanceof Error ? err : new Error(String(err)));
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
flush(callback: TransformCallback) {
|
|
216
|
+
// Size check first — a wrong size is a sharper signal than a hash
|
|
217
|
+
// collision, and a truncated payload frequently triggers both.
|
|
218
|
+
if (bytes !== expected.size) {
|
|
219
|
+
callback(
|
|
220
|
+
new StreamingValidationError(
|
|
221
|
+
"entry_size",
|
|
222
|
+
`Size mismatch for ${expected.archivePath}: expected ${expected.size} bytes, got ${bytes}`,
|
|
223
|
+
expected.archivePath,
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const digest = hash.digest("hex");
|
|
230
|
+
if (digest !== expected.sha256) {
|
|
231
|
+
callback(
|
|
232
|
+
new StreamingValidationError(
|
|
233
|
+
"entry_hash",
|
|
234
|
+
`Checksum mismatch for ${expected.archivePath}: expected ${expected.sha256}, computed ${digest}`,
|
|
235
|
+
expected.archivePath,
|
|
236
|
+
),
|
|
237
|
+
);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
callback();
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming tar reader for `.vbundle` archives.
|
|
3
|
+
*
|
|
4
|
+
* A `.vbundle` is a gzip-compressed tar. This module gunzips the incoming
|
|
5
|
+
* `Readable`, pipes it through `tar-stream`'s push-style extractor, and
|
|
6
|
+
* adapts the `(header, stream, next)` event into a consumer-friendly async
|
|
7
|
+
* generator that yields one entry at a time.
|
|
8
|
+
*
|
|
9
|
+
* Memory invariant: each entry's body is surfaced as a `Readable`, never a
|
|
10
|
+
* pre-buffered blob. Callers MUST fully consume (or explicitly `resume()`)
|
|
11
|
+
* each body stream before advancing the outer generator — otherwise the
|
|
12
|
+
* underlying tar extractor will stall waiting on backpressure.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Readable } from "node:stream";
|
|
16
|
+
import { createGunzip } from "node:zlib";
|
|
17
|
+
|
|
18
|
+
import { extract as tarExtract } from "tar-stream";
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Public types
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
/** Subset of tar-stream's header surface that vbundle consumers care about. */
|
|
25
|
+
export interface StreamedTarHeader {
|
|
26
|
+
name: string;
|
|
27
|
+
size: number;
|
|
28
|
+
type: "file" | "directory" | "pax-header" | "other";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface StreamedTarEntry {
|
|
32
|
+
header: StreamedTarHeader;
|
|
33
|
+
/**
|
|
34
|
+
* The entry body. Must be fully consumed (drained via for-await, piped,
|
|
35
|
+
* or explicitly `.resume()`'d) before the generator is advanced again.
|
|
36
|
+
*/
|
|
37
|
+
body: Readable;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Implementation
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
interface PendingEntry {
|
|
45
|
+
header: StreamedTarHeader;
|
|
46
|
+
body: Readable;
|
|
47
|
+
next: (err?: Error | null) => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeHeaderType(
|
|
51
|
+
type: string | undefined,
|
|
52
|
+
): StreamedTarHeader["type"] {
|
|
53
|
+
switch (type) {
|
|
54
|
+
case "file":
|
|
55
|
+
return "file";
|
|
56
|
+
case "directory":
|
|
57
|
+
return "directory";
|
|
58
|
+
case "pax-header":
|
|
59
|
+
return "pax-header";
|
|
60
|
+
default:
|
|
61
|
+
return "other";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Stream a `.vbundle` archive as an async sequence of tar entries.
|
|
67
|
+
*
|
|
68
|
+
* Errors from the upstream source, gunzip, or tar-stream extractor are
|
|
69
|
+
* surfaced by throwing from the generator.
|
|
70
|
+
*
|
|
71
|
+
* Early termination (caller `break`s or the for-await loop throws) destroys
|
|
72
|
+
* the upstream source and the tar extractor so that sockets and file
|
|
73
|
+
* descriptors are released promptly.
|
|
74
|
+
*/
|
|
75
|
+
export async function* parseVBundleStream(
|
|
76
|
+
source: Readable,
|
|
77
|
+
): AsyncGenerator<StreamedTarEntry, void, void> {
|
|
78
|
+
const gunzip = createGunzip();
|
|
79
|
+
const extractor = tarExtract();
|
|
80
|
+
|
|
81
|
+
// Terminal error captured from any stage in the pipeline. Consumed by the
|
|
82
|
+
// generator's internal pump; re-thrown on the next `yield`.
|
|
83
|
+
let pipelineError: Error | null = null;
|
|
84
|
+
let finished = false;
|
|
85
|
+
|
|
86
|
+
// Single-slot mailbox: the extractor pushes one entry here, and the
|
|
87
|
+
// generator consumes it. We gate the push-style API by holding the tar
|
|
88
|
+
// extractor's `next` callback until the caller is done with the body.
|
|
89
|
+
let waiter: {
|
|
90
|
+
resolve: (entry: PendingEntry | null) => void;
|
|
91
|
+
reject: (err: Error) => void;
|
|
92
|
+
} | null = null;
|
|
93
|
+
let pending: PendingEntry | null = null;
|
|
94
|
+
|
|
95
|
+
function pushEntry(entry: PendingEntry): void {
|
|
96
|
+
if (waiter) {
|
|
97
|
+
const w = waiter;
|
|
98
|
+
waiter = null;
|
|
99
|
+
w.resolve(entry);
|
|
100
|
+
} else {
|
|
101
|
+
pending = entry;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function pushError(err: Error): void {
|
|
106
|
+
if (pipelineError) return;
|
|
107
|
+
pipelineError = err;
|
|
108
|
+
if (waiter) {
|
|
109
|
+
const w = waiter;
|
|
110
|
+
waiter = null;
|
|
111
|
+
w.reject(err);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function pushFinish(): void {
|
|
116
|
+
finished = true;
|
|
117
|
+
if (waiter) {
|
|
118
|
+
const w = waiter;
|
|
119
|
+
waiter = null;
|
|
120
|
+
w.resolve(null);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
extractor.on("entry", (header, body, next) => {
|
|
125
|
+
// Avoid unhandled "error" on body streams destroyed mid-flight; the
|
|
126
|
+
// extractor itself propagates the real error to its "error" listener.
|
|
127
|
+
body.on("error", () => {});
|
|
128
|
+
pushEntry({
|
|
129
|
+
header: {
|
|
130
|
+
name: header.name,
|
|
131
|
+
size: header.size,
|
|
132
|
+
type: normalizeHeaderType(header.type),
|
|
133
|
+
},
|
|
134
|
+
body,
|
|
135
|
+
next,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
extractor.on("error", (err: Error) => {
|
|
140
|
+
pushError(err);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
extractor.on("finish", () => {
|
|
144
|
+
pushFinish();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
gunzip.on("error", (err: Error) => {
|
|
148
|
+
pushError(err);
|
|
149
|
+
extractor.destroy(err);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
source.on("error", (err: Error) => {
|
|
153
|
+
pushError(err);
|
|
154
|
+
gunzip.destroy(err);
|
|
155
|
+
extractor.destroy(err);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Kick off the pipeline. We wire stages with `.pipe(..., { end: true })`
|
|
159
|
+
// so that clean EOF from the source triggers `finish` on the extractor.
|
|
160
|
+
source.pipe(gunzip).pipe(extractor);
|
|
161
|
+
|
|
162
|
+
function nextEntry(): Promise<PendingEntry | null> {
|
|
163
|
+
if (pipelineError) return Promise.reject(pipelineError);
|
|
164
|
+
if (pending) {
|
|
165
|
+
const p = pending;
|
|
166
|
+
pending = null;
|
|
167
|
+
return Promise.resolve(p);
|
|
168
|
+
}
|
|
169
|
+
if (finished) return Promise.resolve(null);
|
|
170
|
+
return new Promise<PendingEntry | null>((resolve, reject) => {
|
|
171
|
+
waiter = { resolve, reject };
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
while (true) {
|
|
177
|
+
const entry = await nextEntry();
|
|
178
|
+
if (entry === null) return;
|
|
179
|
+
|
|
180
|
+
let advanced = false;
|
|
181
|
+
const advance = (err?: Error | null): void => {
|
|
182
|
+
if (advanced) return;
|
|
183
|
+
advanced = true;
|
|
184
|
+
entry.next(err ?? null);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// When the consumer finishes (or abandons) the body, release the tar
|
|
188
|
+
// extractor so the next entry can flow.
|
|
189
|
+
entry.body.once("end", () => advance());
|
|
190
|
+
entry.body.once("close", () => advance());
|
|
191
|
+
entry.body.once("error", (err: Error) => advance(err));
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
yield { header: entry.header, body: entry.body };
|
|
195
|
+
} catch (err) {
|
|
196
|
+
// Caller threw (or re-threw) inside the for-await loop. Propagate
|
|
197
|
+
// after cleanup in the finally below.
|
|
198
|
+
advance(err instanceof Error ? err : new Error(String(err)));
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// If the consumer neither consumed nor destroyed the body, drain it
|
|
203
|
+
// for them so the extractor can advance.
|
|
204
|
+
if (!advanced) {
|
|
205
|
+
entry.body.resume();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} finally {
|
|
209
|
+
// Early termination (break, throw, or natural completion): tear down the
|
|
210
|
+
// pipeline so we don't leak the socket/file descriptor underneath.
|
|
211
|
+
if (!finished || pipelineError) {
|
|
212
|
+
source.destroy();
|
|
213
|
+
gunzip.destroy();
|
|
214
|
+
extractor.destroy();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -29,7 +29,7 @@ const ManifestFileEntry = z.object({
|
|
|
29
29
|
size: z.number().int().nonnegative(),
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
const ManifestSchema = z.object({
|
|
32
|
+
export const ManifestSchema = z.object({
|
|
33
33
|
schema_version: z.string(),
|
|
34
34
|
created_at: z.string(),
|
|
35
35
|
source: z.string().optional(),
|
|
@@ -176,7 +176,7 @@ function sha256Hex(data: Uint8Array | string): string {
|
|
|
176
176
|
* Canonicalize a JSON object by sorting keys recursively, then SHA-256 hash it.
|
|
177
177
|
* This matches the platform's canonicalization approach.
|
|
178
178
|
*/
|
|
179
|
-
function canonicalizeJson(obj: unknown): string {
|
|
179
|
+
export function canonicalizeJson(obj: unknown): string {
|
|
180
180
|
return JSON.stringify(obj, (_key, value) => {
|
|
181
181
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
182
182
|
const sorted: Record<string, unknown> = {};
|
|
@@ -189,6 +189,18 @@ function canonicalizeJson(obj: unknown): string {
|
|
|
189
189
|
});
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Recompute the `manifest_sha256` field for a manifest object. Strips the
|
|
194
|
+
* `manifest_sha256` property, canonicalizes the remaining JSON, and returns
|
|
195
|
+
* the SHA-256 hex digest. Centralized here so the streaming validator and
|
|
196
|
+
* the in-memory validator agree on the exact canonicalization.
|
|
197
|
+
*/
|
|
198
|
+
export function computeManifestSha256(manifest: unknown): string {
|
|
199
|
+
const copy = { ...(manifest as Record<string, unknown>) };
|
|
200
|
+
delete copy.manifest_sha256;
|
|
201
|
+
return sha256Hex(canonicalizeJson(copy));
|
|
202
|
+
}
|
|
203
|
+
|
|
192
204
|
// ---------------------------------------------------------------------------
|
|
193
205
|
// Core validation
|
|
194
206
|
// ---------------------------------------------------------------------------
|
|
@@ -300,10 +312,7 @@ export function validateVBundle(data: Uint8Array): VBundleValidationResult {
|
|
|
300
312
|
// Step 5: Verify manifest checksum
|
|
301
313
|
// The manifest_sha256 field is the SHA-256 of the canonicalized JSON
|
|
302
314
|
// with the manifest_sha256 field itself excluded.
|
|
303
|
-
const
|
|
304
|
-
delete manifestForChecksum.manifest_sha256;
|
|
305
|
-
const canonicalized = canonicalizeJson(manifestForChecksum);
|
|
306
|
-
const computedManifestSha256 = sha256Hex(canonicalized);
|
|
315
|
+
const computedManifestSha256 = computeManifestSha256(manifestRaw);
|
|
307
316
|
|
|
308
317
|
if (computedManifestSha256 !== manifest.manifest_sha256) {
|
|
309
318
|
errors.push({
|
|
@@ -33,7 +33,10 @@ const addedMessages: Array<{
|
|
|
33
33
|
}> = [];
|
|
34
34
|
let createConversationShouldThrow = false;
|
|
35
35
|
|
|
36
|
+
const realConversationCrud =
|
|
37
|
+
await import("../../../memory/conversation-crud.js");
|
|
36
38
|
mock.module("../../../memory/conversation-crud.js", () => ({
|
|
39
|
+
...realConversationCrud,
|
|
37
40
|
createConversation: (opts: unknown) => {
|
|
38
41
|
if (createConversationShouldThrow) {
|
|
39
42
|
throw new Error("synthetic createConversation failure");
|
|
@@ -51,10 +54,37 @@ mock.module("../../../memory/conversation-crud.js", () => ({
|
|
|
51
54
|
addedMessages.push({ conversationId, role, content });
|
|
52
55
|
return { id: `msg-${addedMessages.length}` };
|
|
53
56
|
},
|
|
57
|
+
// Stub the message reader surface transitively required by other
|
|
58
|
+
// modules that route through conversation-crud. The home-feed route
|
|
59
|
+
// paths don't consult it directly, but Bun's ESM mock needs the named
|
|
60
|
+
// export to exist so transitive `import { getMessages }` resolves.
|
|
61
|
+
getMessages: () => [],
|
|
62
|
+
getMessagesPaginated: () => ({ messages: [], hasMore: false }),
|
|
63
|
+
getMessageById: () => null,
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
// Stub the rollup producer so the on-visit refresh trigger inside
|
|
67
|
+
// handleGetHomeFeed doesn't try to resolve a real provider or touch
|
|
68
|
+
// relationship state. The route calls the producer fire-and-forget,
|
|
69
|
+
// so tests observe the trigger via call-count on this spy rather
|
|
70
|
+
// than awaiting a return value.
|
|
71
|
+
//
|
|
72
|
+
// Default skip reason is `empty_items` — a real LLM attempt that
|
|
73
|
+
// returned nothing to consolidate. Using a real-run skip means the
|
|
74
|
+
// debounce gate holds firm in the default case (matching production
|
|
75
|
+
// semantics); individual tests override with `no_provider` etc. to
|
|
76
|
+
// exercise the rollback path.
|
|
77
|
+
const rollupProducerSpy = mock<() => Promise<unknown>>(async () => ({
|
|
78
|
+
wroteCount: 0,
|
|
79
|
+
skippedReason: "empty_items",
|
|
80
|
+
}));
|
|
81
|
+
mock.module("../../../home/rollup-producer.js", () => ({
|
|
82
|
+
runRollupProducer: rollupProducerSpy,
|
|
54
83
|
}));
|
|
55
84
|
|
|
56
85
|
// Dynamic imports so module mocks are wired before evaluation.
|
|
57
86
|
const {
|
|
87
|
+
__resetOnVisitRefreshStateForTests,
|
|
58
88
|
computeGreeting,
|
|
59
89
|
formatRelativeTime,
|
|
60
90
|
handleGetHomeFeed,
|
|
@@ -125,6 +155,8 @@ beforeEach(() => {
|
|
|
125
155
|
origWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
|
|
126
156
|
process.env.VELLUM_WORKSPACE_DIR = workspaceDir;
|
|
127
157
|
publishSpy.mockClear();
|
|
158
|
+
rollupProducerSpy.mockClear();
|
|
159
|
+
__resetOnVisitRefreshStateForTests();
|
|
128
160
|
createdConversations.length = 0;
|
|
129
161
|
addedMessages.length = 0;
|
|
130
162
|
createConversationShouldThrow = false;
|
|
@@ -322,6 +354,85 @@ describe("handleGetHomeFeed", () => {
|
|
|
322
354
|
expect(body.contextBanner.timeAwayLabel).toBe("2 hours ago");
|
|
323
355
|
});
|
|
324
356
|
|
|
357
|
+
test("fires the rollup producer fire-and-forget on the first GET", async () => {
|
|
358
|
+
const res = await handleGetHomeFeed(
|
|
359
|
+
new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
|
|
360
|
+
);
|
|
361
|
+
expect(res.status).toBe(200);
|
|
362
|
+
// Yield a microtask so the fire-and-forget call reaches its
|
|
363
|
+
// first await point.
|
|
364
|
+
await Promise.resolve();
|
|
365
|
+
expect(rollupProducerSpy).toHaveBeenCalledTimes(1);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test("does NOT refire the rollup when the debounce window has not elapsed", async () => {
|
|
369
|
+
await handleGetHomeFeed(
|
|
370
|
+
new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
|
|
371
|
+
);
|
|
372
|
+
await Promise.resolve();
|
|
373
|
+
expect(rollupProducerSpy).toHaveBeenCalledTimes(1);
|
|
374
|
+
|
|
375
|
+
// A second GET milliseconds later should NOT re-trigger the
|
|
376
|
+
// rollup — the 10-minute debounce prevents aggressive pollers or
|
|
377
|
+
// multiple panels from firing repeat refreshes.
|
|
378
|
+
await handleGetHomeFeed(
|
|
379
|
+
new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
|
|
380
|
+
);
|
|
381
|
+
await handleGetHomeFeed(
|
|
382
|
+
new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
|
|
383
|
+
);
|
|
384
|
+
await Promise.resolve();
|
|
385
|
+
expect(rollupProducerSpy).toHaveBeenCalledTimes(1);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test("debounce is rolled back when the producer skips before the LLM call", async () => {
|
|
389
|
+
// Simulate the daemon-boot race: the first GET fires the
|
|
390
|
+
// producer but the provider registry isn't ready yet, so the
|
|
391
|
+
// producer short-circuits with `no_provider`. A second GET a
|
|
392
|
+
// moment later must still be allowed to fire — otherwise Home
|
|
393
|
+
// stays stale for the full 10-minute debounce window while the
|
|
394
|
+
// user is actively trying to refresh.
|
|
395
|
+
rollupProducerSpy.mockImplementationOnce(async () => ({
|
|
396
|
+
wroteCount: 0,
|
|
397
|
+
skippedReason: "no_provider",
|
|
398
|
+
}));
|
|
399
|
+
|
|
400
|
+
await handleGetHomeFeed(
|
|
401
|
+
new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
|
|
402
|
+
);
|
|
403
|
+
// Let the fire-and-forget `.then()` that performs the rollback
|
|
404
|
+
// run before we issue the second GET. Two microtask ticks
|
|
405
|
+
// because the chain is runRollupProducer -> .then handler.
|
|
406
|
+
await Promise.resolve();
|
|
407
|
+
await Promise.resolve();
|
|
408
|
+
expect(rollupProducerSpy).toHaveBeenCalledTimes(1);
|
|
409
|
+
|
|
410
|
+
// Second GET — producer is now ready. Gate must have been
|
|
411
|
+
// rolled back so this GET re-fires the producer.
|
|
412
|
+
await handleGetHomeFeed(
|
|
413
|
+
new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
|
|
414
|
+
);
|
|
415
|
+
await Promise.resolve();
|
|
416
|
+
expect(rollupProducerSpy).toHaveBeenCalledTimes(2);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test("rollup producer failure does not turn the GET into an error", async () => {
|
|
420
|
+
// Even if the rollup producer rejects, the GET must still return
|
|
421
|
+
// 200 with the cached feed — the refresh is fire-and-forget.
|
|
422
|
+
rollupProducerSpy.mockImplementationOnce(async () => {
|
|
423
|
+
throw new Error("synthetic rollup failure");
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const res = await handleGetHomeFeed(
|
|
427
|
+
new Request("http://localhost/v1/home/feed?timeAwaySeconds=0"),
|
|
428
|
+
);
|
|
429
|
+
expect(res.status).toBe(200);
|
|
430
|
+
// Drain the rejected promise so it doesn't leak into the next
|
|
431
|
+
// test as an unhandled rejection.
|
|
432
|
+
await Promise.resolve();
|
|
433
|
+
await Promise.resolve();
|
|
434
|
+
});
|
|
435
|
+
|
|
325
436
|
test("newCount counts only status=new after filtering", async () => {
|
|
326
437
|
writeFeedFile([
|
|
327
438
|
makeItem({ id: "a", status: "new" }),
|