@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
|
@@ -39,6 +39,31 @@ mock.module("../../plugin-api/vision-support.js", () => ({
|
|
|
39
39
|
doesSupportVision: () => pinProfileSupportsVision,
|
|
40
40
|
}));
|
|
41
41
|
|
|
42
|
+
// Attachment hydration for the parked-camera-frame path. Only `att-frame-*`
|
|
43
|
+
// ids exist; anything else resolves to nothing, which is how an id the client
|
|
44
|
+
// invented reaches the bridge. `collectedAttachmentIds` is the store's other
|
|
45
|
+
// half: the fake `deleteMessageById` below writes into it under the real
|
|
46
|
+
// orphan rule, so an id the cleanup ate stops resolving here, exactly as a
|
|
47
|
+
// deleted attachment row would.
|
|
48
|
+
import * as realAttachmentsStore from "../../persistence/attachments-store.js";
|
|
49
|
+
|
|
50
|
+
const collectedAttachmentIds = new Set<string>();
|
|
51
|
+
|
|
52
|
+
mock.module("../../persistence/attachments-store.js", () => ({
|
|
53
|
+
...realAttachmentsStore,
|
|
54
|
+
resolveAttachmentsForPersist: (ids: string[]) =>
|
|
55
|
+
ids
|
|
56
|
+
.filter(
|
|
57
|
+
(id) => id.startsWith("att-frame-") && !collectedAttachmentIds.has(id),
|
|
58
|
+
)
|
|
59
|
+
.map((id) => ({
|
|
60
|
+
id,
|
|
61
|
+
filename: `${id}.png`,
|
|
62
|
+
mimeType: "image/png",
|
|
63
|
+
data: "ZnJhbWU=",
|
|
64
|
+
})),
|
|
65
|
+
}));
|
|
66
|
+
|
|
42
67
|
// Conversation-CRUD doubles for the teardown transcript-hygiene pass. The
|
|
43
68
|
// real module is spread so every other export keeps its production behavior;
|
|
44
69
|
// only the functions the hygiene pass (and discard) touch are recorded.
|
|
@@ -48,15 +73,22 @@ let getMessageByIdImpl: (
|
|
|
48
73
|
messageId: string,
|
|
49
74
|
conversationId?: string,
|
|
50
75
|
) => unknown = () => null;
|
|
76
|
+
// Attachment ids each persisted message links, so the fake delete below can
|
|
77
|
+
// apply the orphan rule to the same candidate set the real one would collect.
|
|
78
|
+
const messageAttachmentLinks = new Map<string, string[]>();
|
|
51
79
|
const crudLog: {
|
|
52
80
|
reads: string[];
|
|
53
81
|
updates: Array<{ messageId: string; content: string }>;
|
|
54
82
|
deletes: string[];
|
|
55
|
-
|
|
83
|
+
retained: Array<{ messageId: string; ids: readonly string[] }>;
|
|
84
|
+
} = { reads: [], updates: [], deletes: [], retained: [] };
|
|
56
85
|
function resetCrudLog(): void {
|
|
57
86
|
crudLog.reads.length = 0;
|
|
58
87
|
crudLog.updates.length = 0;
|
|
59
88
|
crudLog.deletes.length = 0;
|
|
89
|
+
crudLog.retained.length = 0;
|
|
90
|
+
messageAttachmentLinks.clear();
|
|
91
|
+
collectedAttachmentIds.clear();
|
|
60
92
|
getMessageByIdImpl = () => null;
|
|
61
93
|
}
|
|
62
94
|
|
|
@@ -69,8 +101,23 @@ mock.module("../../persistence/conversation-crud.js", () => ({
|
|
|
69
101
|
updateMessageContent: (messageId: string, content: string) => {
|
|
70
102
|
crudLog.updates.push({ messageId, content });
|
|
71
103
|
},
|
|
72
|
-
deleteMessageById: (
|
|
104
|
+
deleteMessageById: (
|
|
105
|
+
messageId: string,
|
|
106
|
+
options?: { retainAttachmentIds?: readonly string[] },
|
|
107
|
+
) => {
|
|
73
108
|
crudLog.deletes.push(messageId);
|
|
109
|
+
const retained = options?.retainAttachmentIds ?? [];
|
|
110
|
+
crudLog.retained.push({ messageId, ids: retained });
|
|
111
|
+
// Mirrors the real cleanup: the row's own attachment links are the
|
|
112
|
+
// candidate set, and a candidate nothing else references is collected
|
|
113
|
+
// unless the caller retained it. Every id here is referenced by this one
|
|
114
|
+
// row, which is the case the rollback path is about.
|
|
115
|
+
for (const id of messageAttachmentLinks.get(messageId) ?? []) {
|
|
116
|
+
if (!retained.includes(id)) {
|
|
117
|
+
collectedAttachmentIds.add(id);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
messageAttachmentLinks.delete(messageId);
|
|
74
121
|
return { segmentIds: [], deletedSummaryIds: [] };
|
|
75
122
|
},
|
|
76
123
|
// The echo path advances the snapshot anchor for a real-user turn; the
|
|
@@ -132,6 +179,7 @@ interface FakeConversation {
|
|
|
132
179
|
content: string;
|
|
133
180
|
requestId: string;
|
|
134
181
|
metadata?: Record<string, unknown>;
|
|
182
|
+
attachments?: Array<Record<string, unknown>>;
|
|
135
183
|
}) => Promise<{ id: string }>;
|
|
136
184
|
workingDir: string;
|
|
137
185
|
addEventObserver: (observer: unknown) => () => void;
|
|
@@ -167,7 +215,12 @@ function makeFakeConversation(opts: {
|
|
|
167
215
|
let clientCallback: ((msg: unknown) => Promise<void>) | undefined;
|
|
168
216
|
let persistCount = 0;
|
|
169
217
|
let lastPersistOpts:
|
|
170
|
-
| {
|
|
218
|
+
| {
|
|
219
|
+
content: string;
|
|
220
|
+
requestId: string;
|
|
221
|
+
metadata?: Record<string, unknown>;
|
|
222
|
+
attachments?: Array<Record<string, unknown>>;
|
|
223
|
+
}
|
|
171
224
|
| undefined;
|
|
172
225
|
const conversation: FakeConversation = {
|
|
173
226
|
conversationId: "conv-voice-bridge-test",
|
|
@@ -196,6 +249,14 @@ function makeFakeConversation(opts: {
|
|
|
196
249
|
persistUserMessage: async (persistOpts) => {
|
|
197
250
|
persistCount += 1;
|
|
198
251
|
lastPersistOpts = persistOpts;
|
|
252
|
+
// The link rows the real persist writes, which is what makes an
|
|
253
|
+
// attachment a cleanup candidate when this message is deleted.
|
|
254
|
+
if (persistOpts.attachments && persistOpts.attachments.length > 0) {
|
|
255
|
+
messageAttachmentLinks.set(
|
|
256
|
+
`msg-${persistCount}`,
|
|
257
|
+
persistOpts.attachments.map((a) => a.id as string),
|
|
258
|
+
);
|
|
259
|
+
}
|
|
199
260
|
// Recorded before `onPersist` so scripted persist FAILURES also
|
|
200
261
|
// appear in the event stream — ordering tests need the losing
|
|
201
262
|
// attempt visible.
|
|
@@ -496,6 +557,125 @@ describe("startVoiceTurn escalation-continuation persistence", () => {
|
|
|
496
557
|
});
|
|
497
558
|
});
|
|
498
559
|
|
|
560
|
+
describe("startVoiceTurn camera-frame attachments", () => {
|
|
561
|
+
test("hydrates parked ids onto the turn's own user message", async () => {
|
|
562
|
+
// The live-voice session parks an ambient camera frame and hands the id
|
|
563
|
+
// over; the row it lands on is the one carrying the user's words, so the
|
|
564
|
+
// picture and the sentence about it are one message.
|
|
565
|
+
const fake = makeFakeConversation({ processing: false });
|
|
566
|
+
fakeConversation = fake.conversation;
|
|
567
|
+
|
|
568
|
+
await startVoiceTurn({
|
|
569
|
+
...makeTurnOptions(),
|
|
570
|
+
content: "what is this",
|
|
571
|
+
attachments: ["att-frame-1"],
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
expect(fake.lastPersistOpts()?.attachments).toEqual([
|
|
575
|
+
{
|
|
576
|
+
id: "att-frame-1",
|
|
577
|
+
filename: "att-frame-1.png",
|
|
578
|
+
mimeType: "image/png",
|
|
579
|
+
data: "ZnJhbWU=",
|
|
580
|
+
},
|
|
581
|
+
]);
|
|
582
|
+
// `UserMessageAttachment` carries no metadata of its own, so which of a
|
|
583
|
+
// row's attachments were ambient frames is recorded on the message.
|
|
584
|
+
expect(fake.lastPersistOpts()?.metadata).toMatchObject({
|
|
585
|
+
voiceSessionTurn: true,
|
|
586
|
+
sightFrameAttachmentIds: ["att-frame-1"],
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
test("an id that resolves to nothing costs the caller no turn", async () => {
|
|
591
|
+
const fake = makeFakeConversation({ processing: false });
|
|
592
|
+
fakeConversation = fake.conversation;
|
|
593
|
+
|
|
594
|
+
await startVoiceTurn({
|
|
595
|
+
...makeTurnOptions(),
|
|
596
|
+
content: "what is this",
|
|
597
|
+
attachments: ["att-missing"],
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
expect(fake.persistCount()).toBe(1);
|
|
601
|
+
expect(fake.lastPersistOpts()).not.toHaveProperty("attachments");
|
|
602
|
+
expect(fake.lastPersistOpts()?.metadata).not.toHaveProperty(
|
|
603
|
+
"sightFrameAttachmentIds",
|
|
604
|
+
);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
test("a discarded turn leaves its camera frame attachable again", async () => {
|
|
608
|
+
// The hold verdict rolls the speculative turn back and the session parks
|
|
609
|
+
// the id again for the replay. The rollback deletes the row, and the row
|
|
610
|
+
// was the attachment's only reference, so without the retention the
|
|
611
|
+
// cleanup takes the frame with it and the replayed utterance speaks
|
|
612
|
+
// without the picture the user is holding up, with nothing said.
|
|
613
|
+
resetCrudLog();
|
|
614
|
+
const fake = makeFakeConversation({ processing: false });
|
|
615
|
+
fakeConversation = fake.conversation;
|
|
616
|
+
|
|
617
|
+
const handle = await startVoiceTurn({
|
|
618
|
+
...makeTurnOptions(),
|
|
619
|
+
content: "what is this",
|
|
620
|
+
attachments: ["att-frame-1"],
|
|
621
|
+
});
|
|
622
|
+
expect(fake.lastPersistOpts()?.attachments).toHaveLength(1);
|
|
623
|
+
|
|
624
|
+
await handle.discard?.();
|
|
625
|
+
|
|
626
|
+
// The replay: the same parked id, and the row it lands on still carries
|
|
627
|
+
// the frame.
|
|
628
|
+
await startVoiceTurn({
|
|
629
|
+
...makeTurnOptions(),
|
|
630
|
+
content: "what is this",
|
|
631
|
+
attachments: ["att-frame-1"],
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
expect(fake.lastPersistOpts()?.attachments).toEqual([
|
|
635
|
+
{
|
|
636
|
+
id: "att-frame-1",
|
|
637
|
+
filename: "att-frame-1.png",
|
|
638
|
+
mimeType: "image/png",
|
|
639
|
+
data: "ZnJhbWU=",
|
|
640
|
+
},
|
|
641
|
+
]);
|
|
642
|
+
expect(fake.lastPersistOpts()?.metadata).toMatchObject({
|
|
643
|
+
sightFrameAttachmentIds: ["att-frame-1"],
|
|
644
|
+
});
|
|
645
|
+
// The rollback asked for exactly the ids its own row linked.
|
|
646
|
+
expect(crudLog.retained).toEqual([
|
|
647
|
+
{ messageId: "msg-1", ids: ["att-frame-1"] },
|
|
648
|
+
]);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
test("a discard with no parked frame retains nothing", async () => {
|
|
652
|
+
resetCrudLog();
|
|
653
|
+
const fake = makeFakeConversation({ processing: false });
|
|
654
|
+
fakeConversation = fake.conversation;
|
|
655
|
+
|
|
656
|
+
const handle = await startVoiceTurn({
|
|
657
|
+
...makeTurnOptions(),
|
|
658
|
+
content: "what is this",
|
|
659
|
+
});
|
|
660
|
+
await handle.discard?.();
|
|
661
|
+
|
|
662
|
+
expect(crudLog.deletes).toEqual(["msg-1"]);
|
|
663
|
+
expect(crudLog.retained).toEqual([{ messageId: "msg-1", ids: [] }]);
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
test("a turn with no parked frame persists exactly as before", async () => {
|
|
667
|
+
const fake = makeFakeConversation({ processing: false });
|
|
668
|
+
fakeConversation = fake.conversation;
|
|
669
|
+
|
|
670
|
+
await startVoiceTurn({ ...makeTurnOptions(), content: "what is this" });
|
|
671
|
+
|
|
672
|
+
expect(fake.lastPersistOpts()).not.toHaveProperty("attachments");
|
|
673
|
+
expect(fake.lastPersistOpts()?.metadata).toEqual({
|
|
674
|
+
voiceSessionTurn: true,
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
499
679
|
describe("startVoiceTurn hiddenSyntheticPrompt", () => {
|
|
500
680
|
// A caller whose internal instruction is composed per call carries no
|
|
501
681
|
// sentinel for the content comparisons to recognize, so it declares itself.
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* channels (telegram, slack) get a direct channel reply.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { DELIVERY_STATUS } from "@vellumai/gateway-client";
|
|
10
|
+
|
|
9
11
|
import { addMessage } from "../persistence/conversation-crud.js";
|
|
10
12
|
import { deliverChannelReply } from "../runtime/gateway-client.js";
|
|
11
13
|
import { composeGuardianActionMessageGenerative } from "../runtime/guardian-action-message-composer.js";
|
|
@@ -35,7 +37,10 @@ export async function sendGuardianExpiryNotices(
|
|
|
35
37
|
assistantId: string,
|
|
36
38
|
): Promise<void> {
|
|
37
39
|
for (const delivery of deliveries) {
|
|
38
|
-
if (
|
|
40
|
+
if (
|
|
41
|
+
delivery.status !== DELIVERY_STATUS.sent &&
|
|
42
|
+
delivery.status !== DELIVERY_STATUS.pending
|
|
43
|
+
) {
|
|
39
44
|
continue;
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -28,6 +28,7 @@ import { CONVERSATION_BUSY_MESSAGE } from "../daemon/conversation-messaging.js";
|
|
|
28
28
|
import { resolveChannelCapabilities } from "../daemon/conversation-runtime-assembly.js";
|
|
29
29
|
import { getOrCreateConversation } from "../daemon/conversation-store.js";
|
|
30
30
|
import type { TrustContext } from "../daemon/trust-context-types.js";
|
|
31
|
+
import { resolveAttachmentsForPersist } from "../persistence/attachments-store.js";
|
|
31
32
|
import {
|
|
32
33
|
deleteMessageById,
|
|
33
34
|
getMessageById,
|
|
@@ -323,6 +324,20 @@ export interface VoiceTurnOptions {
|
|
|
323
324
|
};
|
|
324
325
|
/** Per-turn control prompt. Undefined uses the phone prompt; null disables it. */
|
|
325
326
|
voiceControlPrompt?: string | null;
|
|
327
|
+
/**
|
|
328
|
+
* Ids of already-uploaded attachments to hang on this turn's persisted user
|
|
329
|
+
* message, hydrated here into the shape the message stores.
|
|
330
|
+
*
|
|
331
|
+
* Filled by the live-voice session from the camera frame a client parked on
|
|
332
|
+
* it (`attach_frame`), which is why these are ambient rather than chosen:
|
|
333
|
+
* they ride the turn's own user message so the picture and the words about it
|
|
334
|
+
* are one message, and so the media survives a context-overflow retry, which
|
|
335
|
+
* preserves it only on the newest user message.
|
|
336
|
+
*
|
|
337
|
+
* Ids that resolve to nothing are dropped: an unresolvable frame must not
|
|
338
|
+
* cost the caller their turn.
|
|
339
|
+
*/
|
|
340
|
+
attachments?: readonly string[];
|
|
326
341
|
/** The transcribed caller utterance or synthetic marker. */
|
|
327
342
|
content: string;
|
|
328
343
|
/** Assistant scope for multi-assistant channels. */
|
|
@@ -1023,9 +1038,20 @@ export async function startVoiceTurn(
|
|
|
1023
1038
|
|
|
1024
1039
|
const requestId = uuidv7();
|
|
1025
1040
|
const turnId = crypto.randomUUID();
|
|
1041
|
+
// Ids this turn's row actually links, read back by `discardFn` so a rollback
|
|
1042
|
+
// does not take the attachments down with the row.
|
|
1043
|
+
let linkedAttachmentIds: string[] = [];
|
|
1026
1044
|
const persistTurnUserMessage = async (): Promise<string> => {
|
|
1045
|
+
// Resolved inside the persist, not once outside it, so the retry after a
|
|
1046
|
+
// lost lock race stores exactly what the first attempt would have.
|
|
1047
|
+
const turnAttachments =
|
|
1048
|
+
opts.attachments && opts.attachments.length > 0
|
|
1049
|
+
? resolveAttachmentsForPersist([...opts.attachments])
|
|
1050
|
+
: [];
|
|
1051
|
+
linkedAttachmentIds = turnAttachments.map((a) => a.id);
|
|
1027
1052
|
const persistResult = await conversation.persistUserMessage({
|
|
1028
1053
|
content: persistedContent,
|
|
1054
|
+
...(turnAttachments.length > 0 ? { attachments: turnAttachments } : {}),
|
|
1029
1055
|
requestId,
|
|
1030
1056
|
metadata: {
|
|
1031
1057
|
// Durable "this turn came from an open voice session" marker; see
|
|
@@ -1069,6 +1095,14 @@ export async function startVoiceTurn(
|
|
|
1069
1095
|
},
|
|
1070
1096
|
}
|
|
1071
1097
|
: {}),
|
|
1098
|
+
// Names which of the row's attachments arrived as ambient camera
|
|
1099
|
+
// frames rather than files the user picked, so retention can treat
|
|
1100
|
+
// them differently. `UserMessageAttachment` carries no metadata of its
|
|
1101
|
+
// own, so the marking lives on the message and refers to the
|
|
1102
|
+
// attachments by id.
|
|
1103
|
+
...(turnAttachments.length > 0
|
|
1104
|
+
? { sightFrameAttachmentIds: turnAttachments.map((a) => a.id) }
|
|
1105
|
+
: {}),
|
|
1072
1106
|
},
|
|
1073
1107
|
});
|
|
1074
1108
|
return persistResult.id;
|
|
@@ -1865,7 +1899,18 @@ export async function startVoiceTurn(
|
|
|
1865
1899
|
// Same rollback pattern as the pointer-turn runner: delete the row,
|
|
1866
1900
|
// then rebuild in-memory history from the clean DB (a plain pop is
|
|
1867
1901
|
// fragile against concurrent compaction reassigning the array).
|
|
1868
|
-
|
|
1902
|
+
//
|
|
1903
|
+
// The row's own attachments are exempted from the orphan cleanup. A
|
|
1904
|
+
// discard unwinds the turn as though it never ran, so an ambient camera
|
|
1905
|
+
// frame goes back to being uploaded-but-unsent, which is exactly what
|
|
1906
|
+
// the live-voice session's parked slot expects to find when the held
|
|
1907
|
+
// utterance is replayed. Collecting it here would delete the row and
|
|
1908
|
+
// the bytes, and the replay would silently speak without the picture.
|
|
1909
|
+
deleteMessageById(messageId, {
|
|
1910
|
+
...(linkedAttachmentIds.length > 0
|
|
1911
|
+
? { retainAttachmentIds: linkedAttachmentIds }
|
|
1912
|
+
: {}),
|
|
1913
|
+
});
|
|
1869
1914
|
await conversation.loadFromDb();
|
|
1870
1915
|
publishConversationMessagesChanged(opts.conversationId);
|
|
1871
1916
|
} catch (err) {
|
|
@@ -524,30 +524,30 @@ describe("expiry lifecycle", () => {
|
|
|
524
524
|
]);
|
|
525
525
|
});
|
|
526
526
|
|
|
527
|
-
test("
|
|
528
|
-
const
|
|
529
|
-
ipcResponse =
|
|
530
|
-
expect(
|
|
531
|
-
|
|
532
|
-
|
|
527
|
+
test("listExpiredPendingGuardianRequests passes the limit and returns pending rows", async () => {
|
|
528
|
+
const staleRow = makeWireRequest({ status: "pending" });
|
|
529
|
+
ipcResponse = [staleRow];
|
|
530
|
+
expect(await client.listExpiredPendingGuardianRequests(50)).toEqual([
|
|
531
|
+
staleRow,
|
|
532
|
+
]);
|
|
533
533
|
expect(ipcCalls).toEqual([
|
|
534
534
|
{
|
|
535
|
-
method: "
|
|
536
|
-
params: {
|
|
535
|
+
method: "guardian_requests_list_expired_pending",
|
|
536
|
+
params: { limit: 50 },
|
|
537
537
|
},
|
|
538
538
|
]);
|
|
539
539
|
});
|
|
540
540
|
|
|
541
|
-
test("
|
|
541
|
+
test("the expired-pending list throws on transport failure and malformed responses", async () => {
|
|
542
542
|
ipcError = new Error("gateway unavailable");
|
|
543
|
-
await expect(client.
|
|
543
|
+
await expect(client.listExpiredPendingGuardianRequests()).rejects.toThrow(
|
|
544
544
|
"gateway unavailable",
|
|
545
545
|
);
|
|
546
546
|
|
|
547
547
|
ipcError = null;
|
|
548
548
|
ipcResponse = { expired: 3 };
|
|
549
|
-
await expect(client.
|
|
550
|
-
"
|
|
549
|
+
await expect(client.listExpiredPendingGuardianRequests()).rejects.toThrow(
|
|
550
|
+
"guardian_requests_list_expired_pending",
|
|
551
551
|
);
|
|
552
552
|
});
|
|
553
553
|
});
|
|
@@ -631,11 +631,7 @@ describe("isGuardianRequestInScopeOrFalse", () => {
|
|
|
631
631
|
test("returns the gateway scope verdict", async () => {
|
|
632
632
|
ipcResponse = { inScope: true };
|
|
633
633
|
expect(
|
|
634
|
-
await client.isGuardianRequestInScopeOrFalse(
|
|
635
|
-
"req-1",
|
|
636
|
-
"conv-1",
|
|
637
|
-
"telegram",
|
|
638
|
-
),
|
|
634
|
+
await client.isGuardianRequestInScopeOrFalse("req-1", "conv-1"),
|
|
639
635
|
).toBe(true);
|
|
640
636
|
expect(ipcCalls).toEqual([
|
|
641
637
|
{
|
|
@@ -643,7 +639,6 @@ describe("isGuardianRequestInScopeOrFalse", () => {
|
|
|
643
639
|
params: {
|
|
644
640
|
requestId: "req-1",
|
|
645
641
|
conversationId: "conv-1",
|
|
646
|
-
channel: "telegram",
|
|
647
642
|
},
|
|
648
643
|
},
|
|
649
644
|
]);
|
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
type GuardianRequestWire,
|
|
43
43
|
type ListGuardianRequestsIpcParams,
|
|
44
44
|
type ListPendingGuardianRequestsByDestinationIpcParams,
|
|
45
|
-
SweepExpiredGuardianRequestsIpcResponseSchema,
|
|
46
45
|
type UpdateGuardianRequestDeliveryIpcParams,
|
|
47
46
|
} from "@vellumai/gateway-client";
|
|
48
47
|
import type { ZodType } from "zod";
|
|
@@ -158,7 +157,7 @@ export const getGuardianRequestByCodeOrNull = degradeOnFailure(
|
|
|
158
157
|
);
|
|
159
158
|
|
|
160
159
|
/** List guardian requests matching the filters. Throws on transport failure. */
|
|
161
|
-
async function listGuardianRequests(
|
|
160
|
+
export async function listGuardianRequests(
|
|
162
161
|
filters: ListGuardianRequestsIpcParams = {},
|
|
163
162
|
): Promise<GuardianRequestWire[]> {
|
|
164
163
|
return callGateway(
|
|
@@ -210,9 +209,10 @@ export async function expireGuardianRequest(id: string): Promise<void> {
|
|
|
210
209
|
|
|
211
210
|
/**
|
|
212
211
|
* Daemon-boot expiry: interaction-bound kinds die with the daemon's
|
|
213
|
-
* in-memory pendingInteractions map
|
|
214
|
-
*
|
|
215
|
-
*
|
|
212
|
+
* in-memory pendingInteractions map. Persistent kinds are never touched
|
|
213
|
+
* here, whatever their deadline; their expiry belongs to the sweep, whose
|
|
214
|
+
* per-request confirmation keeps the fan-out recoverable. Returns the
|
|
215
|
+
* expired count. Throws on any failure (fail-closed).
|
|
216
216
|
*/
|
|
217
217
|
export async function expireInteractionBoundGuardianRequests(): Promise<number> {
|
|
218
218
|
const response = await callGateway(
|
|
@@ -224,19 +224,21 @@ export async function expireInteractionBoundGuardianRequests(): Promise<number>
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
/**
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
227
|
+
* Bounded, read-only list of pending requests past their `expiresAt`,
|
|
228
|
+
* oldest deadline first. Rows stay pending (and so stay listed) until the
|
|
229
|
+
* sweep has run their expiry side effects and confirmed each with
|
|
230
|
+
* `expireGuardianRequest`, which is what makes the fan-out recoverable
|
|
231
|
+
* from state after a lost response or a crash. Throws on any failure
|
|
232
|
+
* (fail-closed).
|
|
230
233
|
*/
|
|
231
|
-
export async function
|
|
232
|
-
|
|
234
|
+
export async function listExpiredPendingGuardianRequests(
|
|
235
|
+
limit?: number,
|
|
233
236
|
): Promise<GuardianRequestWire[]> {
|
|
234
|
-
|
|
235
|
-
GUARDIAN_REQUESTS_IPC_METHODS.
|
|
236
|
-
{
|
|
237
|
-
|
|
237
|
+
return callGateway(
|
|
238
|
+
GUARDIAN_REQUESTS_IPC_METHODS.listExpiredPending,
|
|
239
|
+
{ limit },
|
|
240
|
+
GuardianRequestListIpcResponseSchema,
|
|
238
241
|
);
|
|
239
|
-
return response.expired;
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
/**
|
|
@@ -281,6 +283,24 @@ export const listGuardianRequestDeliveriesOrEmpty = degradeOnFailure(
|
|
|
281
283
|
[],
|
|
282
284
|
);
|
|
283
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Every delivery row addressed to one channel-native chat, across all
|
|
288
|
+
* requests. Transcript importers use the recorded message ids to
|
|
289
|
+
* recognize guardian card messages as delivery projections. Throws on
|
|
290
|
+
* transport failure so callers can fail safe instead of importing a
|
|
291
|
+
* card as conversation content.
|
|
292
|
+
*/
|
|
293
|
+
export async function listGuardianRequestDeliveriesByChat(
|
|
294
|
+
channel: string,
|
|
295
|
+
chatId: string,
|
|
296
|
+
): Promise<GuardianRequestDeliveryWire[]> {
|
|
297
|
+
return callGateway(
|
|
298
|
+
GUARDIAN_REQUESTS_IPC_METHODS.listDeliveriesByChat,
|
|
299
|
+
{ channel, chatId },
|
|
300
|
+
GuardianRequestDeliveryListIpcResponseSchema,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
284
304
|
/**
|
|
285
305
|
* Reaction routing: the pending request whose delivered card is the
|
|
286
306
|
* reacted-to message. Throws on transport failure.
|
|
@@ -347,17 +367,16 @@ export const listPendingRequestsByScopeOrEmpty = degradeOnFailure(
|
|
|
347
367
|
|
|
348
368
|
/**
|
|
349
369
|
* Is a decision from this conversation allowed for the request (source
|
|
350
|
-
* match, or delivery match
|
|
370
|
+
* match, or delivery match on any channel's paired conversation)? Throws on
|
|
351
371
|
* transport failure.
|
|
352
372
|
*/
|
|
353
373
|
async function isGuardianRequestInScope(
|
|
354
374
|
requestId: string,
|
|
355
375
|
conversationId: string,
|
|
356
|
-
channel?: string,
|
|
357
376
|
): Promise<boolean> {
|
|
358
377
|
const response = await callGateway(
|
|
359
378
|
GUARDIAN_REQUESTS_IPC_METHODS.inScope,
|
|
360
|
-
{ requestId, conversationId
|
|
379
|
+
{ requestId, conversationId },
|
|
361
380
|
GuardianRequestInScopeIpcResponseSchema,
|
|
362
381
|
);
|
|
363
382
|
return response.inScope;
|