@vellumai/assistant 0.6.4 → 0.6.5
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/ARCHITECTURE.md +32 -36
- package/Dockerfile +12 -0
- package/README.md +3 -4
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +1 -20
- package/docs/architecture/security.md +16 -16
- package/docs/error-handling.md +111 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- 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/openapi.yaml +123 -11
- 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__/approval-cascade.test.ts +31 -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__/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__/call-controller.test.ts +1 -2
- package/src/__tests__/call-site-routing-provider.test.ts +214 -0
- package/src/__tests__/catalog-cache.test.ts +27 -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 +428 -501
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-circuit-breaker.test.ts +336 -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-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +427 -114
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +530 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
- package/src/__tests__/conversation-agent-loop.test.ts +412 -82
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
- package/src/__tests__/conversation-process-callsite.test.ts +306 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
- package/src/__tests__/conversation-queue.test.ts +41 -26
- package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
- package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +34 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
- package/src/__tests__/conversation-speed-override.test.ts +30 -11
- 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 +2 -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 +3 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
- package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- 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__/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__/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 +26 -7
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- 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__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/intent-routing.test.ts +1 -40
- 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__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +684 -0
- package/src/__tests__/model-intents.test.ts +9 -83
- 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__/oauth-store.test.ts +10 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -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__/permission-checker-host-gate.test.ts +1 -1
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/pricing.test.ts +50 -3
- 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 +0 -1
- package/src/__tests__/reaction-persistence.test.ts +560 -0
- 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__/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-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 +160 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor.test.ts +60 -94
- 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__/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-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +1 -13
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +209 -17
- 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/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/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/cli/AGENTS.md +1 -1
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- 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 +666 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +451 -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 +594 -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/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +14 -1
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +300 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- 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/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 +23 -4
- package/src/cli.ts +0 -37
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -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 +11 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -175
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +25 -9
- 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 +30 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/calls.ts +0 -4
- 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 +318 -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 +53 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skills.ts +2 -2
- package/src/context/__tests__/compact-prompt.test.ts +45 -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 +12 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/window-manager.ts +229 -25
- 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/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 -1
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
- package/src/daemon/conversation-agent-loop.ts +462 -80
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-lifecycle.ts +30 -6
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-process.ts +10 -4
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +760 -29
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +10 -5
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +118 -30
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- 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 +54 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +2 -2
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +76 -14
- package/src/daemon/message-types/conversations.ts +14 -0
- package/src/daemon/message-types/messages.ts +9 -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 +117 -9
- package/src/daemon/tool-side-effects.ts +0 -9
- package/src/daemon/watch-handler.ts +4 -4
- 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/heartbeat-service.ts +76 -28
- 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 +4 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +56 -2
- package/src/home/relationship-state-writer.ts +2 -2
- 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 +73 -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 +4 -4
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +61 -0
- package/src/ipc/routes/browser.ts +96 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/index.ts +17 -1
- 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/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +100 -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 +103 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-title-service.ts +7 -4
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-backend.ts +1 -1
- 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 +173 -51
- package/src/memory/graph/graph-search.test.ts +92 -0
- package/src/memory/graph/graph-search.ts +4 -1
- 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 +230 -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/140-backfill-usage-cache-accounting.ts +1 -1
- 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/index.ts +1 -0
- package/src/memory/pkb/pkb-index.test.ts +368 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +438 -0
- package/src/memory/pkb/pkb-search.ts +137 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.ts +122 -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 +1373 -0
- package/src/messaging/providers/slack/render-transcript.ts +443 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/oauth/oauth-store.ts +1 -0
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/seed-providers.ts +4 -2
- package/src/permissions/approval-policy.test.ts +948 -0
- package/src/permissions/approval-policy.ts +257 -0
- package/src/permissions/bash-risk-classifier.test.ts +1208 -0
- package/src/permissions/bash-risk-classifier.ts +707 -0
- package/src/permissions/checker.ts +217 -708
- package/src/permissions/command-registry.test.ts +535 -0
- package/src/permissions/command-registry.ts +825 -0
- package/src/permissions/defaults.ts +26 -78
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/risk-types.ts +205 -0
- package/src/permissions/secret-prompter.ts +53 -2
- 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 +23 -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 +1 -16
- package/src/platform/client.ts +19 -1
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- 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 +501 -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 +76 -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/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 +578 -0
- package/src/providers/speech-to-text/xai-realtime.ts +796 -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 +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- 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/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 +52 -1
- package/src/runtime/http-types.ts +23 -1
- 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-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 +58 -0
- package/src/runtime/routes/approval-routes.ts +12 -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/btw-routes.ts +1 -4
- package/src/runtime/routes/conversation-management-routes.ts +20 -2
- package/src/runtime/routes/conversation-routes.ts +133 -27
- 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 +912 -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/migration-routes.ts +720 -124
- 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 +28 -6
- package/src/schedule/scheduler.ts +8 -0
- 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 +26 -7
- 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 +45 -2
- package/src/tools/browser/browser-execution.ts +65 -38
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +4 -0
- 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/web-fetch.ts +20 -10
- package/src/tools/network/web-search.ts +19 -4
- package/src/tools/permission-checker.ts +36 -15
- package/src/tools/policy-context.ts +25 -8
- package/src/tools/registry.ts +55 -3
- 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/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 +12 -3
- 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 +2 -2
- package/src/util/pricing.ts +15 -5
- 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/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 +57 -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/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- 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__/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__/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/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/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/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/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/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/shared/provider-env-vars.ts +0 -19
- 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
|
@@ -113,7 +113,7 @@ export async function handleWatchObservation(
|
|
|
113
113
|
|
|
114
114
|
async function generateCommentary(session: WatchSession): Promise<void> {
|
|
115
115
|
try {
|
|
116
|
-
const provider = await getConfiguredProvider();
|
|
116
|
+
const provider = await getConfiguredProvider("watchCommentary");
|
|
117
117
|
if (!provider) {
|
|
118
118
|
log.warn(
|
|
119
119
|
{ watchId: session.watchId },
|
|
@@ -164,7 +164,7 @@ async function generateCommentary(session: WatchSession): Promise<void> {
|
|
|
164
164
|
systemPrompt,
|
|
165
165
|
{
|
|
166
166
|
config: {
|
|
167
|
-
|
|
167
|
+
callSite: "watchCommentary",
|
|
168
168
|
max_tokens: 200,
|
|
169
169
|
},
|
|
170
170
|
},
|
|
@@ -225,7 +225,7 @@ export async function generateSummary(session: WatchSession): Promise<void> {
|
|
|
225
225
|
},
|
|
226
226
|
"generateSummary starting — calling LLM",
|
|
227
227
|
);
|
|
228
|
-
const provider = await getConfiguredProvider();
|
|
228
|
+
const provider = await getConfiguredProvider("watchSummary");
|
|
229
229
|
if (!provider) {
|
|
230
230
|
log.warn(
|
|
231
231
|
{ watchId: session.watchId },
|
|
@@ -329,7 +329,7 @@ export async function generateSummary(session: WatchSession): Promise<void> {
|
|
|
329
329
|
systemPrompt,
|
|
330
330
|
{
|
|
331
331
|
config: {
|
|
332
|
-
|
|
332
|
+
callSite: "watchSummary",
|
|
333
333
|
max_tokens: 2000,
|
|
334
334
|
},
|
|
335
335
|
},
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ContentBlock,
|
|
3
|
+
Message,
|
|
4
|
+
ServerToolUseContent,
|
|
5
|
+
TextContent,
|
|
6
|
+
WebSearchToolResultContent,
|
|
7
|
+
} from "../providers/types.js";
|
|
8
|
+
|
|
9
|
+
export interface StripStats {
|
|
10
|
+
blocksStripped: number;
|
|
11
|
+
serverToolUsesDropped: number;
|
|
12
|
+
messagesModified: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface StripResult {
|
|
16
|
+
messages: Message[];
|
|
17
|
+
stats: StripStats;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Replaces every `web_search_tool_result` block in the message list with a
|
|
22
|
+
* plain `text` summary of its results, and drops the paired `server_tool_use`
|
|
23
|
+
* that produced it.
|
|
24
|
+
*
|
|
25
|
+
* Anthropic's `encrypted_content` tokens attached to each `web_search_result`
|
|
26
|
+
* are opaque server tokens with bounded validity (they expire and/or are
|
|
27
|
+
* route-scoped). Replaying a stale token produces
|
|
28
|
+
* `messages.N.content.M: Invalid encrypted_content in search_result block`.
|
|
29
|
+
* For historical turns the model does not need the opaque token to re-read
|
|
30
|
+
* the body — a title+url summary is sufficient to preserve context.
|
|
31
|
+
*
|
|
32
|
+
* Intended to run on `runMessages` immediately before the agent loop starts a
|
|
33
|
+
* new turn, at which point every `web_search_tool_result` in the list is by
|
|
34
|
+
* definition from a prior turn.
|
|
35
|
+
*/
|
|
36
|
+
export function stripHistoricalWebSearchResults(
|
|
37
|
+
messages: Message[],
|
|
38
|
+
): StripResult {
|
|
39
|
+
const stats: StripStats = {
|
|
40
|
+
blocksStripped: 0,
|
|
41
|
+
serverToolUsesDropped: 0,
|
|
42
|
+
messagesModified: 0,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const next: Message[] = messages.map((msg) => {
|
|
46
|
+
const droppedServerToolUseIds = new Set<string>();
|
|
47
|
+
const transformed: ContentBlock[] = [];
|
|
48
|
+
|
|
49
|
+
for (const block of msg.content) {
|
|
50
|
+
if (block.type !== "web_search_tool_result") continue;
|
|
51
|
+
const wsr = block as WebSearchToolResultContent;
|
|
52
|
+
const query = findQueryForToolUseId(msg.content, wsr.tool_use_id);
|
|
53
|
+
transformed.push(formatAsText(wsr, query));
|
|
54
|
+
droppedServerToolUseIds.add(wsr.tool_use_id);
|
|
55
|
+
stats.blocksStripped++;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (droppedServerToolUseIds.size === 0) return msg;
|
|
59
|
+
|
|
60
|
+
const rewritten: ContentBlock[] = [];
|
|
61
|
+
let wsrIndex = 0;
|
|
62
|
+
for (const block of msg.content) {
|
|
63
|
+
if (block.type === "server_tool_use") {
|
|
64
|
+
const stu = block as ServerToolUseContent;
|
|
65
|
+
if (droppedServerToolUseIds.has(stu.id)) {
|
|
66
|
+
stats.serverToolUsesDropped++;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
rewritten.push(block);
|
|
70
|
+
} else if (block.type === "web_search_tool_result") {
|
|
71
|
+
rewritten.push(transformed[wsrIndex++]);
|
|
72
|
+
} else {
|
|
73
|
+
rewritten.push(block);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
stats.messagesModified++;
|
|
78
|
+
return { ...msg, content: rewritten };
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return { messages: next, stats };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function findQueryForToolUseId(
|
|
85
|
+
blocks: ContentBlock[],
|
|
86
|
+
toolUseId: string,
|
|
87
|
+
): string | null {
|
|
88
|
+
for (const b of blocks) {
|
|
89
|
+
if (b.type !== "server_tool_use") continue;
|
|
90
|
+
const stu = b as ServerToolUseContent;
|
|
91
|
+
if (stu.id !== toolUseId) continue;
|
|
92
|
+
const q = stu.input?.query;
|
|
93
|
+
return typeof q === "string" ? q : null;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function formatAsText(
|
|
99
|
+
block: WebSearchToolResultContent,
|
|
100
|
+
query: string | null,
|
|
101
|
+
): TextContent {
|
|
102
|
+
const header = query
|
|
103
|
+
? `[Prior web_search results for "${query}":`
|
|
104
|
+
: "[Prior web_search results:";
|
|
105
|
+
|
|
106
|
+
const content = block.content;
|
|
107
|
+
if (!Array.isArray(content)) {
|
|
108
|
+
return { type: "text", text: `${header} (results unavailable)]` };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const entries = content
|
|
112
|
+
.filter(
|
|
113
|
+
(r): r is { type: string; title?: unknown; url?: unknown } =>
|
|
114
|
+
typeof r === "object" &&
|
|
115
|
+
r != null &&
|
|
116
|
+
(r as { type?: string }).type === "web_search_result",
|
|
117
|
+
)
|
|
118
|
+
.map((r, i) => {
|
|
119
|
+
const title = typeof r.title === "string" ? r.title : "(untitled)";
|
|
120
|
+
const url = typeof r.url === "string" ? r.url : "";
|
|
121
|
+
return url ? `${i + 1}. ${title}\n ${url}` : `${i + 1}. ${title}`;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const body = entries.length > 0 ? entries.join("\n") : "(no results)";
|
|
125
|
+
return { type: "text", text: `${header}\n${body}]` };
|
|
126
|
+
}
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { getConfig } from "../config/loader.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
6
6
|
import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
|
|
7
7
|
import { getLogger } from "../util/logger.js";
|
|
8
8
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
@@ -17,23 +17,24 @@ const FILING_PROMPT_TEMPLATE = `You are running a periodic knowledge base filing
|
|
|
17
17
|
## Part 1: File the buffer
|
|
18
18
|
|
|
19
19
|
Read \`pkb/buffer.md\`. For each item in the buffer:
|
|
20
|
-
1. Determine which topic file it belongs in. Check \`pkb/INDEX.md\` to see what topic files exist.
|
|
21
|
-
2. Read the target topic file, then
|
|
20
|
+
1. Determine which topic file(s) it belongs in. Check \`pkb/INDEX.md\` to see what topic files exist.
|
|
21
|
+
2. Read the target topic file(s), then integrate the new fact.
|
|
22
22
|
3. If the fact is important enough to always be in context, add it to \`pkb/essentials.md\` instead.
|
|
23
23
|
4. If the fact is a commitment, follow-up, or active project, add it to \`pkb/threads.md\`.
|
|
24
24
|
5. If no existing topic file fits, create a new one and update \`pkb/INDEX.md\`.
|
|
25
|
+
6. If the topic file is getting too long (>1500 tokens), compress it or split it into multiple topic files.
|
|
25
26
|
|
|
26
27
|
After all items are filed, clear the processed items from \`pkb/buffer.md\` (leave the file empty, don't delete it).
|
|
27
28
|
|
|
28
|
-
## Part 2:
|
|
29
|
+
## Part 2: Review
|
|
29
30
|
|
|
30
|
-
Pick
|
|
31
|
+
Pick 3 random topic files from your knowledge base and review them:
|
|
31
32
|
- Is the information still accurate and up to date?
|
|
32
33
|
- Are there duplicates that should be consolidated?
|
|
33
34
|
- Is anything important enough to promote to \`pkb/essentials.md\`?
|
|
34
35
|
- Is anything in \`pkb/essentials.md\` that's no longer essential? Demote it to a topic file.
|
|
35
36
|
- Are any threads in \`pkb/threads.md\` completed or stale? Remove them.
|
|
36
|
-
- Is any file getting too long?
|
|
37
|
+
- Is any file getting too long (>1500 tokens)? Strongly consider compressing it or splitting it into multiple topic files.
|
|
37
38
|
- Should any topic file be restructured for clarity?
|
|
38
39
|
|
|
39
40
|
Make improvements as you see fit. This is your knowledge base — keep it sharp.`;
|
|
@@ -42,7 +43,7 @@ export interface FilingDeps {
|
|
|
42
43
|
processMessage: (
|
|
43
44
|
conversationId: string,
|
|
44
45
|
content: string,
|
|
45
|
-
options?: {
|
|
46
|
+
options?: { callSite?: LLMCallSite },
|
|
46
47
|
) => Promise<{ messageId: string }>;
|
|
47
48
|
onConversationCreated?: (info: {
|
|
48
49
|
conversationId: string;
|
|
@@ -190,8 +191,6 @@ export class FilingService {
|
|
|
190
191
|
log.info("Running filing job");
|
|
191
192
|
|
|
192
193
|
try {
|
|
193
|
-
const config = getConfig().filing;
|
|
194
|
-
|
|
195
194
|
const conversation = bootstrapConversation({
|
|
196
195
|
conversationType: "background",
|
|
197
196
|
source: "filing",
|
|
@@ -206,7 +205,7 @@ export class FilingService {
|
|
|
206
205
|
});
|
|
207
206
|
|
|
208
207
|
await this.deps.processMessage(conversation.id, FILING_PROMPT_TEMPLATE, {
|
|
209
|
-
|
|
208
|
+
callSite: "filingAgent",
|
|
210
209
|
});
|
|
211
210
|
|
|
212
211
|
log.info({ conversationId: conversation.id }, "Filing job completed");
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { getConfig } from "../config/loader.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
6
6
|
import type { HeartbeatAlert } from "../daemon/message-protocol.js";
|
|
7
7
|
import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
|
|
8
8
|
import {
|
|
@@ -39,7 +39,8 @@ export function isShallowProfile(): boolean {
|
|
|
39
39
|
try {
|
|
40
40
|
const identityPath = getWorkspacePromptPath("IDENTITY.md");
|
|
41
41
|
const rawIdentity = readTextFileSync(identityPath);
|
|
42
|
-
const identity =
|
|
42
|
+
const identity =
|
|
43
|
+
rawIdentity != null ? stripCommentLines(rawIdentity) : null;
|
|
43
44
|
// `resolveGuardianPersona` returns already-stripped, trimmed content
|
|
44
45
|
// (or null for missing/empty files).
|
|
45
46
|
const user = resolveGuardianPersona();
|
|
@@ -81,7 +82,7 @@ export interface HeartbeatDeps {
|
|
|
81
82
|
processMessage: (
|
|
82
83
|
conversationId: string,
|
|
83
84
|
content: string,
|
|
84
|
-
options?: {
|
|
85
|
+
options?: { callSite?: LLMCallSite },
|
|
85
86
|
) => Promise<{ messageId: string }>;
|
|
86
87
|
alerter: (alert: HeartbeatAlert) => void;
|
|
87
88
|
onConversationCreated?: (info: {
|
|
@@ -222,11 +223,13 @@ export class HeartbeatService {
|
|
|
222
223
|
// permanently blocked. The .finally() handler still serves as the
|
|
223
224
|
// normal-completion cleanup path and uses an identity guard to avoid
|
|
224
225
|
// clearing a different run's activeRun.
|
|
225
|
-
run
|
|
226
|
-
|
|
227
|
-
this.activeRun
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
run
|
|
227
|
+
.finally(() => {
|
|
228
|
+
if (this.activeRun === run) {
|
|
229
|
+
this.activeRun = null;
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
.catch(() => {}); // Suppress unhandled rejection if executeRun rejects
|
|
230
233
|
|
|
231
234
|
let timerId: ReturnType<typeof setTimeout> | undefined;
|
|
232
235
|
try {
|
|
@@ -255,18 +258,47 @@ export class HeartbeatService {
|
|
|
255
258
|
this._nextRunAt = Date.now() + intervalMs;
|
|
256
259
|
}
|
|
257
260
|
|
|
258
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Run credential health checks and notify about unhealthy credentials.
|
|
263
|
+
* Returns a list of unhealthy provider names so callers can gate tool usage.
|
|
264
|
+
*/
|
|
265
|
+
private async runCredentialHealthCheck(): Promise<string[]> {
|
|
259
266
|
try {
|
|
260
|
-
const { checkAllCredentials } =
|
|
261
|
-
"../credential-health/credential-health-service.js"
|
|
262
|
-
);
|
|
267
|
+
const { checkAllCredentials } =
|
|
268
|
+
await import("../credential-health/credential-health-service.js");
|
|
263
269
|
const report = await checkAllCredentials();
|
|
264
270
|
if (report.unhealthy.length > 0) {
|
|
265
271
|
await this.notifyUnhealthyCredentials(report.unhealthy);
|
|
272
|
+
// Only block providers for hard-failure statuses — expiring and ping_failed
|
|
273
|
+
// are transient/still-usable and should not disable provider tools.
|
|
274
|
+
// missing_scopes is a hard failure because required scopes are absent and
|
|
275
|
+
// provider tools will predictably fail.
|
|
276
|
+
const hardFailureStatuses = new Set([
|
|
277
|
+
"revoked",
|
|
278
|
+
"missing_token",
|
|
279
|
+
"expired",
|
|
280
|
+
"missing_scopes",
|
|
281
|
+
]);
|
|
282
|
+
const hardFailures = report.unhealthy.filter((r) =>
|
|
283
|
+
hardFailureStatuses.has(r.status),
|
|
284
|
+
);
|
|
285
|
+
return [...new Set(hardFailures.map((r) => r.provider))];
|
|
266
286
|
}
|
|
267
287
|
} catch (err) {
|
|
268
|
-
log.
|
|
288
|
+
log.error({ err }, "Credential health check failed");
|
|
289
|
+
try {
|
|
290
|
+
this.deps.alerter({
|
|
291
|
+
type: "heartbeat_alert",
|
|
292
|
+
title: "Credential Health Check Failed",
|
|
293
|
+
body:
|
|
294
|
+
"Could not verify OAuth credential health. " +
|
|
295
|
+
(err instanceof Error ? err.message : String(err)),
|
|
296
|
+
});
|
|
297
|
+
} catch {
|
|
298
|
+
// Last resort — alerter itself failed. Already logged above.
|
|
299
|
+
}
|
|
269
300
|
}
|
|
301
|
+
return [];
|
|
270
302
|
}
|
|
271
303
|
|
|
272
304
|
private async notifyUnhealthyCredentials(
|
|
@@ -281,11 +313,13 @@ export class HeartbeatService {
|
|
|
281
313
|
): Promise<void> {
|
|
282
314
|
let emitNotificationSignal: typeof import("../notifications/emit-signal.js").emitNotificationSignal;
|
|
283
315
|
try {
|
|
284
|
-
({ emitNotificationSignal } =
|
|
285
|
-
"../notifications/emit-signal.js"
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
316
|
+
({ emitNotificationSignal } =
|
|
317
|
+
await import("../notifications/emit-signal.js"));
|
|
318
|
+
} catch (importErr) {
|
|
319
|
+
log.error(
|
|
320
|
+
{ err: importErr },
|
|
321
|
+
"Failed to import notification signal emitter",
|
|
322
|
+
);
|
|
289
323
|
return;
|
|
290
324
|
}
|
|
291
325
|
|
|
@@ -317,7 +351,7 @@ export class HeartbeatService {
|
|
|
317
351
|
routingIntent: "single_channel",
|
|
318
352
|
});
|
|
319
353
|
} catch (err) {
|
|
320
|
-
log.
|
|
354
|
+
log.error(
|
|
321
355
|
{ err, provider: result.provider, connectionId: result.connectionId },
|
|
322
356
|
"Failed to emit credential health notification",
|
|
323
357
|
);
|
|
@@ -329,13 +363,16 @@ export class HeartbeatService {
|
|
|
329
363
|
log.info("Running heartbeat");
|
|
330
364
|
|
|
331
365
|
// Credential health check — surface broken credentials proactively
|
|
332
|
-
// before the LLM heartbeat prompt runs.
|
|
333
|
-
|
|
366
|
+
// before the LLM heartbeat prompt runs. Returns unhealthy provider
|
|
367
|
+
// names so the prompt can instruct the LLM to skip those providers.
|
|
368
|
+
const unhealthyProviders = await this.runCredentialHealthCheck();
|
|
334
369
|
|
|
335
370
|
try {
|
|
336
|
-
const config = getConfig().heartbeat;
|
|
337
371
|
const checklist = this.readChecklist();
|
|
338
|
-
const { prompt, includedReengagement } = this.buildPrompt(
|
|
372
|
+
const { prompt, includedReengagement } = this.buildPrompt(
|
|
373
|
+
checklist,
|
|
374
|
+
unhealthyProviders,
|
|
375
|
+
);
|
|
339
376
|
|
|
340
377
|
const conversation = bootstrapConversation({
|
|
341
378
|
conversationType: "background",
|
|
@@ -351,7 +388,7 @@ export class HeartbeatService {
|
|
|
351
388
|
});
|
|
352
389
|
|
|
353
390
|
await this.deps.processMessage(conversation.id, prompt, {
|
|
354
|
-
|
|
391
|
+
callSite: "heartbeatAgent",
|
|
355
392
|
});
|
|
356
393
|
|
|
357
394
|
if (includedReengagement) {
|
|
@@ -368,7 +405,7 @@ export class HeartbeatService {
|
|
|
368
405
|
body: err instanceof Error ? err.message : String(err),
|
|
369
406
|
});
|
|
370
407
|
} catch (alertErr) {
|
|
371
|
-
log.
|
|
408
|
+
log.error({ alertErr }, "Failed to broadcast heartbeat alert");
|
|
372
409
|
}
|
|
373
410
|
}
|
|
374
411
|
}
|
|
@@ -381,14 +418,25 @@ export class HeartbeatService {
|
|
|
381
418
|
}
|
|
382
419
|
|
|
383
420
|
/** @internal Exposed for testing. */
|
|
384
|
-
buildPrompt(
|
|
421
|
+
buildPrompt(
|
|
422
|
+
checklist: string,
|
|
423
|
+
unhealthyProviders: string[] = [],
|
|
424
|
+
): { prompt: string; includedReengagement: boolean } {
|
|
385
425
|
let prompt = `You are running a periodic heartbeat check. Review the following checklist and take any necessary actions.
|
|
386
426
|
|
|
387
427
|
<heartbeat-checklist>
|
|
388
428
|
${checklist}
|
|
389
|
-
</heartbeat-checklist
|
|
429
|
+
</heartbeat-checklist>`;
|
|
430
|
+
|
|
431
|
+
if (unhealthyProviders.length > 0) {
|
|
432
|
+
const providers = unhealthyProviders.join(", ");
|
|
433
|
+
prompt += `\n\n<credential-status>
|
|
434
|
+
The following providers have broken or expired OAuth credentials: ${providers}.
|
|
435
|
+
Do NOT attempt to use tools for these providers — they will fail. Skip any checklist items that depend on them and note the outage in your summary.
|
|
436
|
+
</credential-status>`;
|
|
437
|
+
}
|
|
390
438
|
|
|
391
|
-
<heartbeat-disposition>
|
|
439
|
+
prompt += `\n\n<heartbeat-disposition>
|
|
392
440
|
After completing your review, end your response with one of:
|
|
393
441
|
- HEARTBEAT_OK — if everything looks good, no action needed
|
|
394
442
|
- HEARTBEAT_ALERT — if you found issues that need attention (describe them before this marker)
|
|
@@ -70,28 +70,36 @@ describe("startFeedScheduler", () => {
|
|
|
70
70
|
expect(summary2.gmailDigestRan).toBe(true);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
test("rollup only re-runs every
|
|
73
|
+
test("rollup only re-runs every 2 hours as the safety-net cadence", async () => {
|
|
74
|
+
// The scheduler is the safety net; the primary trigger is the
|
|
75
|
+
// on-visit refresh in home-feed-routes.ts. Long cadence is
|
|
76
|
+
// intentional so the scheduler doesn't fight the route.
|
|
74
77
|
handle = startFeedScheduler(defaultOptions());
|
|
75
78
|
|
|
76
79
|
const t0 = new Date("2026-04-14T12:00:00.000Z");
|
|
77
80
|
await handle.runOnce(t0);
|
|
78
81
|
|
|
79
|
-
//
|
|
80
|
-
const t1 = new Date("2026-04-14T12:
|
|
82
|
+
// 30 min later — below the 2-hour gate.
|
|
83
|
+
const t1 = new Date("2026-04-14T12:30:00.000Z");
|
|
81
84
|
const summary1 = await handle.runOnce(t1);
|
|
82
85
|
expect(summary1.rollupRan).toBe(false);
|
|
83
86
|
|
|
84
|
-
//
|
|
85
|
-
const t2 = new Date("2026-04-
|
|
87
|
+
// 1h later — still below the 2-hour gate.
|
|
88
|
+
const t2 = new Date("2026-04-14T13:00:00.000Z");
|
|
86
89
|
const summary2 = await handle.runOnce(t2);
|
|
87
|
-
expect(summary2.rollupRan).toBe(
|
|
90
|
+
expect(summary2.rollupRan).toBe(false);
|
|
91
|
+
|
|
92
|
+
// 2h 1m later — past the gate, should re-run.
|
|
93
|
+
const t3 = new Date("2026-04-14T14:01:00.000Z");
|
|
94
|
+
const summary3 = await handle.runOnce(t3);
|
|
95
|
+
expect(summary3.rollupRan).toBe(true);
|
|
88
96
|
});
|
|
89
97
|
|
|
90
98
|
test("rollup cooldown is NOT advanced on no_provider so the next tick retries", async () => {
|
|
91
99
|
// Mimic the daemon startup ordering: the scheduler boots before
|
|
92
100
|
// the provider registry is ready. The first tick gets no_provider;
|
|
93
101
|
// the next tick (even one second later) must still run the rollup
|
|
94
|
-
// instead of waiting
|
|
102
|
+
// instead of waiting 2 hours.
|
|
95
103
|
rollupRunner.mockImplementationOnce(async () => ({
|
|
96
104
|
wroteCount: 0,
|
|
97
105
|
skippedReason: "no_provider",
|
|
@@ -110,10 +118,30 @@ describe("startFeedScheduler", () => {
|
|
|
110
118
|
expect(rollupRunner).toHaveBeenCalledTimes(2);
|
|
111
119
|
});
|
|
112
120
|
|
|
121
|
+
test("rollup cooldown is NOT advanced on in_flight so the next tick retries", async () => {
|
|
122
|
+
// in_flight means another caller (on-visit refresh, usually) is
|
|
123
|
+
// already running the producer. Advancing the gate here would
|
|
124
|
+
// force the NEXT tick to wait out the full cadence window even
|
|
125
|
+
// though nothing broken happened — the other caller's result is
|
|
126
|
+
// effectively this tick's run.
|
|
127
|
+
rollupRunner.mockImplementationOnce(async () => ({
|
|
128
|
+
wroteCount: 0,
|
|
129
|
+
skippedReason: "in_flight",
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
handle = startFeedScheduler(defaultOptions());
|
|
133
|
+
await handle.runOnce(new Date("2026-04-14T12:00:00.000Z"));
|
|
134
|
+
expect(rollupRunner).toHaveBeenCalledTimes(1);
|
|
135
|
+
|
|
136
|
+
const summary = await handle.runOnce(new Date("2026-04-14T12:00:01.000Z"));
|
|
137
|
+
expect(summary.rollupRan).toBe(true);
|
|
138
|
+
expect(rollupRunner).toHaveBeenCalledTimes(2);
|
|
139
|
+
});
|
|
140
|
+
|
|
113
141
|
test("rollup cooldown is NOT advanced on no_actions so the next tick retries", async () => {
|
|
114
142
|
// no_actions means the activity log was empty — no LLM call was
|
|
115
143
|
// made. A subsequent tick should retry as soon as new actions
|
|
116
|
-
// land, not wait the full
|
|
144
|
+
// land, not wait the full 2-hour window.
|
|
117
145
|
rollupRunner.mockImplementationOnce(async () => ({
|
|
118
146
|
wroteCount: 0,
|
|
119
147
|
skippedReason: "no_actions",
|
|
@@ -131,7 +159,7 @@ describe("startFeedScheduler", () => {
|
|
|
131
159
|
|
|
132
160
|
test("rollup cooldown IS advanced on other skip reasons to preserve backoff", async () => {
|
|
133
161
|
// empty_items / malformed_output / provider_error are real LLM
|
|
134
|
-
// attempts — the next tick should be gated by the full
|
|
162
|
+
// attempts — the next tick should be gated by the full 2-hour
|
|
135
163
|
// window so a broken producer doesn't get hammered every tick.
|
|
136
164
|
rollupRunner.mockImplementationOnce(async () => ({
|
|
137
165
|
wroteCount: 0,
|
|
@@ -142,8 +170,8 @@ describe("startFeedScheduler", () => {
|
|
|
142
170
|
await handle.runOnce(new Date("2026-04-14T12:00:00.000Z"));
|
|
143
171
|
expect(rollupRunner).toHaveBeenCalledTimes(1);
|
|
144
172
|
|
|
145
|
-
//
|
|
146
|
-
const summary = await handle.runOnce(new Date("2026-04-14T12:
|
|
173
|
+
// Thirty minutes later — below the 2-hour gate, should NOT re-run.
|
|
174
|
+
const summary = await handle.runOnce(new Date("2026-04-14T12:30:00.000Z"));
|
|
147
175
|
expect(summary.rollupRan).toBe(false);
|
|
148
176
|
expect(rollupRunner).toHaveBeenCalledTimes(1);
|
|
149
177
|
});
|
|
@@ -364,6 +364,50 @@ describe("runRollupProducer", () => {
|
|
|
364
364
|
expect(result.wroteCount).toBe(0);
|
|
365
365
|
});
|
|
366
366
|
|
|
367
|
+
test("concurrent calls short-circuit the second with in_flight", async () => {
|
|
368
|
+
// Gate the provider behind a manually-controlled deferred so we
|
|
369
|
+
// can observe state while the first call is still inside the
|
|
370
|
+
// producer body. Without this we'd race the runtime's microtask
|
|
371
|
+
// scheduler to check in-flightness.
|
|
372
|
+
let release: ((value: ContentBlock[]) => void) | null = null;
|
|
373
|
+
const gated = new Promise<ContentBlock[]>((resolve) => {
|
|
374
|
+
release = resolve;
|
|
375
|
+
});
|
|
376
|
+
const provider = makeProvider(async () => {
|
|
377
|
+
const content = await gated;
|
|
378
|
+
return {
|
|
379
|
+
content,
|
|
380
|
+
model: "mock-model",
|
|
381
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
382
|
+
stopReason: "tool_use",
|
|
383
|
+
};
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const first = runRollupProducer(new Date(), {
|
|
387
|
+
writeItem,
|
|
388
|
+
loadRelationshipState: stubRelationshipState,
|
|
389
|
+
loadRecentActions: stubLoadRecentActions(oneAction),
|
|
390
|
+
resolveProvider: () => provider,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// Second call lands while `first` is blocked awaiting the gated
|
|
394
|
+
// provider response — the in-flight guard must short-circuit it.
|
|
395
|
+
const second = await runRollupProducer(new Date(), {
|
|
396
|
+
writeItem,
|
|
397
|
+
loadRelationshipState: stubRelationshipState,
|
|
398
|
+
loadRecentActions: stubLoadRecentActions(oneAction),
|
|
399
|
+
resolveProvider: () => provider,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
expect(second.skippedReason).toBe("in_flight");
|
|
403
|
+
expect(second.wroteCount).toBe(0);
|
|
404
|
+
|
|
405
|
+
// Release the first call and let it finish.
|
|
406
|
+
release!([toolUseContent({ items: [] })]);
|
|
407
|
+
const firstResult = await first;
|
|
408
|
+
expect(firstResult.skippedReason).toBe("empty_items");
|
|
409
|
+
});
|
|
410
|
+
|
|
367
411
|
test("clamps priority to the valid [0, 100] window", async () => {
|
|
368
412
|
const provider = scriptedProvider([
|
|
369
413
|
toolUseContent({
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
feedItemSchema,
|
|
42
42
|
type FeedItemSource,
|
|
43
43
|
type FeedItemType,
|
|
44
|
+
type FeedItemUrgency,
|
|
44
45
|
} from "./feed-types.js";
|
|
45
46
|
import { appendFeedItem } from "./feed-writer.js";
|
|
46
47
|
|
|
@@ -80,6 +81,8 @@ export interface WriteAssistantFeedItemParams {
|
|
|
80
81
|
minTimeAway?: number;
|
|
81
82
|
/** Absolute ISO-8601 expiry timestamp. */
|
|
82
83
|
expiresAt?: string;
|
|
84
|
+
/** Visual urgency treatment — controls badge color independently of sort priority. */
|
|
85
|
+
urgency?: FeedItemUrgency;
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
/**
|
|
@@ -110,6 +113,7 @@ export async function writeAssistantFeedItem(
|
|
|
110
113
|
timestamp: now,
|
|
111
114
|
createdAt: now,
|
|
112
115
|
actions: params.actions,
|
|
116
|
+
urgency: params.urgency,
|
|
113
117
|
minTimeAway: params.minTimeAway,
|
|
114
118
|
expiresAt: params.expiresAt,
|
|
115
119
|
};
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
type FeedItem,
|
|
44
44
|
feedItemSchema,
|
|
45
45
|
type FeedItemSource,
|
|
46
|
+
type FeedItemUrgency,
|
|
46
47
|
} from "./feed-types.js";
|
|
47
48
|
import { appendFeedItem } from "./feed-writer.js";
|
|
48
49
|
|
|
@@ -93,6 +94,8 @@ export interface EmitFeedEventParams {
|
|
|
93
94
|
* until the user dismisses it (default for activity-log actions).
|
|
94
95
|
*/
|
|
95
96
|
expiresAt?: string;
|
|
97
|
+
/** Visual urgency treatment — controls badge color independently of sort priority. */
|
|
98
|
+
urgency?: FeedItemUrgency;
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
/**
|
|
@@ -144,6 +147,7 @@ export async function emitFeedEvent(
|
|
|
144
147
|
timestamp: now,
|
|
145
148
|
createdAt: now,
|
|
146
149
|
actions: params.actions,
|
|
150
|
+
urgency: params.urgency,
|
|
147
151
|
minTimeAway: params.minTimeAway,
|
|
148
152
|
expiresAt: params.expiresAt,
|
|
149
153
|
};
|
|
@@ -43,7 +43,15 @@ const TICK_INTERVAL_MS = 5 * 60 * 1000;
|
|
|
43
43
|
|
|
44
44
|
/** Per-producer minimum gap between runs. */
|
|
45
45
|
const GMAIL_DIGEST_INTERVAL_MS = 5 * 60 * 1000;
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Roll-up cadence is deliberately long — 120 minutes — because the
|
|
48
|
+
* scheduler is the *safety net*, not the primary trigger. Opening the
|
|
49
|
+
* Home page fires a debounced on-visit refresh in the HTTP route (see
|
|
50
|
+
* `runtime/routes/home-feed-routes.ts`), which is the path most users
|
|
51
|
+
* actually hit. The scheduler exists so the feed still stays fresh
|
|
52
|
+
* for long idle stretches where nobody opens the Home page.
|
|
53
|
+
*/
|
|
54
|
+
const ROLLUP_INTERVAL_MS = 2 * 60 * 60 * 1000;
|
|
47
55
|
|
|
48
56
|
export interface FeedSchedulerHandle {
|
|
49
57
|
/** Stops the interval. Safe to call multiple times. */
|
|
@@ -146,8 +154,9 @@ export function startFeedScheduler(
|
|
|
146
154
|
"Rollup producer ran",
|
|
147
155
|
);
|
|
148
156
|
// Only advance the cooldown gate when the producer actually
|
|
149
|
-
// had a chance to run the LLM.
|
|
150
|
-
// before any provider call and should NOT burn the
|
|
157
|
+
// had a chance to run the LLM. Three skip reasons short-
|
|
158
|
+
// circuit before any provider call and should NOT burn the
|
|
159
|
+
// window:
|
|
151
160
|
// - `no_provider`: the provider registry wasn't ready yet
|
|
152
161
|
// (happens on the startup tick because the feed scheduler
|
|
153
162
|
// boots before the provider init pass in
|
|
@@ -155,12 +164,19 @@ export function startFeedScheduler(
|
|
|
155
164
|
// - `no_actions`: there was nothing to roll up. A subsequent
|
|
156
165
|
// tick should retry as soon as new actions land, not wait
|
|
157
166
|
// the full window.
|
|
167
|
+
// - `in_flight`: another caller (usually the on-visit
|
|
168
|
+
// refresh trigger in `home-feed-routes.ts`) is already
|
|
169
|
+
// running the rollup. That caller's result effectively
|
|
170
|
+
// counts as this scheduler tick's real run; bumping the
|
|
171
|
+
// gate here would force the NEXT tick to also wait out
|
|
172
|
+
// the full window even though nothing broken happened.
|
|
158
173
|
// Every other outcome — success, empty items, malformed
|
|
159
174
|
// output, provider error — is a real LLM attempt and does
|
|
160
175
|
// advance the gate so a broken producer doesn't hammer us.
|
|
161
176
|
if (
|
|
162
177
|
result.skippedReason !== "no_provider" &&
|
|
163
|
-
result.skippedReason !== "no_actions"
|
|
178
|
+
result.skippedReason !== "no_actions" &&
|
|
179
|
+
result.skippedReason !== "in_flight"
|
|
164
180
|
) {
|
|
165
181
|
lastRollupAt = nowMs;
|
|
166
182
|
}
|