@vellumai/assistant 0.11.7 → 0.11.8-staging.1
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 +10 -10
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +1 -1
- package/docs/error-handling.md +1 -2
- package/docs/guardian-request-flow.md +51 -5
- package/docs/trusted-contact-access.md +13 -12
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-catalog/package.json +19 -0
- package/{src/avatar/character-components.ts → node_modules/@vellumai/avatar-catalog/src/catalog.ts} +12 -44
- package/node_modules/@vellumai/avatar-catalog/src/colors.ts +19 -0
- package/node_modules/@vellumai/avatar-catalog/src/index.ts +25 -0
- package/node_modules/@vellumai/avatar-catalog/src/types.ts +47 -0
- package/node_modules/@vellumai/avatar-catalog/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +43 -12
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/test-path-guard.test.ts +89 -0
- package/node_modules/@vellumai/environments/src/test-path-guard.ts +113 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +8 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +46 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +46 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +62 -21
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +53 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +11 -5
- package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +1 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/openapi.yaml +481 -9
- package/package.json +3 -1
- package/scripts/sync-llm-catalog.ts +3 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +16 -0
- package/src/__tests__/agent-loop-compacted-history-transform.test.ts +304 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +3 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +3 -0
- package/src/__tests__/anthropic-provider.test.ts +56 -0
- package/src/__tests__/approval-cascade.test.ts +0 -13
- package/src/__tests__/attachments-store.test.ts +117 -6
- package/src/__tests__/attachments.test.ts +122 -0
- package/src/__tests__/bundled-asset.test.ts +22 -1
- package/src/__tests__/channel-approval.test.ts +14 -7
- package/src/__tests__/channel-approvals.test.ts +3 -3
- package/src/__tests__/channel-delivery-store.test.ts +58 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +1 -0
- package/src/__tests__/channel-invite-transport.test.ts +1 -0
- package/src/__tests__/channel-readiness-slack-remote.test.ts +3 -3
- package/src/__tests__/compaction-events.test.ts +0 -12
- package/src/__tests__/compactor-call-site-logging.test.ts +7 -1
- package/src/__tests__/compactor-retained-image-validation.test.ts +15 -0
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/contacts-write.test.ts +141 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -13
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -12
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -13
- package/src/__tests__/conversation-agent-loop.test.ts +95 -13
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -13
- package/src/__tests__/conversation-load-history-repair.test.ts +418 -0
- package/src/__tests__/conversation-media-retry.test.ts +33 -2
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -12
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -13
- package/src/__tests__/conversation-queue.test.ts +4 -13
- package/src/__tests__/conversation-retry-route.test.ts +5 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-sight-frames.test.ts +615 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -13
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -13
- package/src/__tests__/conversation-speed-override.test.ts +0 -12
- package/src/__tests__/conversation-store-ephemeral.test.ts +0 -12
- package/src/__tests__/conversation-summarize-route.test.ts +5 -0
- package/src/__tests__/conversation-summarize-up-to.test.ts +102 -0
- package/src/__tests__/conversation-wait-for-idle.test.ts +0 -12
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -17
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -17
- package/src/__tests__/credential-health-service.test.ts +45 -0
- package/src/__tests__/credential-record-retire.test.ts +256 -0
- package/src/__tests__/credential-record-write-through.test.ts +8 -4
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +0 -4
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +0 -4
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +0 -8
- package/src/__tests__/delete-propagation.test.ts +121 -3
- package/src/__tests__/evict-conversations-for-reload.test.ts +46 -6
- package/src/__tests__/file-ops-service.test.ts +8 -3
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/guardian-action-service-scope.test.ts +118 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +299 -5
- package/src/__tests__/guardian-expiry-notifier.test.ts +117 -13
- package/src/__tests__/guardian-gateway-sim.test.ts +119 -0
- package/src/__tests__/guardian-gateway-sim.ts +56 -20
- package/src/__tests__/guardian-reply-router-answer-mode.test.ts +6 -0
- package/src/__tests__/guardian-request-status-sync.test.ts +162 -0
- package/src/__tests__/guardian-wait-controller.test.ts +6 -0
- package/src/__tests__/headless-browser-mode.test.ts +27 -0
- package/src/__tests__/helpers/mock-conversation.ts +2 -0
- package/src/__tests__/list-messages-system-card.test.ts +70 -0
- package/src/__tests__/llm-catalog-parity.test.ts +2 -0
- package/src/__tests__/managed-profile-guard.test.ts +2 -2
- package/src/__tests__/media-resolve-image-validation.test.ts +106 -0
- package/src/__tests__/media-stream-server-integration.test.ts +6 -0
- package/src/__tests__/non-member-access-request.test.ts +12 -0
- package/src/__tests__/notification-discord-adapter.test.ts +76 -4
- package/src/__tests__/oauth-commands-routes.test.ts +21 -0
- package/src/__tests__/openai-provider.test.ts +63 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +114 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +63 -1
- package/src/__tests__/persist-unsendable-image.test.ts +19 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +2 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -1
- package/src/__tests__/process-message-display-content.test.ts +1 -1
- package/src/__tests__/processing-flag-persist-failure.test.ts +0 -5
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +0 -17
- package/src/__tests__/qdrant-manager.test.ts +6 -0
- package/src/__tests__/reaction-persistence.test.ts +58 -62
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +67 -0
- package/src/__tests__/server-history-render.test.ts +22 -0
- package/src/__tests__/sight-frame-retention.test.ts +362 -0
- package/src/__tests__/slack-reaction-approvals.test.ts +44 -24
- package/src/__tests__/slack-reaction-guardian-approval.test.ts +29 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-notify-stale-rebuild.test.ts +85 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +4 -4
- package/src/__tests__/terminal-tools.test.ts +9 -9
- package/src/__tests__/tool-result-spool.test.ts +5 -8
- package/src/__tests__/usage-attribution.test.ts +26 -0
- package/src/acp/__tests__/acp-auth-marker-store.test.ts +191 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +262 -0
- package/src/acp/__tests__/acp-connect-card-state.test.ts +17 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +10 -3
- package/src/acp/acp-auth-marker-store.ts +196 -0
- package/src/acp/acp-claude-oauth.ts +154 -4
- package/src/acp/acp-connect-card-state.ts +49 -8
- package/src/acp/agent-process.ts +1 -0
- package/src/acp/auto-install.ts +1 -0
- package/src/acp/client-handler.ts +1 -0
- package/src/acp/prepare-agent-env.ts +79 -1
- package/src/acp/resolve-agent.ts +12 -0
- package/src/acp/session-manager.test.ts +183 -0
- package/src/acp/session-manager.ts +110 -6
- package/src/acp/types.ts +17 -0
- package/src/agent/attachments.ts +7 -2
- package/src/agent/loop.ts +59 -1
- package/src/api/events/contact-form-closed.ts +32 -0
- package/src/api/events/contact-record-request.ts +63 -0
- package/src/api/events/contact-request.ts +10 -3
- package/src/api/index.ts +20 -0
- package/src/api/responses/conversation-message.ts +20 -0
- package/src/api/responses/home.ts +101 -0
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-card-withdrawal.ts +127 -30
- package/src/approvals/guardian-decision-primitive.ts +9 -0
- package/src/approvals/guardian-expiry-notifier.ts +22 -16
- package/src/approvals/guardian-request-status-sync.ts +98 -0
- package/src/avatar/svg-compositor.ts +1 -1
- package/src/avatar/traits-png-sync.ts +1 -1
- package/src/bundler/app-compiler.ts +1 -0
- package/src/bundler/compiler-tools.ts +1 -0
- package/src/bundler/package-resolver.ts +1 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +183 -3
- package/src/calls/guardian-action-sweep.ts +6 -1
- package/src/calls/voice-session-bridge.ts +46 -1
- package/src/channels/__tests__/gateway-guardian-requests.test.ts +13 -18
- package/src/channels/gateway-guardian-requests.ts +37 -18
- package/src/cli/__tests__/contacts-record-prompt-cli.test.ts +329 -0
- package/src/cli/commands/__tests__/browser.test.ts +65 -1
- package/src/cli/commands/browser.ts +30 -10
- package/src/cli/commands/contacts.help.ts +173 -11
- package/src/cli/commands/contacts.ts +343 -4
- package/src/cli/commands/db/refresh.ts +1 -0
- package/src/cli/commands/oauth/index.help.ts +6 -3
- package/src/cli/commands/oauth/request.test.ts +37 -0
- package/src/cli/commands/oauth/request.ts +7 -6
- package/src/cli/lib/bundled-marketplace.json +1 -1
- package/src/cli/lib/install-from-github.ts +20 -19
- package/src/cli/lib/install-plugin-dependencies.ts +3 -5
- package/src/cli/lib/merge-plugin-tree.ts +1 -0
- package/src/cli/lib/publish-plugin.ts +4 -1
- package/src/config/__tests__/balanced-model-experiment.test.ts +63 -31
- package/src/config/__tests__/default-profile-catalog.test.ts +2 -2
- package/src/config/balanced-model-experiment.ts +15 -9
- package/src/config/bundled-skills/contacts/SKILL.md +61 -18
- package/src/config/default-profile-catalog.ts +12 -11
- package/src/config/feature-flag-registry.json +22 -13
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +13 -0
- package/src/config/schemas/live-voice.ts +7 -0
- package/src/config/schemas/sight.ts +45 -0
- package/src/config/skills.ts +2 -1
- package/src/contacts/__tests__/mirror-reconciler.test.ts +352 -0
- package/src/contacts/contact-store.ts +47 -4
- package/src/contacts/contacts-write.ts +5 -2
- package/src/contacts/mirror-reconciler.ts +210 -0
- package/src/contacts/mirror-tombstones.ts +42 -0
- package/src/contacts/notify-contacts-changed.ts +11 -2
- package/src/context/compactor.ts +47 -19
- package/src/context/tool-result-spool.ts +21 -14
- package/src/conversations/message-consolidation.ts +2 -2
- package/src/credential-health/credential-health-service.ts +37 -6
- package/src/daemon/conversation-agent-loop-handlers.ts +19 -2
- package/src/daemon/conversation-agent-loop.ts +100 -1
- package/src/daemon/conversation-media-retry.ts +11 -6
- package/src/daemon/conversation-messaging.ts +293 -30
- package/src/daemon/conversation-runtime-assembly.ts +67 -0
- package/src/daemon/conversation-sight-frames.ts +187 -0
- package/src/daemon/conversation-store.ts +18 -28
- package/src/daemon/conversation-surfaces.ts +12 -2
- package/src/daemon/conversation-tool-setup.ts +39 -10
- package/src/daemon/conversation.ts +159 -16
- package/src/daemon/daemon-control.ts +6 -17
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +10 -11
- package/src/daemon/install-assistant-command.ts +1 -0
- package/src/daemon/lifecycle.ts +10 -13
- package/src/daemon/message-types/sync.ts +4 -0
- package/src/daemon/process-message.ts +2 -17
- package/src/daemon/reaction-history-render.test.ts +162 -0
- package/src/daemon/reaction-history-render.ts +115 -0
- package/src/daemon/reaction-record.test.ts +162 -0
- package/src/daemon/reaction-record.ts +83 -0
- package/src/daemon/shutdown-handlers.ts +0 -2
- package/src/daemon/summarize-boundary.ts +12 -0
- package/src/daemon/video-thumbnail.ts +1 -1
- package/src/home/feed-types.ts +4 -0
- package/src/home/feed-writer.ts +25 -0
- package/src/live-voice/__tests__/live-voice-attach-frame.test.ts +679 -0
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +8 -5
- package/src/live-voice/__tests__/live-voice-photo.test.ts +637 -1
- package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +787 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +347 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +109 -0
- package/src/live-voice/live-voice-photo.ts +420 -65
- package/src/live-voice/live-voice-session.ts +311 -27
- package/src/live-voice/protocol.ts +147 -11
- package/src/messaging/provider-message-metadata.ts +7 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +113 -1
- package/src/messaging/providers/channel-transport.ts +37 -0
- package/src/messaging/providers/discord/api.ts +4 -4
- package/src/messaging/providers/discord/send.test.ts +177 -2
- package/src/messaging/providers/discord/send.ts +197 -10
- package/src/messaging/providers/discord/transport.ts +10 -0
- package/src/messaging/providers/index.ts +42 -0
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +129 -3
- package/src/messaging/providers/slack/__tests__/auth.test.ts +35 -0
- package/src/messaging/providers/slack/__tests__/message-content.test.ts +152 -0
- package/src/messaging/providers/slack/adapter.ts +112 -27
- package/src/messaging/providers/slack/api.test.ts +10 -0
- package/src/messaging/providers/slack/auth.ts +32 -6
- package/src/messaging/providers/slack/client.ts +0 -15
- package/src/messaging/providers/slack/message-content.ts +137 -0
- package/src/messaging/providers/slack/send.test.ts +43 -2
- package/src/messaging/providers/slack/send.ts +18 -5
- package/src/messaging/providers/slack/transport.ts +10 -0
- package/src/messaging/providers/slack/types.ts +21 -2
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -1
- package/src/messaging/providers/telegram-bot/send.test.ts +49 -2
- package/src/messaging/providers/telegram-bot/send.ts +41 -1
- package/src/messaging/providers/telegram-bot/transport.ts +10 -0
- package/src/messaging/providers/telegram-bot/withdraw.ts +13 -5
- package/src/messaging/reaction-envelopes.ts +79 -0
- package/src/monitoring/control.ts +2 -2
- package/src/monitoring/page-cache.ts +5 -1
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +360 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +8 -0
- package/src/notifications/adapters/discord.ts +62 -9
- package/src/notifications/conversation-pairing.ts +31 -4
- package/src/notifications/emit-signal.ts +8 -5
- package/src/notifications/guardian-delivery-recorder.ts +47 -6
- package/src/notifications/guardian-feed-projection.ts +492 -0
- package/src/notifications/home-feed-side-effect.ts +59 -11
- package/src/oauth/byo-connection.test.ts +22 -0
- package/src/oauth/byo-connection.ts +11 -5
- package/src/oauth/connection.ts +3 -2
- package/src/oauth/platform-connection.test.ts +35 -0
- package/src/oauth/platform-connection.ts +18 -13
- package/src/oauth/scope-utils.ts +48 -0
- package/src/oauth/seed-providers.ts +12 -0
- package/src/outbound-proxy/certs.ts +5 -4
- package/src/permissions/question-guardian-request.test.ts +7 -1
- package/src/persistence/attachments-store.ts +134 -31
- package/src/persistence/conversation-crud.ts +137 -4
- package/src/persistence/conversation-types.ts +109 -0
- package/src/persistence/db-connection.ts +3 -21
- package/src/persistence/delivery-crud.ts +42 -12
- package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +15 -2
- package/src/persistence/embeddings/embedding-local.ts +7 -112
- package/src/persistence/embeddings/qdrant-manager.ts +4 -1
- package/src/persistence/llm-request-log-store.ts +1 -1
- package/src/persistence/migrations/370-acp-session-history-auth-error-code.ts +36 -0
- package/src/persistence/migrations/371-acp-session-history-auth-error-credential.ts +40 -0
- package/src/persistence/migrations/372-create-acp-refused-credentials.ts +34 -0
- package/src/persistence/migrations/373-acp-auth-marker-index.ts +27 -0
- package/src/persistence/migrations/__tests__/370-acp-session-history-auth-error-code.test.ts +65 -0
- package/src/persistence/migrations/__tests__/371-acp-session-history-auth-error-credential.test.ts +68 -0
- package/src/persistence/migrations/__tests__/372-create-acp-refused-credentials.test.ts +64 -0
- package/src/persistence/migrations/__tests__/373-acp-auth-marker-index.test.ts +82 -0
- package/src/persistence/schema/acp.ts +25 -0
- package/src/persistence/steps.ts +8 -0
- package/src/platform/platform-patch-queue.test.ts +43 -41
- package/src/plugin-api/index.ts +12 -0
- package/src/plugins/defaults/compaction/window-manager.ts +2 -0
- package/src/plugins/defaults/image-recovery/recover.ts +12 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +166 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +3 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +3 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +53 -9
- package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +4 -7
- package/src/plugins/defaults/memory/src/memory-worker-routes.ts +1 -1
- package/src/plugins/defaults/memory/v1/job-handlers/index-maintenance.ts +15 -1
- package/src/plugins/defaults/memory/worker-control.ts +1 -2
- package/src/providers/content-block-schema.ts +1 -0
- package/src/providers/fireworks/client.ts +9 -1
- package/src/providers/gemini/client.ts +22 -15
- package/src/providers/media-resolve.ts +24 -10
- package/src/providers/model-catalog.ts +91 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -5
- package/src/providers/retry.ts +16 -11
- package/src/providers/types.ts +56 -0
- package/src/routes/__tests__/control.test.ts +4 -3
- package/src/routes/control.ts +1 -1
- package/src/routes/route-host-client.ts +1 -1
- package/src/runtime/AGENTS.md +30 -12
- package/src/runtime/__tests__/agent-wake.test.ts +71 -6
- package/src/runtime/__tests__/guardian-form-registry.test.ts +242 -0
- package/src/runtime/access-request-helper.ts +15 -7
- package/src/runtime/agent-wake.ts +16 -4
- package/src/runtime/channel-approval-types.ts +8 -5
- package/src/runtime/channel-invite-transport.ts +2 -0
- package/src/runtime/channel-invite-transports/discord.ts +27 -0
- package/src/runtime/channel-readiness-service.ts +4 -4
- package/src/runtime/channel-reply-delivery.ts +72 -28
- package/src/runtime/guardian-action-service.ts +9 -5
- package/src/runtime/guardian-form-registry.ts +256 -0
- package/src/runtime/guardian-reply-router.ts +33 -14
- package/src/runtime/invite-instruction-generator.ts +14 -2
- package/src/runtime/migrations/vbundle-metadata-merge.ts +4 -7
- package/src/runtime/no-response.ts +13 -74
- package/src/runtime/routes/__tests__/acp-routes.test.ts +527 -0
- package/src/runtime/routes/__tests__/app-management-shared-apps.test.ts +74 -0
- package/src/runtime/routes/__tests__/contact-form-kinds.test.ts +22 -0
- package/src/runtime/routes/__tests__/contact-record-prompt-route.test.ts +303 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +6 -0
- package/src/runtime/routes/__tests__/route-host-worker-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +5 -6
- package/src/runtime/routes/acp-routes-list.test.ts +58 -0
- package/src/runtime/routes/acp-routes.ts +253 -47
- package/src/runtime/routes/app-management-routes.ts +80 -47
- package/src/runtime/routes/approval-routes.ts +9 -0
- package/src/runtime/routes/archive-utils.ts +6 -1
- package/src/runtime/routes/avatar-routes.ts +1 -1
- package/src/runtime/routes/channel-route-shared.ts +18 -20
- package/src/runtime/routes/contact-prompt-routes.ts +271 -105
- package/src/runtime/routes/conversation-routes.ts +41 -32
- package/src/runtime/routes/credential-prompt-routes.ts +10 -1
- package/src/runtime/routes/debug-bash-routes.ts +1 -0
- package/src/runtime/routes/guardian-expiry-sweep.ts +89 -31
- package/src/runtime/routes/guardian-form-routes.ts +115 -0
- package/src/runtime/routes/inbound-message-handler.ts +82 -29
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +6 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +6 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +6 -1
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +50 -17
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +97 -94
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/channel.ts +39 -18
- package/src/runtime/routes/migration-routes.ts +1 -3
- package/src/runtime/routes/monitoring-routes.ts +0 -3
- package/src/runtime/routes/oauth-commands-routes.ts +18 -5
- package/src/runtime/routes/route-host-worker-routes.ts +1 -1
- package/src/runtime/routes/schedule-worker-routes.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +4 -0
- package/src/schedule/run-script.ts +1 -0
- package/src/schedule/scheduler.ts +1 -1
- package/src/schedule/worker-control.ts +2 -2
- package/src/security/oauth2.ts +2 -11
- package/src/security/secure-keys.ts +55 -8
- package/src/skills/catalog-install.ts +7 -4
- package/src/skills/clawhub.ts +1 -0
- package/src/skills/inline-command-runner.ts +1 -0
- package/src/stt/__tests__/room-noise-floor.test.ts +108 -0
- package/src/stt/room-noise-floor.ts +140 -0
- package/src/subagent/notify.ts +49 -3
- package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
- package/src/tools/acp/spawn.ts +2 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +9 -0
- package/src/tools/browser/browser-execution.ts +10 -43
- package/src/tools/browser/browser-manager.ts +1 -0
- package/src/tools/browser/browser-status-constants.ts +6 -5
- package/src/tools/browser/runtime-check.ts +2 -0
- package/src/tools/channel/react-to-message.test.ts +193 -0
- package/src/tools/channel/react-to-message.ts +159 -0
- package/src/tools/credentials/metadata-store.ts +115 -19
- package/src/tools/credentials/store.ts +0 -2
- package/src/tools/filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +11 -38
- package/src/tools/shared/filesystem/file-ops-service.ts +10 -7
- package/src/tools/shared/shell-output.test.ts +104 -0
- package/src/tools/shared/shell-output.ts +112 -39
- package/src/tools/skills/sandbox-runner.ts +1 -0
- package/src/tools/system/request-permission.test.ts +34 -0
- package/src/tools/system/request-permission.ts +29 -5
- package/src/tools/terminal/shell.test.ts +63 -0
- package/src/tools/terminal/shell.ts +12 -44
- package/src/tools/tool-input-schemas.ts +2 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/usage/attribution.ts +12 -4
- package/src/util/__tests__/worker-ownership.test.ts +77 -0
- package/src/util/__tests__/worker-process-command.test.ts +246 -1
- package/src/util/bun-runtime.ts +44 -13
- package/src/util/bundled-asset.ts +9 -1
- package/src/util/disk-usage.ts +1 -0
- package/src/util/file-use.ts +4 -1
- package/src/util/guardian-form-timeouts.ts +50 -0
- package/src/util/image-conversion.ts +1 -0
- package/src/util/oauth-request-body.test.ts +59 -0
- package/src/util/oauth-request-body.ts +56 -5
- package/src/util/platform.ts +69 -73
- package/src/util/process-table.ts +16 -5
- package/src/util/read-stdin.ts +5 -0
- package/src/util/spawn.ts +1 -0
- package/src/util/sqlite3-runtime.ts +25 -5
- package/src/util/worker-ownership.ts +193 -0
- package/src/util/worker-process.ts +375 -37
- package/src/workspace/git-service.ts +4 -12
- package/src/workspace/migrations/010-app-dir-rename.ts +1 -0
package/src/persistence/migrations/__tests__/371-acp-session-history-auth-error-credential.test.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration 371 adds the nullable `auth_error_credential` column that names
|
|
3
|
+
* which Claude token a run's credential failure was refused on, and is
|
|
4
|
+
* idempotent so a repair flow can re-run it.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Database } from "bun:sqlite";
|
|
8
|
+
import { describe, expect, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
11
|
+
|
|
12
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
13
|
+
import * as schema from "../../schema.js";
|
|
14
|
+
import { migrateAcpSessionHistoryAuthErrorCredential } from "../371-acp-session-history-auth-error-credential.js";
|
|
15
|
+
|
|
16
|
+
function createTestDb() {
|
|
17
|
+
const sqlite = new Database(":memory:");
|
|
18
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
19
|
+
sqlite.exec(
|
|
20
|
+
"CREATE TABLE acp_session_history (id TEXT PRIMARY KEY, status TEXT)",
|
|
21
|
+
);
|
|
22
|
+
return drizzle(sqlite, { schema });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function columnNames(db: ReturnType<typeof createTestDb>): string[] {
|
|
26
|
+
const rows = getSqliteFrom(db)
|
|
27
|
+
.prepare("PRAGMA table_info(acp_session_history)")
|
|
28
|
+
.all() as { name: string }[];
|
|
29
|
+
return rows.map((row) => row.name);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("migrateAcpSessionHistoryAuthErrorCredential", () => {
|
|
33
|
+
test("adds the column when it is missing", () => {
|
|
34
|
+
const db = createTestDb();
|
|
35
|
+
|
|
36
|
+
migrateAcpSessionHistoryAuthErrorCredential(db);
|
|
37
|
+
|
|
38
|
+
expect(columnNames(db)).toContain("auth_error_credential");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("re-running is a no-op", () => {
|
|
42
|
+
const db = createTestDb();
|
|
43
|
+
|
|
44
|
+
migrateAcpSessionHistoryAuthErrorCredential(db);
|
|
45
|
+
migrateAcpSessionHistoryAuthErrorCredential(db);
|
|
46
|
+
|
|
47
|
+
expect(columnNames(db)).toContain("auth_error_credential");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("leaves existing rows null, which reads as a marker worth serving", () => {
|
|
51
|
+
// A row marked before this column existed names no credential. The
|
|
52
|
+
// comparison treats that as no evidence the failure was repaired and
|
|
53
|
+
// serves the marker, so an upgrade cannot silently swallow a card.
|
|
54
|
+
const db = createTestDb();
|
|
55
|
+
getSqliteFrom(db).exec(
|
|
56
|
+
"INSERT INTO acp_session_history (id, status) VALUES ('run-1', 'failed')",
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
migrateAcpSessionHistoryAuthErrorCredential(db);
|
|
60
|
+
|
|
61
|
+
const row = getSqliteFrom(db)
|
|
62
|
+
.prepare(
|
|
63
|
+
"SELECT auth_error_credential FROM acp_session_history WHERE id = 'run-1'",
|
|
64
|
+
)
|
|
65
|
+
.get() as { auth_error_credential: string | null };
|
|
66
|
+
expect(row.auth_error_credential).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration 372 creates `acp_refused_credentials`, the record of which Claude
|
|
3
|
+
* tokens Claude has refused. It is separate from the session-history marker so
|
|
4
|
+
* that clearing session history cannot change which credential a spawn picks.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Database } from "bun:sqlite";
|
|
8
|
+
import { describe, expect, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
11
|
+
|
|
12
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
13
|
+
import * as schema from "../../schema.js";
|
|
14
|
+
import { migrateCreateAcpRefusedCredentials } from "../372-create-acp-refused-credentials.js";
|
|
15
|
+
|
|
16
|
+
function createTestDb() {
|
|
17
|
+
const sqlite = new Database(":memory:");
|
|
18
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
19
|
+
return drizzle(sqlite, { schema });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function tableNames(db: ReturnType<typeof createTestDb>): string[] {
|
|
23
|
+
const rows = getSqliteFrom(db)
|
|
24
|
+
.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")
|
|
25
|
+
.all() as { name: string }[];
|
|
26
|
+
return rows.map((row) => row.name);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("migrateCreateAcpRefusedCredentials", () => {
|
|
30
|
+
test("creates the table", () => {
|
|
31
|
+
const db = createTestDb();
|
|
32
|
+
|
|
33
|
+
migrateCreateAcpRefusedCredentials(db);
|
|
34
|
+
|
|
35
|
+
expect(tableNames(db)).toContain("acp_refused_credentials");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("re-running is a no-op", () => {
|
|
39
|
+
const db = createTestDb();
|
|
40
|
+
|
|
41
|
+
migrateCreateAcpRefusedCredentials(db);
|
|
42
|
+
migrateCreateAcpRefusedCredentials(db);
|
|
43
|
+
|
|
44
|
+
expect(tableNames(db)).toContain("acp_refused_credentials");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("keeps one row per digest, so a retried refusal is not an error", () => {
|
|
48
|
+
const db = createTestDb();
|
|
49
|
+
migrateCreateAcpRefusedCredentials(db);
|
|
50
|
+
const raw = getSqliteFrom(db);
|
|
51
|
+
|
|
52
|
+
raw.exec(
|
|
53
|
+
"INSERT OR IGNORE INTO acp_refused_credentials (digest, refused_at) VALUES ('abc', 1)",
|
|
54
|
+
);
|
|
55
|
+
raw.exec(
|
|
56
|
+
"INSERT OR IGNORE INTO acp_refused_credentials (digest, refused_at) VALUES ('abc', 2)",
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const rows = raw
|
|
60
|
+
.prepare("SELECT digest, refused_at FROM acp_refused_credentials")
|
|
61
|
+
.all() as { digest: string; refused_at: number }[];
|
|
62
|
+
expect(rows).toEqual([{ digest: "abc", refused_at: 1 }]);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration 373 indexes a conversation's credential-failure markers, which is
|
|
3
|
+
* the query every snapshot refetch runs.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Database } from "bun:sqlite";
|
|
7
|
+
import { describe, expect, test } from "bun:test";
|
|
8
|
+
|
|
9
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
10
|
+
|
|
11
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
12
|
+
import * as schema from "../../schema.js";
|
|
13
|
+
import { migrateAcpAuthMarkerIndex } from "../373-acp-auth-marker-index.js";
|
|
14
|
+
|
|
15
|
+
const MARKER_QUERY = `
|
|
16
|
+
SELECT agent_id FROM acp_session_history
|
|
17
|
+
WHERE parent_conversation_id = 'conv-1' AND auth_error_code IS NOT NULL
|
|
18
|
+
ORDER BY started_at DESC LIMIT 20
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
function createTestDb() {
|
|
22
|
+
const sqlite = new Database(":memory:");
|
|
23
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
24
|
+
sqlite.exec(
|
|
25
|
+
`CREATE TABLE acp_session_history (
|
|
26
|
+
id TEXT PRIMARY KEY,
|
|
27
|
+
agent_id TEXT,
|
|
28
|
+
parent_conversation_id TEXT,
|
|
29
|
+
started_at INTEGER,
|
|
30
|
+
auth_error_code TEXT
|
|
31
|
+
)`,
|
|
32
|
+
);
|
|
33
|
+
return drizzle(sqlite, { schema });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function indexNames(db: ReturnType<typeof createTestDb>): string[] {
|
|
37
|
+
const rows = getSqliteFrom(db)
|
|
38
|
+
.prepare("SELECT name FROM sqlite_master WHERE type = 'index'")
|
|
39
|
+
.all() as { name: string }[];
|
|
40
|
+
return rows.map((row) => row.name);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function queryPlan(db: ReturnType<typeof createTestDb>): string {
|
|
44
|
+
const rows = getSqliteFrom(db)
|
|
45
|
+
.prepare(`EXPLAIN QUERY PLAN ${MARKER_QUERY}`)
|
|
46
|
+
.all() as { detail: string }[];
|
|
47
|
+
return rows.map((row) => row.detail).join(" | ");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("migrateAcpAuthMarkerIndex", () => {
|
|
51
|
+
test("creates the index", () => {
|
|
52
|
+
const db = createTestDb();
|
|
53
|
+
|
|
54
|
+
migrateAcpAuthMarkerIndex(db);
|
|
55
|
+
|
|
56
|
+
expect(indexNames(db)).toContain("idx_acp_session_history_auth_marker");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("re-running is a no-op", () => {
|
|
60
|
+
const db = createTestDb();
|
|
61
|
+
|
|
62
|
+
migrateAcpAuthMarkerIndex(db);
|
|
63
|
+
migrateAcpAuthMarkerIndex(db);
|
|
64
|
+
|
|
65
|
+
expect(indexNames(db)).toContain("idx_acp_session_history_auth_marker");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("the marker lookup uses it, and stops sorting to answer", () => {
|
|
69
|
+
// The point of the index, not merely its existence. Without it the planner
|
|
70
|
+
// gathers the conversation's rows and sorts them, which is work that grows
|
|
71
|
+
// with how many runs it has had rather than with the few the answer needs.
|
|
72
|
+
const db = createTestDb();
|
|
73
|
+
const before = queryPlan(db);
|
|
74
|
+
|
|
75
|
+
migrateAcpAuthMarkerIndex(db);
|
|
76
|
+
|
|
77
|
+
const after = queryPlan(db);
|
|
78
|
+
expect(before).toContain("SCAN");
|
|
79
|
+
expect(after).toContain("idx_acp_session_history_auth_marker");
|
|
80
|
+
expect(after).not.toContain("TEMP B-TREE");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -33,6 +33,10 @@ export const acpSessionHistory = sqliteTable(
|
|
|
33
33
|
// Usage metadata. Null for rows written before these columns existed.
|
|
34
34
|
task: text("task"),
|
|
35
35
|
parentToolUseId: text("parent_tool_use_id"),
|
|
36
|
+
/** Credential failure that ended the run, when one did. Drives the inline
|
|
37
|
+
* Connect card on reopen; cleared when a replacement token is stored. */
|
|
38
|
+
authErrorCode: text("auth_error_code"),
|
|
39
|
+
authErrorCredential: text("auth_error_credential"),
|
|
36
40
|
usedTokens: integer("used_tokens"),
|
|
37
41
|
contextSize: integer("context_size"),
|
|
38
42
|
costAmount: real("cost_amount"),
|
|
@@ -47,5 +51,26 @@ export const acpSessionHistory = sqliteTable(
|
|
|
47
51
|
index("idx_acp_session_history_parent_conversation_id").on(
|
|
48
52
|
table.parentConversationId,
|
|
49
53
|
),
|
|
54
|
+
// Partial in the database (see migration 373): only rows carrying a
|
|
55
|
+
// credential failure are indexed, which is what keeps the marker lookup a
|
|
56
|
+
// fixed cost rather than one that grows with a conversation's run count.
|
|
57
|
+
// Declared here without the predicate because the schema builder has no
|
|
58
|
+
// way to express one; the migration is the source of truth for its shape.
|
|
59
|
+
index("idx_acp_session_history_auth_marker").on(
|
|
60
|
+
table.parentConversationId,
|
|
61
|
+
table.startedAt,
|
|
62
|
+
),
|
|
50
63
|
],
|
|
51
64
|
);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Claude tokens Claude has refused, by digest.
|
|
68
|
+
*
|
|
69
|
+
* Separate from the session-history marker on purpose: a marker is about
|
|
70
|
+
* showing a card for one run and is deleted with it, while this is about which
|
|
71
|
+
* credential a spawn may resolve and must survive the user clearing history.
|
|
72
|
+
*/
|
|
73
|
+
export const acpRefusedCredentials = sqliteTable("acp_refused_credentials", {
|
|
74
|
+
digest: text("digest").primaryKey(),
|
|
75
|
+
refusedAt: integer("refused_at").notNull(),
|
|
76
|
+
});
|
package/src/persistence/steps.ts
CHANGED
|
@@ -477,6 +477,10 @@ import { migrateChatgptSubscriptionRowIdentity } from "./migrations/366-chatgpt-
|
|
|
477
477
|
import { migrateCreateWatchTimelineEntries } from "./migrations/367-create-watch-timeline-entries.js";
|
|
478
478
|
import { migrateWatchTimelineScreenshotBlob } from "./migrations/368-watch-timeline-screenshot-blob.js";
|
|
479
479
|
import { migrateCreateAppPins } from "./migrations/369-create-app-pins.js";
|
|
480
|
+
import { migrateAcpSessionHistoryAuthErrorCode } from "./migrations/370-acp-session-history-auth-error-code.js";
|
|
481
|
+
import { migrateAcpSessionHistoryAuthErrorCredential } from "./migrations/371-acp-session-history-auth-error-credential.js";
|
|
482
|
+
import { migrateCreateAcpRefusedCredentials } from "./migrations/372-create-acp-refused-credentials.js";
|
|
483
|
+
import { migrateAcpAuthMarkerIndex } from "./migrations/373-acp-auth-marker-index.js";
|
|
480
484
|
import type { MigrationStep } from "./migrations/run-migrations.js";
|
|
481
485
|
|
|
482
486
|
export const migrationSteps: MigrationStep[] = [
|
|
@@ -1588,4 +1592,8 @@ export const migrationSteps: MigrationStep[] = [
|
|
|
1588
1592
|
migrateCreateWatchTimelineEntries,
|
|
1589
1593
|
migrateWatchTimelineScreenshotBlob,
|
|
1590
1594
|
migrateCreateAppPins,
|
|
1595
|
+
migrateAcpSessionHistoryAuthErrorCode,
|
|
1596
|
+
migrateAcpSessionHistoryAuthErrorCredential,
|
|
1597
|
+
migrateCreateAcpRefusedCredentials,
|
|
1598
|
+
migrateAcpAuthMarkerIndex,
|
|
1591
1599
|
];
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
beforeEach,
|
|
4
4
|
describe,
|
|
5
5
|
expect,
|
|
6
|
+
jest,
|
|
6
7
|
mock,
|
|
7
8
|
setSystemTime,
|
|
8
9
|
test,
|
|
@@ -66,14 +67,30 @@ function makeQueue(
|
|
|
66
67
|
return queue;
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
const FROZEN_NOW = new Date("2026-01-01T00:00:00.000Z").getTime();
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Flush already-queued microtasks. Fake timers stop the clock but not the
|
|
74
|
+
* promise chain `enqueue` appends onto, so the PATCH work a timer kicks off
|
|
75
|
+
* lands here.
|
|
76
|
+
*/
|
|
69
77
|
async function settle(): Promise<void> {
|
|
70
|
-
for (let i = 0; i <
|
|
71
|
-
await
|
|
78
|
+
for (let i = 0; i < 20; i += 1) {
|
|
79
|
+
await Promise.resolve();
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
|
|
83
|
+
/** Advance `Date.now()` and the queue's `setTimeout` handles together. */
|
|
84
|
+
async function elapse(ms: number): Promise<void> {
|
|
85
|
+
setSystemTime(new Date(Date.now() + ms));
|
|
86
|
+
jest.advanceTimersByTime(ms);
|
|
87
|
+
await settle();
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
describe("createPlatformPatchQueue", () => {
|
|
76
91
|
beforeEach(() => {
|
|
92
|
+
jest.useFakeTimers();
|
|
93
|
+
setSystemTime(new Date(FROZEN_NOW));
|
|
77
94
|
patches = [];
|
|
78
95
|
builds = 0;
|
|
79
96
|
respond = () => new Response("{}", { status: 200 });
|
|
@@ -85,6 +102,8 @@ describe("createPlatformPatchQueue", () => {
|
|
|
85
102
|
for (const queue of queues) {
|
|
86
103
|
queue.dispose();
|
|
87
104
|
}
|
|
105
|
+
jest.useRealTimers();
|
|
106
|
+
setSystemTime();
|
|
88
107
|
});
|
|
89
108
|
|
|
90
109
|
test("PATCHes once and dedups an unchanged key", async () => {
|
|
@@ -203,21 +222,16 @@ describe("createPlatformPatchQueue", () => {
|
|
|
203
222
|
saveSyncedKey: (synced) => saved.push(synced),
|
|
204
223
|
});
|
|
205
224
|
const start = Date.now();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
queue.enqueue("a");
|
|
217
|
-
await settle();
|
|
218
|
-
} finally {
|
|
219
|
-
setSystemTime();
|
|
220
|
-
}
|
|
225
|
+
queue.enqueue("a");
|
|
226
|
+
await settle();
|
|
227
|
+
setSystemTime(new Date(start + 999));
|
|
228
|
+
queue.enqueue("a");
|
|
229
|
+
await settle();
|
|
230
|
+
setSystemTime(new Date(start + 1000));
|
|
231
|
+
queue.enqueue("a");
|
|
232
|
+
await settle();
|
|
233
|
+
queue.enqueue("a");
|
|
234
|
+
await settle();
|
|
221
235
|
|
|
222
236
|
expect(patches).toHaveLength(2);
|
|
223
237
|
expect(saved.map((s) => s.syncedAt)).toEqual([start, start + 1000]);
|
|
@@ -243,14 +257,12 @@ describe("createPlatformPatchQueue", () => {
|
|
|
243
257
|
await settle();
|
|
244
258
|
expect(patches).toHaveLength(1);
|
|
245
259
|
|
|
246
|
-
await
|
|
247
|
-
await settle();
|
|
260
|
+
await elapse(300);
|
|
248
261
|
expect(patches).toHaveLength(2);
|
|
249
262
|
expect(builds).toBe(2);
|
|
250
263
|
|
|
251
264
|
queue.dispose();
|
|
252
|
-
await
|
|
253
|
-
await settle();
|
|
265
|
+
await elapse(300);
|
|
254
266
|
expect(patches).toHaveLength(2);
|
|
255
267
|
});
|
|
256
268
|
|
|
@@ -266,8 +278,7 @@ describe("createPlatformPatchQueue", () => {
|
|
|
266
278
|
await settle();
|
|
267
279
|
expect(patches).toHaveLength(0);
|
|
268
280
|
|
|
269
|
-
await
|
|
270
|
-
await settle();
|
|
281
|
+
await elapse(300);
|
|
271
282
|
expect(patches).toHaveLength(1);
|
|
272
283
|
});
|
|
273
284
|
|
|
@@ -279,12 +290,10 @@ describe("createPlatformPatchQueue", () => {
|
|
|
279
290
|
expect(patches).toHaveLength(1);
|
|
280
291
|
|
|
281
292
|
respond = () => new Response("{}", { status: 200 });
|
|
282
|
-
await
|
|
283
|
-
await settle();
|
|
293
|
+
await elapse(100);
|
|
284
294
|
expect(patches).toHaveLength(2);
|
|
285
295
|
|
|
286
|
-
await
|
|
287
|
-
await settle();
|
|
296
|
+
await elapse(100);
|
|
288
297
|
queue.enqueue("a");
|
|
289
298
|
await settle();
|
|
290
299
|
expect(patches).toHaveLength(2);
|
|
@@ -301,8 +310,7 @@ describe("createPlatformPatchQueue", () => {
|
|
|
301
310
|
queue.enqueue("a");
|
|
302
311
|
await settle();
|
|
303
312
|
mockClient = makeClient();
|
|
304
|
-
await
|
|
305
|
-
await settle();
|
|
313
|
+
await elapse(100);
|
|
306
314
|
|
|
307
315
|
expect(patches).toHaveLength(1);
|
|
308
316
|
});
|
|
@@ -312,14 +320,11 @@ describe("createPlatformPatchQueue", () => {
|
|
|
312
320
|
respond = () => new Response("nope", { status: 500 });
|
|
313
321
|
queue.enqueue("a");
|
|
314
322
|
await settle();
|
|
315
|
-
await
|
|
316
|
-
await
|
|
317
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
318
|
-
await settle();
|
|
323
|
+
await elapse(50);
|
|
324
|
+
await elapse(50);
|
|
319
325
|
expect(patches).toHaveLength(3);
|
|
320
326
|
|
|
321
|
-
await
|
|
322
|
-
await settle();
|
|
327
|
+
await elapse(100);
|
|
323
328
|
expect(patches).toHaveLength(3);
|
|
324
329
|
});
|
|
325
330
|
|
|
@@ -332,12 +337,10 @@ describe("createPlatformPatchQueue", () => {
|
|
|
332
337
|
await settle();
|
|
333
338
|
expect(patches.map((p) => p.body.value)).toEqual(["a", "b"]);
|
|
334
339
|
|
|
335
|
-
await
|
|
336
|
-
await settle();
|
|
340
|
+
await elapse(100);
|
|
337
341
|
expect(patches.map((p) => p.body.value)).toEqual(["a", "b", "b"]);
|
|
338
342
|
|
|
339
|
-
await
|
|
340
|
-
await settle();
|
|
343
|
+
await elapse(100);
|
|
341
344
|
expect(patches).toHaveLength(3);
|
|
342
345
|
});
|
|
343
346
|
|
|
@@ -347,8 +350,7 @@ describe("createPlatformPatchQueue", () => {
|
|
|
347
350
|
queue.enqueue("a");
|
|
348
351
|
await settle();
|
|
349
352
|
queue.dispose();
|
|
350
|
-
await
|
|
351
|
-
await settle();
|
|
353
|
+
await elapse(300);
|
|
352
354
|
|
|
353
355
|
expect(patches).toHaveLength(1);
|
|
354
356
|
});
|
package/src/plugin-api/index.ts
CHANGED
|
@@ -223,6 +223,18 @@ export { getConfiguredProvider } from "../providers/provider-send-message.js";
|
|
|
223
223
|
// an image, embedding it, re-encoding it — use this instead of reaching into
|
|
224
224
|
// the host attachment store, so they stay agnostic to how media is persisted.
|
|
225
225
|
export { resolveMediaSourceData } from "../providers/media-resolve.js";
|
|
226
|
+
// Build the `_attachmentId` property of a media block as a spreadable fragment,
|
|
227
|
+
// omitting it when there is no usable id. A plugin that rebuilds a media block
|
|
228
|
+
// (resizing an image, re-encoding a file) uses this to carry the block's link
|
|
229
|
+
// to its attachment row across the rebuild, so host consumers that correlate a
|
|
230
|
+
// block back to its attachment still can.
|
|
231
|
+
export { attachmentIdFragment } from "../providers/types.js";
|
|
232
|
+
// Read the attachment row a media block came from, whichever shape it is in: a
|
|
233
|
+
// reference names it on `source.attachmentId`, an inline block on
|
|
234
|
+
// `_attachmentId`. A plugin that rebuilds a block into inline bytes derives the
|
|
235
|
+
// id through this before stamping it, since the reference shape's id would
|
|
236
|
+
// otherwise be lost with the source it replaced.
|
|
237
|
+
export { mediaBlockAttachmentId } from "../providers/types.js";
|
|
226
238
|
// Classify a provider stop reason: whether the turn was truncated at the
|
|
227
239
|
// output token cap (vs. a natural stop or a tool call). A `post-model-call`
|
|
228
240
|
// hook reads it off `PostModelCallContext.stopReason` to decide whether to
|
|
@@ -83,6 +83,8 @@ export interface ContextWindowResult {
|
|
|
83
83
|
summaryOutputTokens: number;
|
|
84
84
|
summaryModel: string;
|
|
85
85
|
summaryCallSite?: LLMCallSite;
|
|
86
|
+
/** See {@link CompactionRunResult.summaryResolutionCallSite}. */
|
|
87
|
+
summaryResolutionCallSite?: LLMCallSite;
|
|
86
88
|
summaryOverrideProfile?: string | null;
|
|
87
89
|
/** See {@link CompactionRunResult.summaryActualProvider}. */
|
|
88
90
|
summaryActualProvider?: string;
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
|
+
attachmentIdFragment,
|
|
18
19
|
type ContentBlock,
|
|
20
|
+
mediaBlockAttachmentId,
|
|
19
21
|
type Message,
|
|
20
22
|
resolveMediaSourceData,
|
|
21
23
|
} from "@vellumai/plugin-api";
|
|
@@ -105,10 +107,13 @@ export async function unsendableImageReplacement(
|
|
|
105
107
|
if (mediaTypeMismatch) {
|
|
106
108
|
// Only the label is wrong, so keep the source shape: a workspace_ref
|
|
107
109
|
// stays a reference (inlining it would bake the full payload into the
|
|
108
|
-
// stored message row) and only media_type is corrected.
|
|
110
|
+
// stored message row) and only media_type is corrected. Because the
|
|
111
|
+
// source survives, a reference still names its own attachment through
|
|
112
|
+
// it, and only an inline block's top-level id needs carrying.
|
|
109
113
|
return {
|
|
110
114
|
type: "image",
|
|
111
115
|
source: { ...block.source, media_type: effectiveMediaType },
|
|
116
|
+
...attachmentIdFragment(block._attachmentId),
|
|
112
117
|
};
|
|
113
118
|
}
|
|
114
119
|
return null;
|
|
@@ -128,6 +133,12 @@ export async function unsendableImageReplacement(
|
|
|
128
133
|
media_type: optimized.mediaType,
|
|
129
134
|
data: optimized.data,
|
|
130
135
|
},
|
|
136
|
+
// The resized block is inline bytes whatever the input was, so a
|
|
137
|
+
// reference's `source.attachmentId` is about to be replaced along with
|
|
138
|
+
// the source that held it. Derive the id from either shape before
|
|
139
|
+
// stamping, or a reference-backed image loses its attachment here and
|
|
140
|
+
// the durable rewrite makes that permanent.
|
|
141
|
+
...attachmentIdFragment(mediaBlockAttachmentId(block)),
|
|
131
142
|
};
|
|
132
143
|
}
|
|
133
144
|
return { type: "text", text: UNSENDABLE_IMAGE_NOTE };
|