@vellumai/assistant 0.8.3 → 0.8.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/ARCHITECTURE.md +2 -2
- package/docker-entrypoint.sh +0 -1
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +1492 -100
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +302 -33
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
- package/src/__tests__/audit-log-rotation.test.ts +70 -16
- package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
- package/src/__tests__/btw-routes.test.ts +2 -3
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +2 -1
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
- package/src/__tests__/computer-use-tools.test.ts +2 -4
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
- package/src/__tests__/conversation-agent-loop.test.ts +188 -129
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-fork-crud.test.ts +324 -0
- package/src/__tests__/conversation-lifecycle.test.ts +53 -12
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +7 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/gateway-flag-listener.test.ts +237 -0
- package/src/__tests__/gemini-provider.test.ts +78 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/injector-chain.test.ts +2 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
- package/src/__tests__/llm-resolver.test.ts +161 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/mcp-abort-signal.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/model-intents.test.ts +2 -4
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +151 -0
- package/src/__tests__/openai-responses-provider.test.ts +118 -16
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -5
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
- package/src/__tests__/plugin-types.test.ts +3 -2
- package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
- package/src/__tests__/pricing.test.ts +12 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/registry.test.ts +2 -8
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
- package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +161 -124
- package/src/__tests__/terminal-tools.test.ts +12 -2
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-approval-handler.test.ts +1 -5
- package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
- package/src/__tests__/tool-executor.test.ts +89 -53
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +94 -10
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +1 -1
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +65 -20
- package/src/api/README.md +5 -0
- package/src/api/index.ts +4 -0
- package/src/api/package.json +10 -0
- package/src/background-wake/background-wake-routes.test.ts +233 -0
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
- package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
- package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/inference.ts +93 -40
- package/src/cli/commands/memory-v2-compare-render.ts +115 -0
- package/src/cli/commands/memory-v2.ts +483 -0
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/program.ts +2 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +24 -12
- package/src/config/call-site-defaults.ts +20 -0
- package/src/config/feature-flag-registry.json +115 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
- package/src/config/schemas/call-site-catalog.ts +35 -0
- package/src/config/schemas/llm.ts +14 -0
- package/src/config/schemas/memory-v2.ts +294 -1
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +60 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
- package/src/daemon/conversation-agent-loop.ts +307 -88
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +149 -118
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +145 -84
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +70 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +54 -32
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +2 -0
- package/src/daemon/handlers/conversations.ts +90 -3
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +5 -5
- package/src/daemon/host-file-proxy.ts +5 -5
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +11 -11
- package/src/daemon/lifecycle.ts +40 -23
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +14 -9
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +66 -7
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +52 -0
- package/src/daemon/tool-setup-types.ts +13 -0
- package/src/daemon/trust-context.ts +6 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +85 -0
- package/src/home/suggested-prompts.ts +168 -9
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/registries.ts +8 -12
- package/src/memory/__tests__/db-async-query.test.ts +165 -0
- package/src/memory/__tests__/db-maintenance.test.ts +115 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +191 -100
- package/src/memory/conversation-starters-cadence.ts +3 -1
- package/src/memory/conversation-title-service.ts +19 -3
- package/src/memory/db-async-query.ts +214 -0
- package/src/memory/db-init.ts +26 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/bootstrap.ts +8 -1
- package/src/memory/graph/capability-seed.ts +7 -3
- package/src/memory/graph/conversation-graph-memory.ts +100 -17
- package/src/memory/graph/extraction.ts +1 -5
- package/src/memory/graph/graph-search.ts +7 -1
- package/src/memory/indexer.ts +28 -18
- package/src/memory/job-handlers/cleanup.ts +76 -18
- package/src/memory/job-handlers/conversation-starters.ts +1 -4
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +68 -15
- package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
- package/src/memory/llm-request-log-source-local.ts +7 -0
- package/src/memory/llm-request-log-source.ts +9 -2
- package/src/memory/llm-request-log-store.ts +43 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +11 -3
- package/src/memory/memory-retrospective-job.ts +413 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +41 -14
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
- package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
- package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
- package/src/memory/migrations/index.ts +34 -0
- package/src/memory/migrations/registry.ts +58 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +22 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
- package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +158 -112
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +660 -4
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +124 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +224 -0
- package/src/memory/v2/harness/retriever.ts +74 -0
- package/src/memory/v2/harness/router-retriever.ts +43 -0
- package/src/memory/v2/harness/runner.ts +106 -0
- package/src/memory/v2/harness/trace.ts +58 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +42 -25
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +369 -62
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +563 -0
- package/src/memory/v3/__tests__/filter.test.ts +512 -0
- package/src/memory/v3/__tests__/gate.test.ts +574 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
- package/src/memory/v3/__tests__/loop.test.ts +530 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +440 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +469 -0
- package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
- package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
- package/src/memory/v3/__tests__/validate.test.ts +245 -0
- package/src/memory/v3/auto-edges.ts +223 -0
- package/src/memory/v3/coactivation-store.ts +124 -0
- package/src/memory/v3/consolidation-job.ts +323 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +249 -0
- package/src/memory/v3/filter.ts +281 -0
- package/src/memory/v3/gate.ts +334 -0
- package/src/memory/v3/index-composition.ts +113 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +382 -0
- package/src/memory/v3/maintenance.ts +144 -0
- package/src/memory/v3/prompt-context.ts +33 -0
- package/src/memory/v3/prompts/consolidation.ts +458 -0
- package/src/memory/v3/prompts/system-prompts.ts +196 -0
- package/src/memory/v3/retriever.ts +33 -0
- package/src/memory/v3/scouts.ts +420 -0
- package/src/memory/v3/shadow-middleware.ts +305 -0
- package/src/memory/v3/traversal.ts +206 -0
- package/src/memory/v3/tree-index.ts +237 -0
- package/src/memory/v3/tree-store.ts +394 -0
- package/src/memory/v3/tree-walk.ts +351 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +300 -0
- package/src/messaging/providers/index.ts +7 -1
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +38 -50
- package/src/notifications/home-feed-side-effect.ts +60 -30
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +8 -5
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +6 -3
- package/src/plugin-api/index.ts +4 -0
- package/src/plugin-api/types.ts +7 -33
- package/src/plugins/defaults/index.ts +6 -0
- package/src/plugins/defaults/injectors.ts +100 -20
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +11 -16
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +69 -19
- package/src/prompts/system-prompt.ts +118 -216
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +10 -2
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +281 -9
- package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +159 -66
- package/src/providers/call-site-routing.ts +14 -2
- package/src/providers/connection-model-compat.ts +38 -0
- package/src/providers/connection-resolution.ts +16 -2
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/gemini/client.ts +49 -6
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/adapter-factory.ts +18 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +91 -1
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +63 -23
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +86 -23
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/provider-send-message.ts +7 -1
- package/src/providers/retry.ts +34 -3
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/types.ts +25 -0
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +152 -56
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +43 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -6
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +50 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +188 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +147 -2
- package/src/runtime/routes/conversation-list-routes.ts +12 -4
- package/src/runtime/routes/conversation-management-routes.ts +77 -20
- package/src/runtime/routes/conversation-query-routes.ts +196 -31
- package/src/runtime/routes/conversation-routes.ts +472 -425
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/domain-routes.ts +60 -10
- package/src/runtime/routes/email-routes.ts +5 -2
- package/src/runtime/routes/events-routes.ts +54 -10
- package/src/runtime/routes/group-routes.ts +24 -8
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +17 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +20 -4
- package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
- package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +427 -0
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/secret-routes.ts +25 -5
- package/src/runtime/routes/settings-routes.ts +12 -11
- package/src/runtime/routes/slack-channel-routes.ts +188 -0
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/verification-outbound-actions.ts +73 -1
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/telemetry/types.ts +12 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/acp/spawn.test.ts +119 -0
- package/src/tools/acp/spawn.ts +15 -2
- package/src/tools/apps/definitions.ts +2 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
- package/src/tools/ask-question/ask-question-tool.ts +38 -45
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
- package/src/tools/browser/browser-execution.ts +106 -0
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
- package/src/tools/browser/cdp-client/factory.ts +171 -4
- package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
- package/src/tools/browser/cdp-client/types.ts +101 -0
- package/src/tools/browser/pinned-tabs.ts +146 -0
- package/src/tools/computer-use/definitions.ts +22 -78
- package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
- package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
- package/src/tools/credentials/vault.ts +3 -9
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +187 -2
- package/src/tools/execution-target.ts +21 -23
- package/src/tools/executor.ts +6 -1
- package/src/tools/filesystem/edit.ts +3 -9
- package/src/tools/filesystem/list.ts +3 -9
- package/src/tools/filesystem/read.ts +3 -9
- package/src/tools/filesystem/write.ts +3 -9
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.ts +3 -9
- package/src/tools/host-terminal/host-shell.ts +3 -9
- package/src/tools/mcp/mcp-tool-factory.ts +1 -8
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +216 -73
- package/src/tools/network/web-search.ts +216 -98
- package/src/tools/registry.ts +7 -23
- package/src/tools/schema-transforms.ts +1 -1
- package/src/tools/skills/execute.ts +3 -9
- package/src/tools/skills/load.ts +3 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -8
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +31 -98
- package/src/tools/ui-surface/definitions.ts +9 -23
- package/src/types/onboarding-context.ts +4 -0
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -7
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
- package/src/__tests__/message-complete-display-id.test.ts +0 -175
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/prompts/cache-boundary.ts +0 -8
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Suites:
|
|
5
5
|
* - POST /v1/acp/spawn — the three failure paths produced by
|
|
6
6
|
* `resolveAcpAgent` (acp_disabled, unknown_agent, binary_not_found).
|
|
7
|
+
* - POST /v1/acp/spawn (env injection) — CLAUDE_CODE_OAUTH_TOKEN is read
|
|
8
|
+
* from the secure store under the canonical
|
|
9
|
+
* `credential/acp/claude_oauth_token` key (built by `credentialKey()`)
|
|
10
|
+
* and merged into `agentConfig.env` ONLY for the `claude` agent.
|
|
7
11
|
* - DELETE /v1/acp/sessions?status=completed — the bulk-clear route that
|
|
8
12
|
* wipes terminal-state rows (completed/failed/cancelled) from
|
|
9
13
|
* `acp_session_history` while leaving running/initializing rows intact.
|
|
@@ -42,10 +46,21 @@ afterAll(() => {
|
|
|
42
46
|
});
|
|
43
47
|
|
|
44
48
|
// Stub `getAcpSessionManager` so the DELETE /:id tests can drive the
|
|
45
|
-
// in-memory-status check without spawning real ACP processes
|
|
46
|
-
//
|
|
49
|
+
// in-memory-status check without spawning real ACP processes, and so the
|
|
50
|
+
// env-injection spawn tests can capture the `agentConfig` arg without
|
|
51
|
+
// launching a real subprocess. Stored in mutable state so individual tests
|
|
52
|
+
// can plant arbitrary states / inspect capture.
|
|
47
53
|
const inMemoryStates = new Map<string, AcpSessionState>();
|
|
48
54
|
|
|
55
|
+
interface CapturedSpawn {
|
|
56
|
+
agent: string;
|
|
57
|
+
agentConfig: { env?: Record<string, string> };
|
|
58
|
+
task: string;
|
|
59
|
+
conversationId: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const capturedSpawns: CapturedSpawn[] = [];
|
|
63
|
+
|
|
49
64
|
mock.module("../../acp/index.js", () => ({
|
|
50
65
|
getAcpSessionManager: () => ({
|
|
51
66
|
getStatus: (id?: string) => {
|
|
@@ -56,9 +71,27 @@ mock.module("../../acp/index.js", () => ({
|
|
|
56
71
|
if (!state) throw new Error(`ACP session "${id}" not found`);
|
|
57
72
|
return state;
|
|
58
73
|
},
|
|
74
|
+
spawn: async (
|
|
75
|
+
agent: string,
|
|
76
|
+
agentConfig: { env?: Record<string, string> },
|
|
77
|
+
task: string,
|
|
78
|
+
_cwd: string | undefined,
|
|
79
|
+
conversationId: string,
|
|
80
|
+
) => {
|
|
81
|
+
capturedSpawns.push({ agent, agentConfig, task, conversationId });
|
|
82
|
+
return { acpSessionId: "acp-test", protocolSessionId: "proto-test" };
|
|
83
|
+
},
|
|
59
84
|
}),
|
|
60
85
|
}));
|
|
61
86
|
|
|
87
|
+
// Stub secure-keys so env-injection tests can plant a known token (or
|
|
88
|
+
// absence). Driven via `secureKeyStore` per test in beforeEach.
|
|
89
|
+
const secureKeyStore = new Map<string, string>();
|
|
90
|
+
|
|
91
|
+
mock.module("../../security/secure-keys.js", () => ({
|
|
92
|
+
getSecureKeyAsync: async (key: string) => secureKeyStore.get(key),
|
|
93
|
+
}));
|
|
94
|
+
|
|
62
95
|
import { eq } from "drizzle-orm";
|
|
63
96
|
|
|
64
97
|
import { getDb, getSqlite } from "../../memory/db-connection.js";
|
|
@@ -79,6 +112,8 @@ function getSpawnHandler() {
|
|
|
79
112
|
beforeEach(() => {
|
|
80
113
|
config.setConfig({});
|
|
81
114
|
which.setWhich((cmd) => `/usr/local/bin/${cmd}`);
|
|
115
|
+
capturedSpawns.length = 0;
|
|
116
|
+
secureKeyStore.clear();
|
|
82
117
|
});
|
|
83
118
|
|
|
84
119
|
// ---------------------------------------------------------------------------
|
|
@@ -146,6 +181,212 @@ describe("POST /v1/acp/spawn", () => {
|
|
|
146
181
|
});
|
|
147
182
|
});
|
|
148
183
|
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// POST /v1/acp/spawn — CLAUDE_CODE_OAUTH_TOKEN env injection + preflight
|
|
186
|
+
//
|
|
187
|
+
// claude-agent-acp authenticates via CLAUDE_CODE_OAUTH_TOKEN. The route
|
|
188
|
+
// accepts the token from two provisioning routes:
|
|
189
|
+
// 1. Secure store under the canonical `credential/acp/claude_oauth_token`
|
|
190
|
+
// key (built by `credentialKey()`), populated by
|
|
191
|
+
// `assistant credentials set --service acp --field claude_oauth_token`.
|
|
192
|
+
// 2. `acp.agents.claude.env.CLAUDE_CODE_OAUTH_TOKEN` in the user's
|
|
193
|
+
// config.json, surfaced on `resolved.agent.env` by the resolver.
|
|
194
|
+
// After merging the secure-store value into `agentConfig.env`, the route
|
|
195
|
+
// preflights for the token and throws `FailedDependencyError` if it is
|
|
196
|
+
// still absent. The "fail-fast" behavior is symmetric with the existing
|
|
197
|
+
// `binary_not_found` preflight and avoids the zombie-subprocess footgun
|
|
198
|
+
// where claude-agent-acp launches, crashes on auth, and leaves the
|
|
199
|
+
// caller with no useful signal.
|
|
200
|
+
//
|
|
201
|
+
// These tests pin both the happy paths and the throw path so a future
|
|
202
|
+
// drift in the key path, the env-override route, or the preflight check
|
|
203
|
+
// fails the suite loudly.
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
describe("POST /v1/acp/spawn — CLAUDE_CODE_OAUTH_TOKEN injection", () => {
|
|
207
|
+
test("injects CLAUDE_CODE_OAUTH_TOKEN from credential/acp/claude_oauth_token for the claude agent", async () => {
|
|
208
|
+
secureKeyStore.set(
|
|
209
|
+
"credential/acp/claude_oauth_token",
|
|
210
|
+
"test-token-abc123",
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const handler = getSpawnHandler();
|
|
214
|
+
await handler({
|
|
215
|
+
body: {
|
|
216
|
+
agent: "claude",
|
|
217
|
+
task: "do a thing",
|
|
218
|
+
conversationId: "conv-1",
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(capturedSpawns).toHaveLength(1);
|
|
223
|
+
expect(capturedSpawns[0]?.agent).toBe("claude");
|
|
224
|
+
expect(capturedSpawns[0]?.agentConfig.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
|
|
225
|
+
"test-token-abc123",
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("accepts CLAUDE_CODE_OAUTH_TOKEN from acp.agents.claude.env (config.json override) without a secure-store entry", async () => {
|
|
230
|
+
// The user-supplied config.json env override is the first-priority
|
|
231
|
+
// provisioning route. resolveAcpAgent returns it on `resolved.agent.env`,
|
|
232
|
+
// which the route then preserves on `agentConfig.env`. The preflight
|
|
233
|
+
// should accept this path with no secure-store entry needed.
|
|
234
|
+
config.setConfig({
|
|
235
|
+
agents: {
|
|
236
|
+
claude: {
|
|
237
|
+
command: "claude-agent-acp",
|
|
238
|
+
args: [],
|
|
239
|
+
env: { CLAUDE_CODE_OAUTH_TOKEN: "config-token-xyz789" },
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const handler = getSpawnHandler();
|
|
245
|
+
await handler({
|
|
246
|
+
body: {
|
|
247
|
+
agent: "claude",
|
|
248
|
+
task: "do a thing",
|
|
249
|
+
conversationId: "conv-1",
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
expect(capturedSpawns).toHaveLength(1);
|
|
254
|
+
expect(capturedSpawns[0]?.agentConfig.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
|
|
255
|
+
"config-token-xyz789",
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("config.json env override wins over a secure-store token (precedence pin)", async () => {
|
|
260
|
+
// Codex review feedback (PR #31901 / P2): when a user explicitly sets
|
|
261
|
+
// CLAUDE_CODE_OAUTH_TOKEN under `acp.agents.<id>.env` (per-workspace,
|
|
262
|
+
// rotated, scoped credential, etc.), the secure-store value must NOT
|
|
263
|
+
// silently overwrite it. Vault is fallback, not override.
|
|
264
|
+
secureKeyStore.set("credential/acp/claude_oauth_token", "vault-token-AAA");
|
|
265
|
+
config.setConfig({
|
|
266
|
+
agents: {
|
|
267
|
+
claude: {
|
|
268
|
+
command: "claude-agent-acp",
|
|
269
|
+
args: [],
|
|
270
|
+
env: { CLAUDE_CODE_OAUTH_TOKEN: "config-token-BBB" },
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const handler = getSpawnHandler();
|
|
276
|
+
await handler({
|
|
277
|
+
body: {
|
|
278
|
+
agent: "claude",
|
|
279
|
+
task: "do a thing",
|
|
280
|
+
conversationId: "conv-1",
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(capturedSpawns).toHaveLength(1);
|
|
285
|
+
expect(capturedSpawns[0]?.agentConfig.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
|
|
286
|
+
"config-token-BBB",
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("injects via command match for a user-defined agent id aliased to claude-agent-acp", async () => {
|
|
291
|
+
// Codex review feedback (PR #31901 / P2): gating is keyed off the
|
|
292
|
+
// resolved command (basename), not the agent id. A custom agent id
|
|
293
|
+
// pointing at claude-agent-acp still needs CLAUDE_CODE_OAUTH_TOKEN,
|
|
294
|
+
// so injection + preflight must fire regardless of the id string.
|
|
295
|
+
secureKeyStore.set("credential/acp/claude_oauth_token", "vault-token-zzz");
|
|
296
|
+
config.setConfig({
|
|
297
|
+
agents: {
|
|
298
|
+
"my-claude": {
|
|
299
|
+
command: "claude-agent-acp",
|
|
300
|
+
args: [],
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const handler = getSpawnHandler();
|
|
306
|
+
await handler({
|
|
307
|
+
body: {
|
|
308
|
+
agent: "my-claude",
|
|
309
|
+
task: "do a thing",
|
|
310
|
+
conversationId: "conv-1",
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
expect(capturedSpawns).toHaveLength(1);
|
|
315
|
+
expect(capturedSpawns[0]?.agent).toBe("my-claude");
|
|
316
|
+
expect(capturedSpawns[0]?.agentConfig.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
|
|
317
|
+
"vault-token-zzz",
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("throws FailedDependencyError when no CLAUDE_CODE_OAUTH_TOKEN is available from any source", async () => {
|
|
322
|
+
// secureKeyStore intentionally empty AND no agentConfig.env override —
|
|
323
|
+
// simulates a fresh install where the user hasn't provisioned a token
|
|
324
|
+
// via either route. Fail-fast preflight surfaces this immediately
|
|
325
|
+
// instead of letting claude-agent-acp launch, crash on auth, and leave
|
|
326
|
+
// a zombie subprocess behind.
|
|
327
|
+
|
|
328
|
+
const handler = getSpawnHandler();
|
|
329
|
+
await expect(
|
|
330
|
+
handler({
|
|
331
|
+
body: {
|
|
332
|
+
agent: "claude",
|
|
333
|
+
task: "do a thing",
|
|
334
|
+
conversationId: "conv-1",
|
|
335
|
+
},
|
|
336
|
+
}),
|
|
337
|
+
).rejects.toThrow(/CLAUDE_CODE_OAUTH_TOKEN/);
|
|
338
|
+
expect(capturedSpawns).toHaveLength(0);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("does NOT inject CLAUDE_CODE_OAUTH_TOKEN for agents whose command is not claude-agent-acp", async () => {
|
|
342
|
+
// Token-injection AND preflight are scoped to claude-agent-acp by
|
|
343
|
+
// command basename. A codex-acp spawn with the secure-store key set
|
|
344
|
+
// must still launch without that env var — and must not be blocked
|
|
345
|
+
// by claude's preflight.
|
|
346
|
+
secureKeyStore.set(
|
|
347
|
+
"credential/acp/claude_oauth_token",
|
|
348
|
+
"test-token-abc123",
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const handler = getSpawnHandler();
|
|
352
|
+
await handler({
|
|
353
|
+
body: {
|
|
354
|
+
agent: "codex",
|
|
355
|
+
task: "do a thing",
|
|
356
|
+
conversationId: "conv-1",
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
expect(capturedSpawns).toHaveLength(1);
|
|
361
|
+
expect(capturedSpawns[0]?.agent).toBe("codex");
|
|
362
|
+
expect(
|
|
363
|
+
capturedSpawns[0]?.agentConfig.env?.CLAUDE_CODE_OAUTH_TOKEN,
|
|
364
|
+
).toBeUndefined();
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test("does NOT pick up a token planted at the legacy non-`credential/` key path", async () => {
|
|
368
|
+
// Regression guard: the original implementation used the raw key
|
|
369
|
+
// "acp/claude/oauth_token". The fix routes through `credentialKey()`
|
|
370
|
+
// so the CLI (`assistant credentials set --service acp --field
|
|
371
|
+
// claude_oauth_token`) is the canonical provisioning path. Pin this
|
|
372
|
+
// by planting the token ONLY under the legacy key — the preflight
|
|
373
|
+
// should fail-fast because the canonical path is empty.
|
|
374
|
+
secureKeyStore.set("acp/claude/oauth_token", "legacy-token-should-miss");
|
|
375
|
+
|
|
376
|
+
const handler = getSpawnHandler();
|
|
377
|
+
await expect(
|
|
378
|
+
handler({
|
|
379
|
+
body: {
|
|
380
|
+
agent: "claude",
|
|
381
|
+
task: "do a thing",
|
|
382
|
+
conversationId: "conv-1",
|
|
383
|
+
},
|
|
384
|
+
}),
|
|
385
|
+
).rejects.toThrow(/CLAUDE_CODE_OAUTH_TOKEN/);
|
|
386
|
+
expect(capturedSpawns).toHaveLength(0);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
149
390
|
// ---------------------------------------------------------------------------
|
|
150
391
|
// DELETE /v1/acp/sessions?status=completed — bulk-clear terminal rows
|
|
151
392
|
// ---------------------------------------------------------------------------
|
|
@@ -205,7 +446,9 @@ describe("DELETE /v1/acp/sessions?status=completed", () => {
|
|
|
205
446
|
seedHistoryRow("row-initializing", "initializing", 5000);
|
|
206
447
|
|
|
207
448
|
const handler = getBulkDeleteHandler();
|
|
208
|
-
const result = (await handler({
|
|
449
|
+
const result = (await handler({
|
|
450
|
+
queryParams: { status: "completed" },
|
|
451
|
+
})) as {
|
|
209
452
|
deleted: number;
|
|
210
453
|
};
|
|
211
454
|
expect(result.deleted).toBe(3);
|
|
@@ -221,7 +464,9 @@ describe("DELETE /v1/acp/sessions?status=completed", () => {
|
|
|
221
464
|
seedHistoryRow("row-running", "running", 1000);
|
|
222
465
|
|
|
223
466
|
const handler = getBulkDeleteHandler();
|
|
224
|
-
const result = (await handler({
|
|
467
|
+
const result = (await handler({
|
|
468
|
+
queryParams: { status: "completed" },
|
|
469
|
+
})) as {
|
|
225
470
|
deleted: number;
|
|
226
471
|
};
|
|
227
472
|
expect(result.deleted).toBe(0);
|
|
@@ -244,7 +489,9 @@ describe("DELETE /v1/acp/sessions?status=completed", () => {
|
|
|
244
489
|
seedHistoryRow("row-completed", "completed", 1000);
|
|
245
490
|
|
|
246
491
|
const handler = getBulkDeleteHandler();
|
|
247
|
-
expect(() => handler({ queryParams: { status: "failed" } })).toThrow(
|
|
492
|
+
expect(() => handler({ queryParams: { status: "failed" } })).toThrow(
|
|
493
|
+
"status",
|
|
494
|
+
);
|
|
248
495
|
expect(listRows()).toHaveLength(1);
|
|
249
496
|
});
|
|
250
497
|
});
|
|
@@ -297,7 +544,9 @@ describe("DELETE /v1/acp/sessions/:id", () => {
|
|
|
297
544
|
insertHistoryRow({ id: "sess-completed", status: "completed" });
|
|
298
545
|
|
|
299
546
|
const handler = getDeleteSessionHandler();
|
|
300
|
-
const result = (await handler({
|
|
547
|
+
const result = (await handler({
|
|
548
|
+
pathParams: { id: "sess-completed" },
|
|
549
|
+
})) as {
|
|
301
550
|
deleted: boolean;
|
|
302
551
|
};
|
|
303
552
|
expect(result.deleted).toBe(true);
|
|
@@ -8,6 +8,7 @@ import { desc, eq, inArray } from "drizzle-orm";
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import { getAcpSessionManager } from "../../acp/index.js";
|
|
11
|
+
import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
|
|
11
12
|
import { resolveAcpAgent } from "../../acp/resolve-agent.js";
|
|
12
13
|
import type { AcpSessionState } from "../../acp/types.js";
|
|
13
14
|
import { getDb } from "../../memory/db-connection.js";
|
|
@@ -81,6 +82,12 @@ async function spawnSession({ body }: RouteHandlerArgs) {
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
// Inject required env vars and preflight via the shared helper. See
|
|
86
|
+
// `acp/prepare-agent-env.ts` for the full rationale; calling it here
|
|
87
|
+
// keeps the HTTP route in lockstep with the skill-tool spawn path
|
|
88
|
+
// (`tools/acp/spawn.ts`).
|
|
89
|
+
const agentConfig = await prepareAgentEnv(resolved.agent);
|
|
90
|
+
|
|
84
91
|
log.info(
|
|
85
92
|
{ agent, task: task.slice(0, 100), conversationId },
|
|
86
93
|
"ACP spawn request received",
|
|
@@ -89,7 +96,7 @@ async function spawnSession({ body }: RouteHandlerArgs) {
|
|
|
89
96
|
const manager = getAcpSessionManager();
|
|
90
97
|
const { acpSessionId, protocolSessionId } = await manager.spawn(
|
|
91
98
|
agent,
|
|
92
|
-
|
|
99
|
+
agentConfig,
|
|
93
100
|
task,
|
|
94
101
|
cwd,
|
|
95
102
|
conversationId,
|
|
@@ -78,7 +78,10 @@ function handleConfirm({ body }: RouteHandlerArgs) {
|
|
|
78
78
|
// ACP permissions: resolve directly without a Conversation object.
|
|
79
79
|
// No PermissionPrompter involved, so the route owns deregistration.
|
|
80
80
|
if (interaction.directResolve) {
|
|
81
|
-
pendingInteractions.resolve(
|
|
81
|
+
pendingInteractions.resolve(
|
|
82
|
+
requestId,
|
|
83
|
+
effectiveDecision === "allow" ? "approved" : "rejected",
|
|
84
|
+
);
|
|
82
85
|
interaction.directResolve(effectiveDecision as UserDecision);
|
|
83
86
|
return { accepted: true };
|
|
84
87
|
}
|
|
@@ -40,7 +40,7 @@ function handleGetCharacterComponents() {
|
|
|
40
40
|
return getCharacterComponents();
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
function handleRenderFromTraits({ body }: RouteHandlerArgs) {
|
|
43
|
+
function handleRenderFromTraits({ body, headers }: RouteHandlerArgs) {
|
|
44
44
|
const traits = body as CharacterTraits | undefined;
|
|
45
45
|
|
|
46
46
|
if (
|
|
@@ -69,11 +69,11 @@ function handleRenderFromTraits({ body }: RouteHandlerArgs) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
updateIdentityAvatarSection(null, log);
|
|
72
|
-
publishAvatarChanged();
|
|
72
|
+
publishAvatarChanged(headers?.["x-vellum-client-id"]?.trim() || undefined);
|
|
73
73
|
return { ok: true };
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
async function handleGenerateAvatar({ body }: RouteHandlerArgs) {
|
|
76
|
+
async function handleGenerateAvatar({ body, headers }: RouteHandlerArgs) {
|
|
77
77
|
const description = (body as Record<string, unknown>)?.description as
|
|
78
78
|
| string
|
|
79
79
|
| undefined;
|
|
@@ -109,11 +109,11 @@ async function handleGenerateAvatar({ body }: RouteHandlerArgs) {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
updateIdentityAvatarSection(null, log);
|
|
112
|
-
publishAvatarChanged();
|
|
112
|
+
publishAvatarChanged(headers?.["x-vellum-client-id"]?.trim() || undefined);
|
|
113
113
|
return { ok: true, message: result.content };
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
function handleSetAvatar({ body }: RouteHandlerArgs) {
|
|
116
|
+
function handleSetAvatar({ body, headers }: RouteHandlerArgs) {
|
|
117
117
|
const imagePath = (body as Record<string, unknown>)?.imagePath as
|
|
118
118
|
| string
|
|
119
119
|
| undefined;
|
|
@@ -142,11 +142,11 @@ function handleSetAvatar({ body }: RouteHandlerArgs) {
|
|
|
142
142
|
copyFileSync(normalized, avatarPath);
|
|
143
143
|
|
|
144
144
|
updateIdentityAvatarSection(null, log);
|
|
145
|
-
publishAvatarChanged();
|
|
145
|
+
publishAvatarChanged(headers?.["x-vellum-client-id"]?.trim() || undefined);
|
|
146
146
|
return { ok: true };
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
function handleRemoveAvatar(
|
|
149
|
+
function handleRemoveAvatar({ headers }: RouteHandlerArgs) {
|
|
150
150
|
const avatarPath = getAvatarImagePath();
|
|
151
151
|
|
|
152
152
|
if (!existsSync(avatarPath)) {
|
|
@@ -172,7 +172,7 @@ function handleRemoveAvatar(_args: RouteHandlerArgs) {
|
|
|
172
172
|
"Default character avatar (no custom image set)",
|
|
173
173
|
log,
|
|
174
174
|
);
|
|
175
|
-
publishAvatarChanged();
|
|
175
|
+
publishAvatarChanged(headers?.["x-vellum-client-id"]?.trim() || undefined);
|
|
176
176
|
return { ok: true, hadAvatar: true };
|
|
177
177
|
}
|
|
178
178
|
|
|
@@ -286,8 +286,8 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
286
286
|
operationId: "notify_avatar_updated",
|
|
287
287
|
endpoint: "avatar/notify-updated",
|
|
288
288
|
method: "POST",
|
|
289
|
-
handler: () => {
|
|
290
|
-
publishAvatarChanged();
|
|
289
|
+
handler: ({ headers }: RouteHandlerArgs) => {
|
|
290
|
+
publishAvatarChanged(headers?.["x-vellum-client-id"]?.trim() || undefined);
|
|
291
291
|
return { ok: true };
|
|
292
292
|
},
|
|
293
293
|
summary: "Notify avatar updated",
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { computeNextBackgroundWakeIntent } from "../../background-wake/next-wake.js";
|
|
4
|
+
import { getBackgroundWakeRuntime } from "../../background-wake/runtime-registry.js";
|
|
5
|
+
import { BadRequestError, ServiceUnavailableError } from "./errors.js";
|
|
6
|
+
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
7
|
+
|
|
8
|
+
const PREPARE_SLEEP_DEFER_WINDOW_MS = 60_000;
|
|
9
|
+
const HEARTBEAT_DUE_TOLERANCE_MS = 1_000;
|
|
10
|
+
|
|
11
|
+
let computeWakeIntent = computeNextBackgroundWakeIntent;
|
|
12
|
+
const timestampInputSchema = z.union([z.number(), z.string()]);
|
|
13
|
+
|
|
14
|
+
const wakeIntentSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
nextWakeAt: z.number(),
|
|
17
|
+
actualNextDueAt: z.number(),
|
|
18
|
+
reason: z.enum(["heartbeat", "schedule", "mixed"]),
|
|
19
|
+
sourceGeneration: z.string(),
|
|
20
|
+
computedAt: z.number(),
|
|
21
|
+
sourcePayload: z.record(z.string(), z.unknown()),
|
|
22
|
+
})
|
|
23
|
+
.nullable();
|
|
24
|
+
|
|
25
|
+
const normalizedDrainDueRequestSchema = z.object({
|
|
26
|
+
leaseId: z.string().min(1),
|
|
27
|
+
reason: z.string().min(1),
|
|
28
|
+
sourceGeneration: z.string().min(1),
|
|
29
|
+
startedAt: z.number(),
|
|
30
|
+
deadlineAt: z.number(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const drainDueRequestSchema = z.union([
|
|
34
|
+
z.object({
|
|
35
|
+
leaseId: z.string().min(1),
|
|
36
|
+
reason: z.string().min(1),
|
|
37
|
+
sourceGeneration: z.string().min(1),
|
|
38
|
+
startedAt: timestampInputSchema,
|
|
39
|
+
deadlineAt: timestampInputSchema,
|
|
40
|
+
}),
|
|
41
|
+
z.object({
|
|
42
|
+
lease_id: z.string().min(1),
|
|
43
|
+
reason: z.string().min(1),
|
|
44
|
+
source_generation: z.string().min(1),
|
|
45
|
+
started_at: timestampInputSchema,
|
|
46
|
+
deadline_at: timestampInputSchema,
|
|
47
|
+
}),
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
function normalizeDrainDueBody(body: Record<string, unknown>) {
|
|
51
|
+
return {
|
|
52
|
+
leaseId: body.leaseId ?? body.lease_id,
|
|
53
|
+
reason: body.reason,
|
|
54
|
+
sourceGeneration: body.sourceGeneration ?? body.source_generation,
|
|
55
|
+
startedAt: normalizeTimestamp(body.startedAt ?? body.started_at),
|
|
56
|
+
deadlineAt: normalizeTimestamp(body.deadlineAt ?? body.deadline_at),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeTimestamp(value: unknown): unknown {
|
|
61
|
+
if (typeof value === "number") return value;
|
|
62
|
+
if (typeof value !== "string") return value;
|
|
63
|
+
const numeric = Number(value);
|
|
64
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
65
|
+
const parsed = Date.parse(value);
|
|
66
|
+
return Number.isFinite(parsed) ? parsed : value;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseDrainDueBody(body: Record<string, unknown>) {
|
|
70
|
+
const parsed = normalizedDrainDueRequestSchema.safeParse(
|
|
71
|
+
normalizeDrainDueBody(body),
|
|
72
|
+
);
|
|
73
|
+
if (!parsed.success) {
|
|
74
|
+
throw new BadRequestError(
|
|
75
|
+
"leaseId, reason, sourceGeneration, startedAt, and deadlineAt are required",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return parsed.data;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function handleGetIntent() {
|
|
82
|
+
return { intent: computeWakeIntent() };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function handlePrepareSleep() {
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
const intent = computeWakeIntent(now);
|
|
88
|
+
return {
|
|
89
|
+
intent,
|
|
90
|
+
deferSleep:
|
|
91
|
+
intent != null &&
|
|
92
|
+
intent.nextWakeAt <= now + PREPARE_SLEEP_DEFER_WINDOW_MS,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function handleDrainDue(body: Record<string, unknown>) {
|
|
97
|
+
const request = parseDrainDueBody(body);
|
|
98
|
+
const runtime = getBackgroundWakeRuntime();
|
|
99
|
+
if (!runtime) {
|
|
100
|
+
throw new ServiceUnavailableError(
|
|
101
|
+
"Background wake runtime is not registered",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const now = Date.now();
|
|
106
|
+
const heartbeatDue =
|
|
107
|
+
runtime.heartbeat.nextRunAt != null &&
|
|
108
|
+
runtime.heartbeat.nextRunAt <= now + HEARTBEAT_DUE_TOLERANCE_MS;
|
|
109
|
+
const heartbeatRan = heartbeatDue ? await runtime.heartbeat.runOnce() : false;
|
|
110
|
+
const scheduledCount = await runtime.scheduler.runOnce();
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
leaseId: request.leaseId,
|
|
114
|
+
reason: request.reason,
|
|
115
|
+
sourceGeneration: request.sourceGeneration,
|
|
116
|
+
startedAt: request.startedAt,
|
|
117
|
+
deadlineAt: request.deadlineAt,
|
|
118
|
+
counts: {
|
|
119
|
+
heartbeat: heartbeatRan ? 1 : 0,
|
|
120
|
+
scheduler: scheduledCount,
|
|
121
|
+
total: (heartbeatRan ? 1 : 0) + scheduledCount,
|
|
122
|
+
},
|
|
123
|
+
nextIntent: computeWakeIntent(),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** @internal Test helper for route-only tests. */
|
|
128
|
+
export function setBackgroundWakeIntentComputerForTest(
|
|
129
|
+
nextCompute: typeof computeNextBackgroundWakeIntent | null,
|
|
130
|
+
): void {
|
|
131
|
+
computeWakeIntent = nextCompute ?? computeNextBackgroundWakeIntent;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const ROUTES: RouteDefinition[] = [
|
|
135
|
+
{
|
|
136
|
+
operationId: "getBackgroundWakeIntent",
|
|
137
|
+
endpoint: "background-wake/intent",
|
|
138
|
+
method: "GET",
|
|
139
|
+
policyKey: "background-wake",
|
|
140
|
+
summary: "Get background wake intent",
|
|
141
|
+
description: "Return the current computed background wake intent.",
|
|
142
|
+
tags: ["background-wake"],
|
|
143
|
+
responseBody: z.object({
|
|
144
|
+
intent: wakeIntentSchema,
|
|
145
|
+
}),
|
|
146
|
+
handler: () => handleGetIntent(),
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
operationId: "prepareBackgroundWakeSleep",
|
|
150
|
+
endpoint: "background-wake/prepare-sleep",
|
|
151
|
+
method: "POST",
|
|
152
|
+
policyKey: "background-wake",
|
|
153
|
+
summary: "Prepare for assistant sleep",
|
|
154
|
+
description:
|
|
155
|
+
"Return the current background wake intent and whether sleep should be deferred.",
|
|
156
|
+
tags: ["background-wake"],
|
|
157
|
+
responseBody: z.object({
|
|
158
|
+
intent: wakeIntentSchema,
|
|
159
|
+
deferSleep: z.boolean(),
|
|
160
|
+
}),
|
|
161
|
+
handler: () => handlePrepareSleep(),
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
operationId: "drainDueBackgroundWake",
|
|
165
|
+
endpoint: "background-wake/drain-due",
|
|
166
|
+
method: "POST",
|
|
167
|
+
policyKey: "background-wake",
|
|
168
|
+
summary: "Drain due background wake work",
|
|
169
|
+
description:
|
|
170
|
+
"Run due heartbeat and scheduler work for a background wake lease.",
|
|
171
|
+
tags: ["background-wake"],
|
|
172
|
+
requestBody: drainDueRequestSchema,
|
|
173
|
+
responseBody: z.object({
|
|
174
|
+
leaseId: z.string(),
|
|
175
|
+
reason: z.string(),
|
|
176
|
+
sourceGeneration: z.string(),
|
|
177
|
+
startedAt: z.number(),
|
|
178
|
+
deadlineAt: z.number(),
|
|
179
|
+
counts: z.object({
|
|
180
|
+
heartbeat: z.number(),
|
|
181
|
+
scheduler: z.number(),
|
|
182
|
+
total: z.number(),
|
|
183
|
+
}),
|
|
184
|
+
nextIntent: wakeIntentSchema,
|
|
185
|
+
}),
|
|
186
|
+
handler: ({ body }: RouteHandlerArgs) => handleDrainDue(body ?? {}),
|
|
187
|
+
},
|
|
188
|
+
];
|