@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
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parking rail behind every guardian form.
|
|
3
|
+
*
|
|
4
|
+
* A form-backed command does not write. It parks here, its form goes to every
|
|
5
|
+
* connected client, and the write happens elsewhere (today the gateway, which
|
|
6
|
+
* owns contact writes) and reports back. With no human at a form, nothing is
|
|
7
|
+
* written and the call times out. That property is what makes these commands
|
|
8
|
+
* guardian-gated, and it lives in this file rather than in any one form.
|
|
9
|
+
*
|
|
10
|
+
* What a form owner supplies: how to broadcast it, how to take it down, and
|
|
11
|
+
* what its result looks like. Everything below is the same for all of them.
|
|
12
|
+
*
|
|
13
|
+
* Lifecycle:
|
|
14
|
+
* open -> the call parks, the form goes out, a deadline starts
|
|
15
|
+
* claim -> one submission wins; the answer deadline is swapped for a
|
|
16
|
+
* bounded settle window while the write runs
|
|
17
|
+
* resolve-> the write reports back, the parked call returns, the card is
|
|
18
|
+
* retired everywhere it is showing
|
|
19
|
+
*
|
|
20
|
+
* See `docs/guardian-forms.md` for how to add one.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { v4 as uuid } from "uuid";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
GUARDIAN_FORM_DEFAULT_TIMEOUT_MS,
|
|
27
|
+
GUARDIAN_FORM_SETTLE_MS,
|
|
28
|
+
} from "../util/guardian-form-timeouts.js";
|
|
29
|
+
import { getLogger } from "../util/logger.js";
|
|
30
|
+
|
|
31
|
+
const log = getLogger("guardian-form-registry");
|
|
32
|
+
|
|
33
|
+
/** Every form's result carries at least this; owners widen it. */
|
|
34
|
+
export interface GuardianFormResult {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type GuardianFormClosedReason = "answered" | "cancelled" | "timed_out";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* How a form reaches clients and how it leaves them.
|
|
43
|
+
*
|
|
44
|
+
* Both are the owner's, because the event names are wire contract. The
|
|
45
|
+
* registry decides *when* they fire.
|
|
46
|
+
*/
|
|
47
|
+
export interface GuardianFormBroadcast {
|
|
48
|
+
open: (requestId: string) => void;
|
|
49
|
+
closed: (requestId: string, reason: GuardianFormClosedReason) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface PendingGuardianForm {
|
|
53
|
+
kind: string;
|
|
54
|
+
resolve: (result: GuardianFormResult) => void;
|
|
55
|
+
timer: ReturnType<typeof setTimeout>;
|
|
56
|
+
closed: GuardianFormBroadcast["closed"];
|
|
57
|
+
/**
|
|
58
|
+
* Owner-defined state the write may need to read back out of band, for a
|
|
59
|
+
* client with no field to echo it in. Opaque here.
|
|
60
|
+
*/
|
|
61
|
+
meta: Record<string, unknown>;
|
|
62
|
+
logContext: Record<string, unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Set once a submission has been accepted. The form went to every connected
|
|
65
|
+
* client, so more than one can answer it; the first claim wins and the rest
|
|
66
|
+
* write nothing.
|
|
67
|
+
*/
|
|
68
|
+
claimed: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const pendingForms = new Map<string, PendingGuardianForm>();
|
|
72
|
+
|
|
73
|
+
export interface OpenGuardianFormOptions {
|
|
74
|
+
/** Which form this is, for logs and for scoping the open-form check. */
|
|
75
|
+
kind: string;
|
|
76
|
+
broadcast: GuardianFormBroadcast;
|
|
77
|
+
/** How long to hold it open. Defaults to the shared 5 minutes. */
|
|
78
|
+
timeoutMs?: number;
|
|
79
|
+
meta?: Record<string, unknown>;
|
|
80
|
+
/** Form-specific fields folded into this form's log lines. */
|
|
81
|
+
logContext?: Record<string, unknown>;
|
|
82
|
+
/** What the parked call is told when nobody answers. */
|
|
83
|
+
timeoutError?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Park a call until the guardian answers its form.
|
|
88
|
+
*
|
|
89
|
+
* The returned promise settles exactly once: on an answer, on a dismissal, or
|
|
90
|
+
* on the deadline. It never rejects, because the caller's failure is a result
|
|
91
|
+
* it has to report, not an exception to unwind through.
|
|
92
|
+
*/
|
|
93
|
+
export function openGuardianForm<TResult extends GuardianFormResult>(
|
|
94
|
+
options: OpenGuardianFormOptions,
|
|
95
|
+
): Promise<TResult> {
|
|
96
|
+
const {
|
|
97
|
+
kind,
|
|
98
|
+
broadcast,
|
|
99
|
+
timeoutMs,
|
|
100
|
+
meta = {},
|
|
101
|
+
logContext = {},
|
|
102
|
+
timeoutError = "Prompt timed out",
|
|
103
|
+
} = options;
|
|
104
|
+
const requestId = uuid();
|
|
105
|
+
|
|
106
|
+
return new Promise<TResult>((resolve) => {
|
|
107
|
+
const timer = setTimeout(() => {
|
|
108
|
+
const pending = pendingForms.get(requestId);
|
|
109
|
+
if (!pending) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
log.warn({ requestId, kind, ...logContext }, "Guardian form timed out");
|
|
113
|
+
expireForm(requestId, pending, timeoutError);
|
|
114
|
+
}, timeoutMs ?? GUARDIAN_FORM_DEFAULT_TIMEOUT_MS);
|
|
115
|
+
|
|
116
|
+
pendingForms.set(requestId, {
|
|
117
|
+
kind,
|
|
118
|
+
// The registry knows only the base shape; the owner's extra fields ride
|
|
119
|
+
// through resolveGuardianForm untouched, so the cast is confined here.
|
|
120
|
+
resolve: resolve as (result: GuardianFormResult) => void,
|
|
121
|
+
timer,
|
|
122
|
+
closed: broadcast.closed,
|
|
123
|
+
meta,
|
|
124
|
+
logContext,
|
|
125
|
+
claimed: false,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
broadcast.open(requestId);
|
|
129
|
+
log.info({ requestId, kind, ...logContext }, "Guardian form broadcast");
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* End a form nobody answered in time, and tell the clients showing it.
|
|
135
|
+
*
|
|
136
|
+
* Without the broadcast the card stays up offering an answer that would now be
|
|
137
|
+
* refused: a form that has closed accepts no submission.
|
|
138
|
+
*/
|
|
139
|
+
function expireForm(
|
|
140
|
+
requestId: string,
|
|
141
|
+
pending: PendingGuardianForm,
|
|
142
|
+
error: string,
|
|
143
|
+
): void {
|
|
144
|
+
pendingForms.delete(requestId);
|
|
145
|
+
pending.resolve({ ok: false, error });
|
|
146
|
+
pending.closed(requestId, "timed_out");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Claim a pending form so exactly one submission can write.
|
|
151
|
+
*
|
|
152
|
+
* The daemon holds the only record of which forms are still open, so it is the
|
|
153
|
+
* one place that can decide a race between two clients answering the same
|
|
154
|
+
* broadcast, and the only one that can tell whether an id is even the right
|
|
155
|
+
* form. Pass `expectedKind` whenever the caller knows which form it is
|
|
156
|
+
* writing for. First caller wins; the rest are told why they lost, so the writer
|
|
157
|
+
* can tell "somebody already answered this" (leave their answer alone) apart
|
|
158
|
+
* from "no such form" (expired or already resolved, so nothing should be
|
|
159
|
+
* written at all).
|
|
160
|
+
*/
|
|
161
|
+
export function claimGuardianForm(
|
|
162
|
+
requestId: string,
|
|
163
|
+
expectedKind?: string,
|
|
164
|
+
): {
|
|
165
|
+
claimed: boolean;
|
|
166
|
+
reason?: "already_claimed" | "unknown" | "wrong_kind";
|
|
167
|
+
settleMs?: number;
|
|
168
|
+
} {
|
|
169
|
+
const pending = pendingForms.get(requestId);
|
|
170
|
+
if (!pending) {
|
|
171
|
+
return { claimed: false, reason: "unknown" };
|
|
172
|
+
}
|
|
173
|
+
if (expectedKind !== undefined && pending.kind !== expectedKind) {
|
|
174
|
+
// An id alone does not say which form it belongs to. Without this, a
|
|
175
|
+
// submission to form B's endpoint could claim an open form A: B's write
|
|
176
|
+
// would run, and A's parked caller would be handed B's result for a form
|
|
177
|
+
// it never showed.
|
|
178
|
+
log.warn(
|
|
179
|
+
{ requestId, expectedKind, actualKind: pending.kind },
|
|
180
|
+
"Guardian form claim rejected: the id belongs to a different form",
|
|
181
|
+
);
|
|
182
|
+
return { claimed: false, reason: "wrong_kind" };
|
|
183
|
+
}
|
|
184
|
+
if (pending.claimed) {
|
|
185
|
+
return { claimed: false, reason: "already_claimed" };
|
|
186
|
+
}
|
|
187
|
+
pending.claimed = true;
|
|
188
|
+
// Answered, so its open-for-answers deadline gives way to a bounded settle
|
|
189
|
+
// window while the write reports back.
|
|
190
|
+
clearTimeout(pending.timer);
|
|
191
|
+
pending.timer = setTimeout(() => {
|
|
192
|
+
log.warn(
|
|
193
|
+
{ requestId, kind: pending.kind, ...pending.logContext },
|
|
194
|
+
"Guardian form claimed but never settled; the write never reported back",
|
|
195
|
+
);
|
|
196
|
+
expireForm(requestId, pending, "The submitted form never completed");
|
|
197
|
+
}, GUARDIAN_FORM_SETTLE_MS);
|
|
198
|
+
// The claimer needs the window too: it is how long its write has to report
|
|
199
|
+
// back before the caller gives up, and it should not have to know the number
|
|
200
|
+
// independently.
|
|
201
|
+
return { claimed: true, settleMs: GUARDIAN_FORM_SETTLE_MS };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Hand a form's answer to the call parked on it and retire the card.
|
|
206
|
+
*
|
|
207
|
+
* An error covers a dismissal and a failed write alike: the form is over
|
|
208
|
+
* either way, and the caller is the one told why.
|
|
209
|
+
*/
|
|
210
|
+
export function resolveGuardianForm<TResult extends GuardianFormResult>(
|
|
211
|
+
requestId: string,
|
|
212
|
+
result: TResult,
|
|
213
|
+
): { resolved: boolean } {
|
|
214
|
+
const pending = pendingForms.get(requestId);
|
|
215
|
+
if (!pending) {
|
|
216
|
+
log.warn({ requestId }, "resolve: no pending guardian form found");
|
|
217
|
+
return { resolved: false };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
clearTimeout(pending.timer);
|
|
221
|
+
pendingForms.delete(requestId);
|
|
222
|
+
pending.resolve(result);
|
|
223
|
+
pending.closed(requestId, result.ok ? "answered" : "cancelled");
|
|
224
|
+
|
|
225
|
+
log.info(
|
|
226
|
+
{ requestId, kind: pending.kind, ...pending.logContext },
|
|
227
|
+
"Guardian form resolved",
|
|
228
|
+
);
|
|
229
|
+
return { resolved: true };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Whether any form of these kinds is open and still unanswered.
|
|
234
|
+
*
|
|
235
|
+
* Takes the kinds to check rather than sweeping the whole registry. Clients
|
|
236
|
+
* hold one card per kind, so a second broadcast of a kind already on screen
|
|
237
|
+
* replaces the first and leaves its command waiting on a form nobody can
|
|
238
|
+
* answer. A registry-wide check would go further than that and let any pending
|
|
239
|
+
* form block every other one, which is a rule each form should opt into rather
|
|
240
|
+
* than inherit.
|
|
241
|
+
*/
|
|
242
|
+
export function hasUnclaimedGuardianForm(kinds: readonly string[]): boolean {
|
|
243
|
+
for (const pending of pendingForms.values()) {
|
|
244
|
+
if (!pending.claimed && kinds.includes(pending.kind)) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Owner-defined state parked with a form, for a writer that must read it back. */
|
|
252
|
+
export function getGuardianFormMeta(
|
|
253
|
+
requestId: string,
|
|
254
|
+
): Record<string, unknown> | undefined {
|
|
255
|
+
return pendingForms.get(requestId)?.meta;
|
|
256
|
+
}
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* to allow for incremental migration and independent testability.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
import type { InboundReactionPayload } from "@vellumai/gateway-client";
|
|
26
27
|
import { isGuardianRequestExpired } from "@vellumai/gateway-client";
|
|
27
28
|
|
|
28
29
|
import {
|
|
@@ -61,7 +62,7 @@ import type {
|
|
|
61
62
|
ApprovalConversationGenerator,
|
|
62
63
|
} from "./http-types.js";
|
|
63
64
|
import * as pendingInteractions from "./pending-interactions.js";
|
|
64
|
-
import {
|
|
65
|
+
import { reactionDecisionForEmoji } from "./routes/channel-route-shared.js";
|
|
65
66
|
|
|
66
67
|
const log = getLogger("guardian-reply-router");
|
|
67
68
|
|
|
@@ -106,7 +107,12 @@ export interface GuardianReplyContext {
|
|
|
106
107
|
/** Callback data from button presses (e.g. `apr:<requestId>:<action>`). */
|
|
107
108
|
callbackData?: string;
|
|
108
109
|
/**
|
|
109
|
-
*
|
|
110
|
+
* Structured reaction payload when the inbound event is a reaction; the
|
|
111
|
+
* reaction branch keys on it and reads nothing off callbackData.
|
|
112
|
+
*/
|
|
113
|
+
reaction?: InboundReactionPayload;
|
|
114
|
+
/**
|
|
115
|
+
* For emoji-reaction decisions: the
|
|
110
116
|
* channel-native id (e.g. Slack `ts`) of the message the reaction was
|
|
111
117
|
* attached to. Used to recover the target request from its delivery record.
|
|
112
118
|
*/
|
|
@@ -368,23 +374,36 @@ export async function routeGuardianReply(
|
|
|
368
374
|
// even when several cards are pending in the same chat, so — unlike the
|
|
369
375
|
// text/NL paths — no clarification prompt is ever needed. `reaction_removed`
|
|
370
376
|
// never expresses intent and is filtered out before reaching the router.
|
|
371
|
-
if (
|
|
372
|
-
|
|
373
|
-
!callbackData.startsWith("reaction_removed:")
|
|
374
|
-
) {
|
|
375
|
-
const reaction = parseReactionCallbackData(callbackData);
|
|
377
|
+
if (ctx.reaction?.op === "added") {
|
|
378
|
+
const decision = reactionDecisionForEmoji(ctx.reaction.emoji);
|
|
376
379
|
const guardianChatId = channelDeliveryContext?.guardianChatId;
|
|
377
|
-
if (!
|
|
380
|
+
if (!decision || !reactedMessageTs || !guardianChatId) {
|
|
378
381
|
// Unknown emoji, or missing addressing context — not an actionable
|
|
379
382
|
// approval reaction. Leave it for the caller to persist as a transcript
|
|
380
383
|
// signal (it must not trigger an agent turn).
|
|
381
384
|
return notConsumed();
|
|
382
385
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
)
|
|
386
|
+
// A delivery row keys its card by the destination the adapter delivered
|
|
387
|
+
// to, and channels address that destination in one of two modalities: a
|
|
388
|
+
// chat (the conversation the reaction arrives from) or a person (the
|
|
389
|
+
// guardian's own user id, which person-addressed adapters store so a
|
|
390
|
+
// stale room id can never become a delivery target). The reactor on an
|
|
391
|
+
// approval card is the person it was delivered to, so their id is the
|
|
392
|
+
// second probe; identity validation in applyGuardianDecision still
|
|
393
|
+
// guards the decision itself.
|
|
394
|
+
const request =
|
|
395
|
+
(await getPendingRequestByDestinationMessageOrNull(
|
|
396
|
+
channel,
|
|
397
|
+
guardianChatId,
|
|
398
|
+
reactedMessageTs,
|
|
399
|
+
)) ??
|
|
400
|
+
(actor.actorExternalUserId
|
|
401
|
+
? await getPendingRequestByDestinationMessageOrNull(
|
|
402
|
+
channel,
|
|
403
|
+
actor.actorExternalUserId,
|
|
404
|
+
reactedMessageTs,
|
|
405
|
+
)
|
|
406
|
+
: null);
|
|
388
407
|
if (!request) {
|
|
389
408
|
// The reacted message is not a known pending approval card (a stray
|
|
390
409
|
// reaction, or one whose request was already resolved). Never approve
|
|
@@ -393,7 +412,7 @@ export async function routeGuardianReply(
|
|
|
393
412
|
}
|
|
394
413
|
return applyDecision(
|
|
395
414
|
request.id,
|
|
396
|
-
|
|
415
|
+
decision.action,
|
|
397
416
|
actor,
|
|
398
417
|
undefined,
|
|
399
418
|
channelDeliveryContext,
|
|
@@ -66,9 +66,16 @@ export async function generateInviteInstruction(params: {
|
|
|
66
66
|
const handle = params.channelHandle
|
|
67
67
|
? ` at ${params.channelHandle}`
|
|
68
68
|
: ` on ${channelLabel}`;
|
|
69
|
+
// Discord DMs only open between accounts that share a server, and a
|
|
70
|
+
// member can still have server-member DMs turned off, so the instruction
|
|
71
|
+
// names both or the invitee dead-ends before the code.
|
|
72
|
+
const discordCaveat =
|
|
73
|
+
params.channelType === "discord"
|
|
74
|
+
? " They need to be in a server with me, with DMs from server members enabled, before they can DM me."
|
|
75
|
+
: "";
|
|
69
76
|
const fallback = params.shareUrl
|
|
70
|
-
? `Send ${contact} this link: ${params.shareUrl}
|
|
71
|
-
: `Tell ${contact} to message me${handle} with the code below
|
|
77
|
+
? `Send ${contact} this link: ${params.shareUrl}, or tell them to message me${handle} with the code below.${discordCaveat}`
|
|
78
|
+
: `Tell ${contact} to message me${handle} with the code below.${discordCaveat}`;
|
|
72
79
|
|
|
73
80
|
const resolved = await resolveConfiguredProvider(
|
|
74
81
|
"inviteInstructionGenerator",
|
|
@@ -101,6 +108,11 @@ export async function generateInviteInstruction(params: {
|
|
|
101
108
|
"No share link is available for this channel. Do NOT mention sharing a link or URL.",
|
|
102
109
|
);
|
|
103
110
|
}
|
|
111
|
+
if (params.channelType === "discord") {
|
|
112
|
+
parts.push(
|
|
113
|
+
"Platform constraint: the contact must share a Discord server with the bot, and have DMs from server members enabled in their privacy settings, before they can send it a direct message. Mention this.",
|
|
114
|
+
);
|
|
115
|
+
}
|
|
104
116
|
parts.push(
|
|
105
117
|
"",
|
|
106
118
|
"Requirements:",
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Merge helper for `data/credentials/metadata.json` on bundle import.
|
|
3
3
|
*
|
|
4
|
-
* The credential metadata file
|
|
5
|
-
*
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
8
|
-
* `assistant_api_key`, `platform_assistant_id`, `webhook_secret`) to be
|
|
9
|
-
* present in metadata before the gateway will even look up their values in
|
|
10
|
-
* CES.
|
|
4
|
+
* The leftover workspace credential metadata file may remain on disk after
|
|
5
|
+
* CES import. CES owns identity and policy records. Bundle import still
|
|
6
|
+
* merges `vellum:*` catalog rows so platform identity survives a
|
|
7
|
+
* local-to-platform teleport.
|
|
11
8
|
*
|
|
12
9
|
* On a local→platform teleport, the bundle carries the SOURCE's metadata
|
|
13
10
|
* (no `vellum:*` entries, since the source is local), and a naive overwrite
|
|
@@ -1,75 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/** Matches every sentinel occurrence for stripping it out of mixed content. */
|
|
16
|
-
export const NO_RESPONSE_INLINE_RE = /<no_response\s*\/?>/gi;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Detection variant without the `g` flag: a `g`-flagged regex is stateful
|
|
20
|
-
* under `.test()` (it resumes from `lastIndex`), so reusing
|
|
21
|
-
* {@link NO_RESPONSE_INLINE_RE} for detection would alternate between
|
|
22
|
-
* matches and misses across calls.
|
|
23
|
-
*/
|
|
24
|
-
const NO_RESPONSE_MARKER_RE = new RegExp(NO_RESPONSE_INLINE_RE.source, "i");
|
|
25
|
-
|
|
26
|
-
/** Whether `text` contains the sentinel anywhere. */
|
|
27
|
-
export function containsNoResponseMarker(text: string): boolean {
|
|
28
|
-
return NO_RESPONSE_MARKER_RE.test(text);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Removes every sentinel occurrence, trimming the leftover whitespace. */
|
|
32
|
-
export function stripNoResponseMarkers(text: string): string {
|
|
33
|
-
return text.replace(NO_RESPONSE_INLINE_RE, "").trim();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const NO_RESPONSE_SENTINEL_FORMS = [
|
|
37
|
-
"<no_response/>",
|
|
38
|
-
"<no_response />",
|
|
39
|
-
"<no_response>",
|
|
40
|
-
] as const;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Whether `text` could still grow into the sentinel — i.e. it is a leading
|
|
44
|
-
* substring of one of its forms. Holding on these keeps a slowly-streamed
|
|
45
|
-
* `<no_response/>` from surfacing as visible partial content.
|
|
46
|
-
*/
|
|
47
|
-
function isPotentialNoResponsePrefix(text: string): boolean {
|
|
48
|
-
const lower = text.trim().toLowerCase();
|
|
49
|
-
if (lower.length === 0) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
return NO_RESPONSE_SENTINEL_FORMS.some((sentinel) =>
|
|
53
|
-
sentinel.startsWith(lower),
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Whether `text` carries user-visible content worth acting on now. Returns
|
|
59
|
-
* `false` for empty text, the standalone sentinel, and any prefix that could
|
|
60
|
-
* still complete into one; returns `true` once real content remains after
|
|
61
|
-
* stripping inline sentinels.
|
|
62
|
-
*/
|
|
63
|
-
export function hasDeliverableAssistantText(text: string): boolean {
|
|
64
|
-
const trimmed = text.trim();
|
|
65
|
-
if (trimmed.length === 0) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
if (NO_RESPONSE_ONLY_RE.test(trimmed)) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
if (isPotentialNoResponsePrefix(trimmed)) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
return stripNoResponseMarkers(trimmed).length > 0;
|
|
75
|
-
}
|
|
2
|
+
* Daemon door for the shared `<no_response/>` sentinel convention. The
|
|
3
|
+
* definitions live in `@vellumai/service-contracts/no-response` so the
|
|
4
|
+
* daemon, gateway, and clients parse identically; this module exists so
|
|
5
|
+
* daemon-internal imports keep one stable path.
|
|
6
|
+
*/
|
|
7
|
+
export {
|
|
8
|
+
containsNoResponseMarker,
|
|
9
|
+
hasDeliverableAssistantText,
|
|
10
|
+
isNoResponseOnlyText,
|
|
11
|
+
isPotentialNoResponsePrefix,
|
|
12
|
+
NO_RESPONSE_INLINE_RE,
|
|
13
|
+
stripNoResponseMarkers,
|
|
14
|
+
} from "@vellumai/service-contracts/no-response";
|