@vellumai/assistant 0.10.0 → 0.10.1-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 +36 -37
- package/bun.lock +3 -0
- package/docs/workflows.md +12 -7
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@slack/types/LICENSE +23 -0
- package/node_modules/@slack/types/README.md +32 -0
- package/node_modules/@slack/types/dist/block-kit/block-elements.d.ts +953 -0
- package/node_modules/@slack/types/dist/block-kit/block-elements.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/block-kit/block-elements.js +4 -0
- package/node_modules/@slack/types/dist/block-kit/block-elements.js.map +1 -0
- package/node_modules/@slack/types/dist/block-kit/blocks.d.ts +474 -0
- package/node_modules/@slack/types/dist/block-kit/blocks.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/block-kit/blocks.js +3 -0
- package/node_modules/@slack/types/dist/block-kit/blocks.js.map +1 -0
- package/node_modules/@slack/types/dist/block-kit/composition-objects.d.ts +237 -0
- package/node_modules/@slack/types/dist/block-kit/composition-objects.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/block-kit/composition-objects.js +4 -0
- package/node_modules/@slack/types/dist/block-kit/composition-objects.js.map +1 -0
- package/node_modules/@slack/types/dist/block-kit/extensions.d.ts +88 -0
- package/node_modules/@slack/types/dist/block-kit/extensions.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/block-kit/extensions.js +3 -0
- package/node_modules/@slack/types/dist/block-kit/extensions.js.map +1 -0
- package/node_modules/@slack/types/dist/calls.d.ts +26 -0
- package/node_modules/@slack/types/dist/calls.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/calls.js +6 -0
- package/node_modules/@slack/types/dist/calls.js.map +1 -0
- package/node_modules/@slack/types/dist/chunk.d.ts +52 -0
- package/node_modules/@slack/types/dist/chunk.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/chunk.js +3 -0
- package/node_modules/@slack/types/dist/chunk.js.map +1 -0
- package/node_modules/@slack/types/dist/common/bot-profile.d.ts +12 -0
- package/node_modules/@slack/types/dist/common/bot-profile.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/common/bot-profile.js +3 -0
- package/node_modules/@slack/types/dist/common/bot-profile.js.map +1 -0
- package/node_modules/@slack/types/dist/common/status-emoji-display-info.d.ts +6 -0
- package/node_modules/@slack/types/dist/common/status-emoji-display-info.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/common/status-emoji-display-info.js +3 -0
- package/node_modules/@slack/types/dist/common/status-emoji-display-info.js.map +1 -0
- package/node_modules/@slack/types/dist/dialog.d.ts +36 -0
- package/node_modules/@slack/types/dist/dialog.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/dialog.js +3 -0
- package/node_modules/@slack/types/dist/dialog.js.map +1 -0
- package/node_modules/@slack/types/dist/events/app.d.ts +204 -0
- package/node_modules/@slack/types/dist/events/app.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/app.js +3 -0
- package/node_modules/@slack/types/dist/events/app.js.map +1 -0
- package/node_modules/@slack/types/dist/events/assistant.d.ts +29 -0
- package/node_modules/@slack/types/dist/events/assistant.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/assistant.js +3 -0
- package/node_modules/@slack/types/dist/events/assistant.js.map +1 -0
- package/node_modules/@slack/types/dist/events/call.d.ts +8 -0
- package/node_modules/@slack/types/dist/events/call.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/call.js +3 -0
- package/node_modules/@slack/types/dist/events/call.js.map +1 -0
- package/node_modules/@slack/types/dist/events/channel.d.ts +85 -0
- package/node_modules/@slack/types/dist/events/channel.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/channel.js +3 -0
- package/node_modules/@slack/types/dist/events/channel.js.map +1 -0
- package/node_modules/@slack/types/dist/events/dnd.d.ts +24 -0
- package/node_modules/@slack/types/dist/events/dnd.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/dnd.js +3 -0
- package/node_modules/@slack/types/dist/events/dnd.js.map +1 -0
- package/node_modules/@slack/types/dist/events/email.d.ts +6 -0
- package/node_modules/@slack/types/dist/events/email.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/email.js +3 -0
- package/node_modules/@slack/types/dist/events/email.js.map +1 -0
- package/node_modules/@slack/types/dist/events/emoji.d.ts +11 -0
- package/node_modules/@slack/types/dist/events/emoji.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/emoji.js +3 -0
- package/node_modules/@slack/types/dist/events/emoji.js.map +1 -0
- package/node_modules/@slack/types/dist/events/entity-details-requested.d.ts +21 -0
- package/node_modules/@slack/types/dist/events/entity-details-requested.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/entity-details-requested.js +3 -0
- package/node_modules/@slack/types/dist/events/entity-details-requested.js.map +1 -0
- package/node_modules/@slack/types/dist/events/file.d.ts +60 -0
- package/node_modules/@slack/types/dist/events/file.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/file.js +4 -0
- package/node_modules/@slack/types/dist/events/file.js.map +1 -0
- package/node_modules/@slack/types/dist/events/function.d.ts +33 -0
- package/node_modules/@slack/types/dist/events/function.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/function.js +3 -0
- package/node_modules/@slack/types/dist/events/function.js.map +1 -0
- package/node_modules/@slack/types/dist/events/grid-migration.d.ts +9 -0
- package/node_modules/@slack/types/dist/events/grid-migration.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/grid-migration.js +3 -0
- package/node_modules/@slack/types/dist/events/grid-migration.js.map +1 -0
- package/node_modules/@slack/types/dist/events/group.d.ts +55 -0
- package/node_modules/@slack/types/dist/events/group.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/group.js +3 -0
- package/node_modules/@slack/types/dist/events/group.js.map +1 -0
- package/node_modules/@slack/types/dist/events/im.d.ts +26 -0
- package/node_modules/@slack/types/dist/events/im.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/im.js +3 -0
- package/node_modules/@slack/types/dist/events/im.js.map +1 -0
- package/node_modules/@slack/types/dist/events/index.d.ts +60 -0
- package/node_modules/@slack/types/dist/events/index.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/index.js +43 -0
- package/node_modules/@slack/types/dist/events/index.js.map +1 -0
- package/node_modules/@slack/types/dist/events/invite.d.ts +20 -0
- package/node_modules/@slack/types/dist/events/invite.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/invite.js +3 -0
- package/node_modules/@slack/types/dist/events/invite.js.map +1 -0
- package/node_modules/@slack/types/dist/events/link-shared.d.ts +16 -0
- package/node_modules/@slack/types/dist/events/link-shared.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/link-shared.js +3 -0
- package/node_modules/@slack/types/dist/events/link-shared.js.map +1 -0
- package/node_modules/@slack/types/dist/events/member.d.ts +19 -0
- package/node_modules/@slack/types/dist/events/member.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/member.js +3 -0
- package/node_modules/@slack/types/dist/events/member.js.map +1 -0
- package/node_modules/@slack/types/dist/events/message-metadata.d.ts +38 -0
- package/node_modules/@slack/types/dist/events/message-metadata.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/message-metadata.js +3 -0
- package/node_modules/@slack/types/dist/events/message-metadata.js.map +1 -0
- package/node_modules/@slack/types/dist/events/message.d.ts +306 -0
- package/node_modules/@slack/types/dist/events/message.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/message.js +3 -0
- package/node_modules/@slack/types/dist/events/message.js.map +1 -0
- package/node_modules/@slack/types/dist/events/pin.d.ts +60 -0
- package/node_modules/@slack/types/dist/events/pin.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/pin.js +3 -0
- package/node_modules/@slack/types/dist/events/pin.js.map +1 -0
- package/node_modules/@slack/types/dist/events/reaction.d.ts +23 -0
- package/node_modules/@slack/types/dist/events/reaction.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/reaction.js +3 -0
- package/node_modules/@slack/types/dist/events/reaction.js.map +1 -0
- package/node_modules/@slack/types/dist/events/shared-channel.d.ts +134 -0
- package/node_modules/@slack/types/dist/events/shared-channel.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/shared-channel.js +3 -0
- package/node_modules/@slack/types/dist/events/shared-channel.js.map +1 -0
- package/node_modules/@slack/types/dist/events/star.d.ts +13 -0
- package/node_modules/@slack/types/dist/events/star.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/star.js +3 -0
- package/node_modules/@slack/types/dist/events/star.js.map +1 -0
- package/node_modules/@slack/types/dist/events/steps-from-apps.d.ts +82 -0
- package/node_modules/@slack/types/dist/events/steps-from-apps.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/steps-from-apps.js +3 -0
- package/node_modules/@slack/types/dist/events/steps-from-apps.js.map +1 -0
- package/node_modules/@slack/types/dist/events/subteam.d.ts +66 -0
- package/node_modules/@slack/types/dist/events/subteam.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/subteam.js +3 -0
- package/node_modules/@slack/types/dist/events/subteam.js.map +1 -0
- package/node_modules/@slack/types/dist/events/team.d.ts +99 -0
- package/node_modules/@slack/types/dist/events/team.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/team.js +3 -0
- package/node_modules/@slack/types/dist/events/team.js.map +1 -0
- package/node_modules/@slack/types/dist/events/token.d.ts +8 -0
- package/node_modules/@slack/types/dist/events/token.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/token.js +3 -0
- package/node_modules/@slack/types/dist/events/token.js.map +1 -0
- package/node_modules/@slack/types/dist/events/user.d.ts +313 -0
- package/node_modules/@slack/types/dist/events/user.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/events/user.js +3 -0
- package/node_modules/@slack/types/dist/events/user.js.map +1 -0
- package/node_modules/@slack/types/dist/index.d.ts +12 -0
- package/node_modules/@slack/types/dist/index.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/index.js +28 -0
- package/node_modules/@slack/types/dist/index.js.map +1 -0
- package/node_modules/@slack/types/dist/message-attachments.d.ts +171 -0
- package/node_modules/@slack/types/dist/message-attachments.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/message-attachments.js +3 -0
- package/node_modules/@slack/types/dist/message-attachments.js.map +1 -0
- package/node_modules/@slack/types/dist/message-metadata.d.ts +281 -0
- package/node_modules/@slack/types/dist/message-metadata.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/message-metadata.js +27 -0
- package/node_modules/@slack/types/dist/message-metadata.js.map +1 -0
- package/node_modules/@slack/types/dist/views.d.ts +71 -0
- package/node_modules/@slack/types/dist/views.d.ts.map +1 -0
- package/node_modules/@slack/types/dist/views.js +3 -0
- package/node_modules/@slack/types/dist/views.js.map +1 -0
- package/node_modules/@slack/types/package.json +47 -0
- package/node_modules/@vellumai/gateway-client/bun.lock +3 -0
- package/node_modules/@vellumai/gateway-client/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +69 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +65 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +162 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +4 -2
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +3 -2
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +78 -0
- package/openapi.yaml +345 -18
- package/package.json +2 -1
- package/scripts/memory-inspect.ts +24 -14
- package/src/__tests__/access-request-seed-content-blocks.test.ts +83 -103
- package/src/__tests__/activation-early-marking.test.ts +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -3
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +1 -40
- package/src/__tests__/agent-loop-compaction-events.test.ts +0 -1
- package/src/__tests__/agent-loop-compaction-strip.test.ts +0 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +0 -1
- package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
- package/src/__tests__/agent-loop-regrowth-guard.test.ts +0 -1
- package/src/__tests__/agent-loop.test.ts +3 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/anthropic-provider.test.ts +143 -9
- package/src/__tests__/app-builder-skill-instructions.test.ts +47 -5
- package/src/__tests__/app-conversation-ids-backfill.test.ts +1 -1
- package/src/__tests__/app-source-watcher.test.ts +30 -10
- package/src/__tests__/approval-cascade.test.ts +6 -0
- package/src/__tests__/approval-interception-trust-gates.test.ts +151 -0
- package/src/__tests__/approval-primitive.test.ts +1 -1
- package/src/__tests__/approval-routes-http.test.ts +1 -1
- package/src/__tests__/assistant-attachments.test.ts +155 -0
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +2 -4
- package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
- package/src/__tests__/assistant-events-sse-shed.test.ts +1 -1
- package/src/__tests__/attachment-upload-trusted-source.test.ts +13 -8
- package/src/__tests__/attachments-store.test.ts +1 -1
- package/src/__tests__/audit-log-rotation.test.ts +50 -54
- package/src/__tests__/auth-fallback-events-store.test.ts +1 -1
- package/src/__tests__/auto-analysis-end-to-end.test.ts +9 -14
- package/src/__tests__/background-shell-bash.test.ts +4 -1
- package/src/__tests__/background-shell-host-bash.test.ts +17 -3
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -0
- package/src/__tests__/call-controller.test.ts +1 -1
- package/src/__tests__/call-conversation-messages.test.ts +1 -1
- package/src/__tests__/call-domain.test.ts +1 -1
- package/src/__tests__/call-pointer-messages.test.ts +3 -4
- package/src/__tests__/call-recovery.test.ts +1 -1
- package/src/__tests__/call-routes-http.test.ts +1 -1
- package/src/__tests__/call-store.test.ts +1 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/canonical-guardian-store.test.ts +24 -1
- package/src/__tests__/channel-approval-routes.test.ts +73 -1119
- package/src/__tests__/channel-delivery-store.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +265 -641
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +1 -2
- package/src/__tests__/channel-retry-sweep.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +6 -0
- package/src/__tests__/compaction-trail-store.test.ts +6 -5
- package/src/__tests__/compaction.benchmark.test.ts +0 -1
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/config-loader-backfill.test.ts +183 -51
- package/src/__tests__/config-schema.test.ts +34 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -2
- package/src/__tests__/contact-store-user-file.test.ts +2 -2
- package/src/__tests__/contacts-relay-reads.test.ts +409 -0
- package/src/__tests__/contacts-tools.test.ts +4 -4
- package/src/__tests__/contacts-write.test.ts +1 -2
- package/src/__tests__/context-search-conversations-source.test.ts +1 -1
- package/src/__tests__/context-window-manager-compact-retry.test.ts +6 -2
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +6 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +3 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +3 -0
- package/src/__tests__/conversation-agent-loop.test.ts +3 -0
- package/src/__tests__/conversation-attachments.test.ts +2 -5
- package/src/__tests__/conversation-attention-store.test.ts +1 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +1 -2
- package/src/__tests__/conversation-clear-safety.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
- package/src/__tests__/conversation-crud-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +12 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -1
- package/src/__tests__/conversation-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +10 -8
- package/src/__tests__/conversation-fork-retrospective.test.ts +250 -0
- package/src/__tests__/conversation-fork-route.test.ts +1 -1
- package/src/__tests__/conversation-inference-profile-list.test.ts +1 -1
- package/src/__tests__/conversation-inference-profile-route.test.ts +1 -1
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +117 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -3
- package/src/__tests__/conversation-process-callsite.test.ts +6 -14
- package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/conversation-queue.test.ts +6 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +115 -12
- package/src/__tests__/conversation-slash-queue.test.ts +6 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
- package/src/__tests__/conversation-speed-override.test.ts +6 -0
- package/src/__tests__/conversation-starter-routes.test.ts +5 -5
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -1
- package/src/__tests__/conversation-sync-tags.test.ts +1 -1
- package/src/__tests__/conversation-usage.test.ts +1 -1
- package/src/__tests__/conversation-wipe.test.ts +9 -8
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/conversations-import-system-filter.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +17 -0
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/db-acp-history.test.ts +2 -2
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +5 -7
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +6 -7
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +5 -10
- package/src/__tests__/db-migration-rollback.test.ts +129 -39
- package/src/__tests__/db-proxy-transaction.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +0 -11
- package/src/__tests__/db-test-helpers.ts +36 -19
- package/src/__tests__/delete-propagation.test.ts +1 -1
- package/src/__tests__/deterministic-verification-control-plane.test.ts +26 -8
- package/src/__tests__/disk-pressure-tools.test.ts +41 -1
- package/src/__tests__/dm-backfill.test.ts +1 -1
- package/src/__tests__/drop-capability-card-state-migration.test.ts +0 -8
- package/src/__tests__/edit-propagation.test.ts +1 -1
- package/src/__tests__/emit-signal-routing-intent.test.ts +83 -0
- package/src/__tests__/empty-response-hook.test.ts +42 -0
- package/src/__tests__/events-client-registration.test.ts +1 -1
- package/src/__tests__/followup-tools.test.ts +1 -1
- package/src/__tests__/gemini-count-tokens.test.ts +70 -0
- package/src/__tests__/guardian-action-sweep.test.ts +9 -2
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +1 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +1 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -12
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/guardian-routing-state.test.ts +1 -2
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
- package/src/__tests__/headless-browser-mode.test.ts +2 -2
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +6 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +98 -0
- package/src/__tests__/http-conversation-lineage.test.ts +1 -1
- package/src/__tests__/image-recovery-hook.test.ts +1 -1
- package/src/__tests__/inbound-invite-redemption.test.ts +1 -2
- package/src/__tests__/inbound-trust-verdict.test.ts +254 -0
- package/src/__tests__/inference-profile-reaper.test.ts +1 -1
- package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
- package/src/__tests__/inference-profile-session-ipc.test.ts +1 -1
- package/src/__tests__/injector-chain.test.ts +1 -1
- package/src/__tests__/injector-disk-pressure.test.ts +11 -6
- package/src/__tests__/internal-telemetry-routes.test.ts +1 -1
- package/src/__tests__/invite-redemption-service.test.ts +244 -43
- package/src/__tests__/invite-routes-http.test.ts +35 -186
- package/src/__tests__/invite-service-ipc.test.ts +287 -0
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +5 -5
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +9 -12
- package/src/__tests__/list-messages-attachments.test.ts +42 -1
- package/src/__tests__/list-messages-client-message-id.test.ts +1 -1
- package/src/__tests__/list-messages-hidden-metadata.test.ts +1 -1
- package/src/__tests__/list-messages-page-latest.test.ts +1 -1
- package/src/__tests__/list-messages-tool-merge.test.ts +1 -1
- package/src/__tests__/llm-context-route-provider.test.ts +69 -4
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +9 -5
- package/src/__tests__/llm-request-log-call-site.test.ts +6 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +27 -13
- package/src/__tests__/llm-usage-store.test.ts +40 -1
- package/src/__tests__/log-export-routes.test.ts +1 -1
- package/src/__tests__/log-export-workspace.test.ts +3 -3
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +5 -5
- package/src/__tests__/memory-recall-log-store.test.ts +1 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +3 -4
- package/src/__tests__/messages-after-tiebreaker.test.ts +1 -1
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +375 -0
- package/src/__tests__/non-member-access-request.test.ts +1 -2
- package/src/__tests__/notification-candidate-guardian-context.test.ts +203 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -1
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
- package/src/__tests__/oauth-provider-visibility.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +1 -1
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +1 -1
- package/src/__tests__/persist-unsendable-image.test.ts +1 -1
- package/src/__tests__/persona-resolver.test.ts +39 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/playbook-execution.test.ts +1 -1
- package/src/__tests__/playbook-tools.test.ts +1 -1
- package/src/__tests__/plugin-api-model-profiles.test.ts +74 -21
- package/src/__tests__/plugin-bootstrap.test.ts +78 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +25 -5
- package/src/__tests__/provider-usage-tracking.test.ts +1 -1
- package/src/__tests__/prune-old-conversations-job.test.ts +1 -1
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +357 -56
- package/src/__tests__/runtime-attachment-metadata.test.ts +10 -1
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +7 -9
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/schedule-retry.test.ts +1 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -1
- package/src/__tests__/schedule-routes.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +1 -1
- package/src/__tests__/schedule-tools.test.ts +1 -1
- package/src/__tests__/scheduler-disk-pressure.test.ts +1 -1
- package/src/__tests__/scheduler-recurrence.test.ts +1 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +1 -1
- package/src/__tests__/scheduler-wake.test.ts +2 -1
- package/src/__tests__/scoped-approval-grants.test.ts +1 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +5 -5
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +0 -8
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +1 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +40 -1
- package/src/__tests__/settings-routes.test.ts +11 -10
- package/src/__tests__/skill-load-tool.test.ts +72 -0
- package/src/__tests__/slack-inbound-verification.test.ts +1 -3
- package/src/__tests__/slack-messaging-token-resolution.test.ts +13 -2
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +1 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +2 -73
- package/src/__tests__/subagent-tools.test.ts +1 -31
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
- package/src/__tests__/task-compiler.test.ts +1 -1
- package/src/__tests__/task-management-tools.test.ts +1 -1
- package/src/__tests__/task-memory-cleanup.test.ts +9 -6
- package/src/__tests__/task-scheduler.test.ts +1 -1
- package/src/__tests__/thread-backfill.test.ts +1 -1
- package/src/__tests__/tool-approval-handler.test.ts +1 -1
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +32 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +73 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +34 -34
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -2
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +3 -3
- package/src/__tests__/turn-events-store.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +2 -3
- package/src/__tests__/usage-cache-backfill-migration.test.ts +20 -10
- package/src/__tests__/usage-routes.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +34 -29
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/voice-invite-redemption.test.ts +134 -36
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-git-service.test.ts +114 -1
- package/src/__tests__/workspace-heartbeat-service.test.ts +45 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +88 -18
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +6 -6
- package/src/__tests__/workspace-migration-109-swap-quality-profile-to-glm-5p2.test.ts +281 -0
- package/src/__tests__/workspace-migration-110-flip-balanced-profile-to-together.test.ts +167 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +55 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +1 -1
- package/src/a2a/__tests__/task-store.test.ts +1 -1
- package/src/acp/__tests__/session-manager-persistence.test.ts +1 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +22 -11
- package/src/acp/__tests__/session-manager-startup.test.ts +1 -1
- package/src/acp/__tests__/session-manager.test.ts +72 -1
- package/src/acp/index.ts +10 -0
- package/src/acp/session-manager.ts +35 -0
- package/src/agent/loop.ts +45 -27
- package/src/api/index.ts +0 -6
- package/src/approvals/AGENTS.md +1 -2
- package/src/approvals/guardian-decision-primitive.ts +13 -210
- package/src/approvals/guardian-request-resolvers.ts +104 -58
- package/src/background-wake/wake-intent-hooks.test.ts +1 -1
- package/src/calls/__tests__/inbound-trust-reader.test.ts +110 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +88 -62
- package/src/calls/inbound-trust-reader.ts +40 -0
- package/src/calls/relay-server.ts +65 -23
- package/src/calls/relay-setup-router.ts +20 -6
- package/src/calls/relay-verification.ts +7 -7
- package/src/cli/commands/contacts.ts +6 -24
- package/src/cli/commands/db/__tests__/repair.test.ts +15 -6
- package/src/cli/commands/db/__tests__/status.test.ts +7 -3
- package/src/cli/commands/db/status.ts +212 -33
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +6 -1
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-retrospective.ts +129 -0
- package/src/cli/commands/memory/memory-v3.ts +176 -4
- package/src/cli/commands/plugins.ts +268 -11
- package/src/cli/lib/__tests__/install-from-github.test.ts +40 -0
- package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +158 -0
- package/src/cli/lib/install-from-github.ts +47 -6
- package/src/cli/lib/plugin-marketplace.ts +11 -0
- package/src/cli/lib/plugin-pin-history.ts +257 -0
- package/src/cli/lib/toggle-plugin.ts +146 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +15 -33
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +3 -8
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +64 -37
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +1 -1
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +14 -72
- package/src/config/bundled-skills/app-builder/references/examples/README.md +1 -2
- package/src/config/bundled-skills/contacts/SKILL.md +7 -12
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
- package/src/config/bundled-skills/workflows/SKILL.md +4 -3
- package/src/config/call-site-defaults.ts +11 -2
- package/src/config/feature-flag-registry.json +0 -8
- package/src/config/profile-dispatchability.ts +11 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/llm.ts +2 -0
- package/src/config/schemas/memory-lifecycle.ts +5 -3
- package/src/config/schemas/timeouts.ts +24 -0
- package/src/config/seed-inference-profiles.ts +133 -45
- package/src/config/sync-gated-profiles.ts +13 -1
- package/src/contacts/contact-store.ts +21 -0
- package/src/contacts/member-status.ts +9 -0
- package/src/credential-health/credential-health-service.ts +1 -5
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
- package/src/daemon/app-source-watcher.ts +31 -18
- package/src/daemon/assistant-attachments.ts +94 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +3 -0
- package/src/daemon/conversation-agent-loop.ts +9 -36
- package/src/daemon/conversation-runtime-assembly.ts +91 -66
- package/src/daemon/conversation-tool-setup.ts +20 -63
- package/src/daemon/conversation.ts +144 -52
- package/src/daemon/event-loop-watchdog.test.ts +85 -0
- package/src/daemon/event-loop-watchdog.ts +133 -0
- package/src/daemon/external-plugins-bootstrap.ts +26 -80
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +1 -1
- package/src/daemon/handlers/config-channels.ts +32 -18
- package/src/daemon/handlers/conversations.ts +7 -0
- package/src/daemon/handlers/shared.ts +7 -0
- package/src/daemon/lifecycle.ts +16 -3
- package/src/daemon/message-types/inbox.ts +0 -6
- package/src/daemon/message-types/messages.ts +0 -4
- package/src/daemon/message-types/surfaces.ts +18 -8
- package/src/daemon/server.ts +0 -4
- package/src/daemon/tool-setup-types.ts +0 -7
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-conversation-ops.ts +70 -0
- package/src/daemon/workspace-tools-watcher.ts +7 -3
- package/src/documents/document-comments-store.test.ts +1 -1
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +1 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +6 -0
- package/src/heartbeat/heartbeat-service.ts +3 -4
- package/src/ipc/__tests__/attachment-ipc.test.ts +1 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +73 -2
- package/src/ipc/__tests__/watcher-ipc.test.ts +59 -39
- package/src/ipc/assistant-server.ts +8 -0
- package/src/ipc/gateway-client.ts +2 -1
- package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +58 -0
- package/src/ipc/routes/invite-ipc-routes.ts +66 -0
- package/src/live-voice/__tests__/live-voice-archive.test.ts +1 -1
- package/src/memory/__tests__/activation-session-store.test.ts +1 -1
- package/src/memory/__tests__/auto-analysis-guard.test.ts +1 -1
- package/src/memory/__tests__/conversation-group-migration.test.ts +1 -1
- package/src/memory/__tests__/conversation-queries.test.ts +1 -1
- package/src/memory/__tests__/db-async-query.test.ts +1 -1
- package/src/memory/__tests__/db-logs-attach.test.ts +110 -0
- package/src/memory/__tests__/db-maintenance.test.ts +28 -36
- package/src/memory/__tests__/db-memory-attach.test.ts +113 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +1 -1
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +1 -1
- package/src/memory/__tests__/fork-message-copy.test.ts +232 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +3 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +5 -5
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +8 -6
- package/src/memory/__tests__/memory-retrospective-job.test.ts +30 -37
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +69 -66
- package/src/memory/__tests__/memory-retrospective-state.test.ts +1 -1
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +1 -1
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +1 -1
- package/src/memory/__tests__/onboarding-events-store.test.ts +1 -1
- package/src/memory/__tests__/table-relocation.test.ts +129 -0
- package/src/memory/conversation-crud.ts +461 -152
- package/src/memory/db-async-query.ts +89 -5
- package/src/memory/db-connection.ts +101 -18
- package/src/memory/db-init.ts +409 -234
- package/src/memory/db-maintenance.ts +43 -38
- package/src/memory/db-singleton.ts +45 -19
- package/src/memory/fork-message-copy.ts +170 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +92 -0
- package/src/memory/graph/bootstrap.test.ts +6 -3
- package/src/memory/graph/retriever.test.ts +12 -12
- package/src/memory/graph/store.test.ts +15 -25
- package/src/memory/graph/store.ts +23 -14
- package/src/memory/graph/tool-handlers.ts +34 -5
- package/src/memory/graph/tools.ts +5 -2
- package/src/memory/indexer.ts +21 -9
- package/src/memory/job-handlers/cleanup.ts +10 -3
- package/src/memory/job-handlers/embedding.test.ts +4 -4
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +4 -4
- package/src/memory/jobs/embed-pkb-file.test.ts +7 -7
- package/src/memory/jobs-store.ts +36 -24
- package/src/memory/llm-request-log-store.ts +51 -19
- package/src/memory/llm-usage-store.ts +31 -1
- package/src/memory/memory-retrospective-job.ts +27 -19
- package/src/memory/memory-retrospective-startup-cleanup.ts +10 -2
- package/src/memory/migrations/{100-core-tables.ts → 000-core-tables.ts} +6 -10
- package/src/memory/migrations/104-core-indexes.ts +1 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +189 -196
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +98 -105
- package/src/memory/migrations/134-contacts-notes-column.ts +66 -69
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +19 -22
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +227 -230
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +204 -209
- package/src/memory/migrations/141-rename-verification-table.ts +45 -48
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +16 -23
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +23 -30
- package/src/memory/migrations/144-rename-voice-to-phone.ts +133 -136
- package/src/memory/migrations/145-drop-accounts-table.ts +4 -7
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +79 -82
- package/src/memory/migrations/148-drop-reminders-table.ts +3 -6
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +71 -78
- package/src/memory/migrations/157-invite-contact-id.ts +73 -76
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +44 -58
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +36 -43
- package/src/memory/migrations/174-rename-thread-starters-table.ts +30 -37
- package/src/memory/migrations/176-drop-capability-card-state.ts +17 -22
- package/src/memory/migrations/177-create-trace-events-table.ts +23 -28
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +36 -43
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +14 -21
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +17 -24
- package/src/memory/migrations/192-contacts-user-file-column.ts +6 -9
- package/src/memory/migrations/193-add-source-type-columns.ts +33 -36
- package/src/memory/migrations/194-memory-recall-logs.ts +34 -39
- package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +59 -66
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +41 -48
- package/src/memory/migrations/204-rename-memory-graph-type-values.ts +11 -18
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +76 -83
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +50 -57
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +6 -11
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +4 -9
- package/src/memory/migrations/217-conversation-host-access.ts +13 -18
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +86 -93
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +41 -48
- package/src/memory/migrations/230-acp-session-history.ts +23 -28
- package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +58 -62
- package/src/memory/migrations/232-activation-state.ts +11 -16
- package/src/memory/migrations/233-document-conversations.ts +20 -25
- package/src/memory/migrations/234-memory-v2-activation-logs.ts +26 -31
- package/src/memory/migrations/235-slack-compaction-watermark.ts +5 -10
- package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +6 -11
- package/src/memory/migrations/237-heartbeat-runs.ts +22 -27
- package/src/memory/migrations/239-trace-events-created-at-index.ts +4 -9
- package/src/memory/migrations/242-message-bookmarks.ts +17 -22
- package/src/memory/migrations/245-memory-retrospective-state.ts +8 -13
- package/src/memory/migrations/249-normalize-slack-external-content.ts +37 -41
- package/src/memory/migrations/251-a2a-tasks.ts +27 -32
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +12 -17
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +10 -15
- package/src/memory/migrations/256-memory-v2-injection-events.ts +70 -74
- package/src/memory/migrations/259-conversation-cleaned-at.ts +4 -9
- package/src/memory/migrations/260-rename-cleaned-at.ts +11 -16
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +3 -8
- package/src/memory/migrations/262-memory-v3-coactivation.ts +21 -26
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +14 -19
- package/src/memory/migrations/270-schedule-description.ts +7 -12
- package/src/memory/migrations/272-acp-session-history-cwd.ts +8 -13
- package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +8 -13
- package/src/memory/migrations/297-move-llm-request-logs-to-logs-db.ts +111 -0
- package/src/memory/migrations/298-move-memory-jobs-to-memory-db.ts +128 -0
- package/src/memory/migrations/299-canonical-guardian-deliveries-conversation-index.ts +19 -0
- package/src/memory/migrations/__tests__/297-move-llm-request-logs.test.ts +180 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +333 -7
- package/src/memory/migrations/helpers/relocation.ts +227 -0
- package/src/memory/migrations/registry.ts +63 -0
- package/src/memory/migrations/run-migrations.ts +187 -16
- package/src/memory/migrations/validate-migration-state.ts +50 -145
- package/src/memory/raw-query.ts +47 -2
- package/src/memory/skill-loaded-events-store.test.ts +1 -1
- package/src/memory/task-memory-cleanup.ts +62 -41
- package/src/memory/tool-executed-events-store.test.ts +1 -1
- package/src/memory/turn-trace-store.test.ts +1 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +16 -15
- package/src/memory/v2/__tests__/harness-compare.test.ts +1 -1
- package/src/memory/v2/__tests__/harness-oracle.test.ts +1 -1
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +1 -1
- package/src/memory/v2/__tests__/sweep-job.test.ts +2 -2
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +38 -0
- package/src/memory/v3-eval/__tests__/eval-tally.test.ts +139 -0
- package/src/memory/v3-eval/eval-packets.ts +197 -12
- package/src/memory/v3-eval/eval-tally.ts +234 -0
- package/src/messaging/provider.ts +10 -0
- package/src/messaging/providers/gmail/adapter.ts +1 -0
- package/src/messaging/providers/gmail/client.ts +14 -0
- package/src/messaging/providers/index.ts +1 -1
- package/src/messaging/providers/slack/send.test.ts +87 -39
- package/src/messaging/providers/slack/send.ts +84 -105
- package/src/notifications/README.md +9 -5
- package/src/notifications/__tests__/deterministic-checks.test.ts +43 -1
- package/src/notifications/adapters/slack.ts +12 -10
- package/src/notifications/approval-card-builder.ts +81 -20
- package/src/notifications/approval-card-data.ts +8 -5
- package/src/notifications/canonical-delivery-recorder.ts +7 -5
- package/src/notifications/conversation-candidates.ts +24 -59
- package/src/notifications/copy-composer.ts +48 -68
- package/src/notifications/deterministic-checks.ts +19 -16
- package/src/notifications/emit-signal.ts +29 -1
- package/src/notifications/trusted-contact-payloads.ts +70 -0
- package/src/oauth/byo-connection.test.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +146 -6
- package/src/oauth/connection-resolver.ts +132 -5
- package/src/oauth/oauth-store.ts +16 -3
- package/src/oauth/scope-utils.ts +21 -0
- package/src/plugin-api/index.ts +9 -4
- package/src/plugin-api/model-profiles.test.ts +123 -0
- package/src/plugin-api/model-profiles.ts +5 -1
- package/src/plugin-api/vision-support.test.ts +149 -0
- package/src/plugin-api/vision-support.ts +78 -0
- package/src/plugins/defaults/compaction/window-manager.ts +45 -64
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +13 -4
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +302 -0
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +103 -0
- package/src/plugins/defaults/image-fallback/package.json +14 -0
- package/src/plugins/defaults/image-fallback/src/caption-cache.ts +49 -0
- package/src/plugins/defaults/image-fallback/src/image-persist.ts +59 -0
- package/src/plugins/defaults/image-fallback/src/vision-caption.ts +120 -0
- package/src/plugins/defaults/index.ts +23 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +14 -1
- package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
- package/src/plugins/external-plugin-loader.ts +47 -6
- package/src/plugins/mtime-cache.ts +772 -0
- package/src/plugins/pipeline.ts +7 -2
- package/src/plugins/registry.ts +16 -5
- package/src/plugins/user-loader.ts +22 -76
- package/src/prompts/persona-resolver.ts +29 -11
- package/src/prompts/system-prompt.ts +1 -1
- package/src/prompts/templates/system-sections.ts +4 -4
- package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
- package/src/providers/anthropic/client.ts +254 -185
- package/src/providers/call-site-routing.ts +10 -0
- package/src/providers/gemini/client.ts +43 -0
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/inference/connections.ts +6 -1
- package/src/providers/model-catalog.ts +37 -0
- package/src/providers/platform-proxy/constants.ts +5 -0
- package/src/providers/ratelimit.ts +9 -0
- package/src/providers/retry.ts +10 -0
- package/src/providers/together/client.ts +35 -0
- package/src/providers/types.ts +16 -0
- package/src/providers/usage-tracking.ts +7 -0
- package/src/runtime/AGENTS.md +9 -1
- package/src/runtime/__tests__/agent-wake.test.ts +259 -4
- package/src/runtime/__tests__/slack-block-formatting.test.ts +39 -10
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +417 -0
- package/src/runtime/actor-trust-resolver.ts +8 -16
- package/src/runtime/agent-wake.ts +183 -60
- package/src/runtime/channel-reply-delivery.ts +6 -3
- package/src/runtime/guardian-decision-types.ts +3 -22
- package/src/runtime/http-server.ts +1 -15
- package/src/runtime/invite-redemption-service.ts +155 -6
- package/src/runtime/invite-service.ts +113 -62
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +3 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +277 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +140 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +26 -7
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/contact-routes-update-channel-relay.test.ts +164 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +8 -8
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +1 -3
- package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +240 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +4 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +143 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +1 -1
- package/src/runtime/routes/acp-routes-list.test.ts +4 -0
- package/src/runtime/routes/acp-routes.test.ts +5 -6
- package/src/runtime/routes/attachment-routes.ts +21 -17
- package/src/runtime/routes/browser-routes.ts +19 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -9
- package/src/runtime/routes/channel-verification-routes.ts +12 -1
- package/src/runtime/routes/contact-routes.ts +275 -164
- package/src/runtime/routes/conversation-query-routes.ts +15 -5
- package/src/runtime/routes/conversation-routes.ts +24 -3
- package/src/runtime/routes/conversation-starter-routes.ts +7 -8
- package/src/runtime/routes/guardian-approval-interception.ts +13 -274
- package/src/runtime/routes/inbound-message-handler.ts +20 -15
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +285 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +45 -34
- package/src/runtime/routes/inbound-stages/admission-policy.ts +20 -5
- package/src/runtime/routes/log-export-routes.ts +2 -2
- package/src/runtime/routes/memory-eval-routes.ts +92 -0
- package/src/runtime/routes/memory-item-routes.test.ts +12 -11
- package/src/runtime/routes/migration-routes.ts +51 -40
- package/src/runtime/routes/plugins-routes.ts +164 -8
- package/src/runtime/routes/schedule-routes.ts +1 -0
- package/src/runtime/routes/usage-routes.ts +3 -0
- package/src/runtime/routes/work-items-routes.test.ts +1 -1
- package/src/runtime/slack-block-formatting.ts +46 -48
- package/src/runtime/trust-verdict-consumer.ts +172 -0
- package/src/schedule/scheduler.ts +6 -9
- package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +60 -52
- package/src/tools/ask-question/ask-question-tool.ts +14 -73
- package/src/tools/browser/__tests__/browser-status.test.ts +20 -0
- package/src/tools/browser/browser-execution.ts +16 -4
- package/src/tools/document/document-comment-tool.test.ts +1 -1
- package/src/tools/executor.ts +15 -3
- package/src/tools/host-terminal/host-shell.ts +28 -9
- package/src/tools/memory/register.test.ts +32 -0
- package/src/tools/skills/load.ts +43 -2
- package/src/tools/subagent/spawn.ts +4 -10
- package/src/tools/terminal/shell.ts +16 -5
- package/src/tools/types.ts +1 -0
- package/src/util/fs-watcher-error.ts +36 -0
- package/src/util/logs-db-path.ts +22 -0
- package/src/util/memory-db-path.ts +23 -0
- package/src/watcher/providers/gmail.ts +7 -2
- package/src/workflows/engine-integration.test.ts +1 -1
- package/src/workflows/engine.test.ts +1 -1
- package/src/workflows/engine.ts +22 -0
- package/src/workflows/fanout-load.test.ts +1 -1
- package/src/workflows/journal-store.test.ts +1 -1
- package/src/workflows/leaf-runner.test.ts +40 -1
- package/src/workflows/leaf-runner.ts +26 -1
- package/src/workspace/git-service.ts +144 -29
- package/src/workspace/migrations/109-swap-quality-profile-to-glm-5p2.ts +121 -0
- package/src/workspace/migrations/110-flip-balanced-profile-to-together.ts +82 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/migrations/runner.ts +32 -2
- package/src/__tests__/access-request-decision.test.ts +0 -375
- package/src/__tests__/guardian-grant-minting.test.ts +0 -607
- package/src/__tests__/plugin-source-watcher.test.ts +0 -302
- package/src/api/events/turn-profile-auto-routed.ts +0 -28
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +0 -107
- package/src/daemon/plugin-source-watcher.ts +0 -278
- package/src/daemon/switch-inference-profile-tool.ts +0 -62
- package/src/memory/guardian-approvals.ts +0 -361
- package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +0 -66
- package/src/memory/migrations/038-actor-token-records.ts +0 -45
- package/src/memory/migrations/039-actor-refresh-token-records.ts +0 -57
- package/src/memory/migrations/103-complex-migrations.ts +0 -23
- package/src/memory/migrations/113-late-migrations.ts +0 -30
- package/src/memory/migrations/index.ts +0 -301
- package/src/runtime/routes/access-request-decision.ts +0 -297
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +0 -963
- package/src/runtime/routes/channel-guardian-routes.ts +0 -19
- package/src/runtime/routes/guardian-expiry-sweep.ts +0 -132
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { ChannelId } from "../channels/types.js";
|
|
11
11
|
import { findContactChannel, getContact } from "../contacts/contact-store.js";
|
|
12
12
|
import { upsertContactChannel } from "../contacts/contacts-write.js";
|
|
13
|
+
import { ipcCallPersistent } from "../ipc/gateway-client.js";
|
|
13
14
|
import { getSqlite } from "../memory/db-connection.js";
|
|
14
15
|
import {
|
|
15
16
|
findActiveVoiceInvites,
|
|
@@ -20,8 +21,84 @@ import {
|
|
|
20
21
|
recordInviteUse,
|
|
21
22
|
} from "../memory/invite-store.js";
|
|
22
23
|
import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
|
|
24
|
+
import { getLogger } from "../util/logger.js";
|
|
23
25
|
import { hashVoiceCode } from "../util/voice-code.js";
|
|
24
26
|
|
|
27
|
+
const log = getLogger("invite-redemption-service");
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Gateway lifecycle bridge (shared by all redemption paths)
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
//
|
|
33
|
+
// The assistant is the authority on token/code → invite resolution (it holds
|
|
34
|
+
// the hashes and caller scoping). Once it has resolved the EXACT invite and
|
|
35
|
+
// passed its own validation, it CLAIMS the gateway-canonical row — BY ID, which
|
|
36
|
+
// is unambiguous — as the AUTHORITATIVE lifecycle gate BEFORE mutating its own
|
|
37
|
+
// DB. `record_invite_redemption` atomically checks status="active" and consumes
|
|
38
|
+
// the row, so there is no check-then-act window: a guardian revoke or a racing
|
|
39
|
+
// redemption that consumes the gateway row first makes this claim fail, and the
|
|
40
|
+
// assistant never mutates. This keeps the gateway invite row the lifecycle
|
|
41
|
+
// source of truth across every redemption path (token + 6-digit channel
|
|
42
|
+
// intercepts, voice relay, HTTP).
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Atomically CLAIM the gateway-canonical invite row for `inviteId`.
|
|
46
|
+
*
|
|
47
|
+
* This is the single authoritative gateway gate, performed BEFORE the assistant
|
|
48
|
+
* mutation. Returns a decision the caller maps to behavior:
|
|
49
|
+
* - "claimed": the gateway row existed and was consumed (active → redeemed) →
|
|
50
|
+
* proceed to the assistant mutation.
|
|
51
|
+
* - "rejected": the gateway row EXISTS but was NOT consumable
|
|
52
|
+
* (revoked / exhausted / expired / already redeemed in a race) → REJECT the
|
|
53
|
+
* redemption WITHOUT mutating the assistant DB.
|
|
54
|
+
* - "legacy": no gateway row exists (pre-migration assistant-only invite) →
|
|
55
|
+
* proceed (the assistant stays authoritative). Logged at warn.
|
|
56
|
+
* - "unavailable": the gateway IPC threw (gateway unreachable). FAIL-OPEN —
|
|
57
|
+
* proceed; the assistant-side checks still apply. Logged loudly.
|
|
58
|
+
*/
|
|
59
|
+
async function claimGatewayRedemption(
|
|
60
|
+
inviteId: string,
|
|
61
|
+
redeemedBy: {
|
|
62
|
+
redeemedByExternalUserId?: string | null;
|
|
63
|
+
redeemedByExternalChatId?: string | null;
|
|
64
|
+
},
|
|
65
|
+
): Promise<"claimed" | "rejected" | "legacy" | "unavailable"> {
|
|
66
|
+
try {
|
|
67
|
+
const res = (await ipcCallPersistent("record_invite_redemption", {
|
|
68
|
+
inviteId,
|
|
69
|
+
redeemedByExternalUserId: redeemedBy.redeemedByExternalUserId ?? null,
|
|
70
|
+
redeemedByExternalChatId: redeemedBy.redeemedByExternalChatId ?? null,
|
|
71
|
+
})) as { ok?: boolean; updated?: boolean; mirrored?: boolean } | null;
|
|
72
|
+
|
|
73
|
+
if (!res || typeof res !== "object") {
|
|
74
|
+
log.warn(
|
|
75
|
+
{ inviteId, res },
|
|
76
|
+
"record_invite_redemption: gateway returned malformed response — failing open",
|
|
77
|
+
);
|
|
78
|
+
return "unavailable";
|
|
79
|
+
}
|
|
80
|
+
if (res.mirrored === false) {
|
|
81
|
+
log.warn(
|
|
82
|
+
{ inviteId },
|
|
83
|
+
"record_invite_redemption: no gateway invite row — treating as legacy assistant-only invite",
|
|
84
|
+
);
|
|
85
|
+
return "legacy";
|
|
86
|
+
}
|
|
87
|
+
// mirrored === true: a gateway row exists. `updated` reflects whether the
|
|
88
|
+
// atomic status="active" claim consumed it.
|
|
89
|
+
return res.updated ? "claimed" : "rejected";
|
|
90
|
+
} catch (err) {
|
|
91
|
+
// FAIL-OPEN: availability > strict gating. The assistant-side validation
|
|
92
|
+
// (status/expiry/use-count/channel) still applies; we just couldn't claim
|
|
93
|
+
// the gateway-canonical row.
|
|
94
|
+
log.error(
|
|
95
|
+
{ err, inviteId },
|
|
96
|
+
"record_invite_redemption: gateway claim unavailable — failing open (assistant-side checks still apply)",
|
|
97
|
+
);
|
|
98
|
+
return "unavailable";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
25
102
|
// ---------------------------------------------------------------------------
|
|
26
103
|
// Outcome type
|
|
27
104
|
// ---------------------------------------------------------------------------
|
|
@@ -67,7 +144,7 @@ const STORE_ERROR_TO_REASON: Record<
|
|
|
67
144
|
// redeemInvite
|
|
68
145
|
// ---------------------------------------------------------------------------
|
|
69
146
|
|
|
70
|
-
export function redeemInvite(params: {
|
|
147
|
+
export async function redeemInvite(params: {
|
|
71
148
|
rawToken: string;
|
|
72
149
|
sourceChannel: string;
|
|
73
150
|
externalUserId?: string;
|
|
@@ -75,7 +152,7 @@ export function redeemInvite(params: {
|
|
|
75
152
|
displayName?: string;
|
|
76
153
|
username?: string;
|
|
77
154
|
assistantId?: string;
|
|
78
|
-
}): InviteRedemptionOutcome {
|
|
155
|
+
}): Promise<InviteRedemptionOutcome> {
|
|
79
156
|
const {
|
|
80
157
|
rawToken,
|
|
81
158
|
sourceChannel,
|
|
@@ -156,6 +233,20 @@ export function redeemInvite(params: {
|
|
|
156
233
|
return { ok: false, reason: "invalid_token" };
|
|
157
234
|
}
|
|
158
235
|
|
|
236
|
+
// Authoritative gateway claim (by id — caller-scoped resolution already
|
|
237
|
+
// picked the exact invite). Consume the gateway-canonical row BEFORE mutating
|
|
238
|
+
// the assistant so a concurrent revoke/redemption that consumes it first
|
|
239
|
+
// rejects here, with no assistant mutation to roll back. No use is consumed
|
|
240
|
+
// for the already_member/blocked early returns above.
|
|
241
|
+
if (
|
|
242
|
+
(await claimGatewayRedemption(invite.id, {
|
|
243
|
+
redeemedByExternalUserId: externalUserId,
|
|
244
|
+
redeemedByExternalChatId: externalChatId,
|
|
245
|
+
})) === "rejected"
|
|
246
|
+
) {
|
|
247
|
+
return { ok: false, reason: "invalid_token" };
|
|
248
|
+
}
|
|
249
|
+
|
|
159
250
|
// Inactive member reactivation: when the user already has a member record
|
|
160
251
|
// in a non-active state (revoked/pending), reactivate it via upsertContactChannel
|
|
161
252
|
// and consume an invite use atomically. The fresh-member path below also
|
|
@@ -211,6 +302,13 @@ export function redeemInvite(params: {
|
|
|
211
302
|
if (err === STALE_INVITE) {
|
|
212
303
|
return { ok: false, reason: "invalid_token" };
|
|
213
304
|
}
|
|
305
|
+
// Rare: the gateway claim already consumed the row but the assistant
|
|
306
|
+
// mutation failed — a recoverable wasted gateway use; no cross-process
|
|
307
|
+
// rollback is attempted.
|
|
308
|
+
log.error(
|
|
309
|
+
{ err, inviteId: invite.id },
|
|
310
|
+
"redeemInvite: assistant mutation failed AFTER gateway claim consumed the row (wasted use)",
|
|
311
|
+
);
|
|
214
312
|
throw err;
|
|
215
313
|
}
|
|
216
314
|
|
|
@@ -267,6 +365,12 @@ export function redeemInvite(params: {
|
|
|
267
365
|
if (err === STALE_INVITE_FRESH) {
|
|
268
366
|
return { ok: false, reason: "invalid_token" };
|
|
269
367
|
}
|
|
368
|
+
// Rare: gateway claim succeeded but the assistant mutation failed — a
|
|
369
|
+
// recoverable wasted gateway use; no cross-process rollback attempted.
|
|
370
|
+
log.error(
|
|
371
|
+
{ err, inviteId: invite.id },
|
|
372
|
+
"redeemInvite: assistant mutation failed AFTER gateway claim consumed the row (wasted use)",
|
|
373
|
+
);
|
|
270
374
|
throw err;
|
|
271
375
|
}
|
|
272
376
|
|
|
@@ -298,12 +402,12 @@ export function redeemInvite(params: {
|
|
|
298
402
|
* oracle attacks that could reveal which invites exist or which phone numbers
|
|
299
403
|
* are bound.
|
|
300
404
|
*/
|
|
301
|
-
export function redeemVoiceInviteCode(params: {
|
|
405
|
+
export async function redeemVoiceInviteCode(params: {
|
|
302
406
|
assistantId?: string;
|
|
303
407
|
callerExternalUserId: string;
|
|
304
408
|
sourceChannel: "phone";
|
|
305
409
|
code: string;
|
|
306
|
-
}): VoiceRedemptionOutcome {
|
|
410
|
+
}): Promise<VoiceRedemptionOutcome> {
|
|
307
411
|
const { callerExternalUserId, code } = params;
|
|
308
412
|
|
|
309
413
|
if (!callerExternalUserId) {
|
|
@@ -378,6 +482,20 @@ export function redeemVoiceInviteCode(params: {
|
|
|
378
482
|
return { ok: false, reason: "invalid_or_expired" };
|
|
379
483
|
}
|
|
380
484
|
|
|
485
|
+
// Authoritative gateway claim (by id). The candidate was already resolved
|
|
486
|
+
// caller-scoped (expectedExternalUserId == caller), so the id is unambiguous
|
|
487
|
+
// even when two phone invites share a 6-digit code — this closes the
|
|
488
|
+
// colliding-code gap that a code-hash-only gateway gate left open. Consuming
|
|
489
|
+
// the gateway row here, before the assistant mutation, closes the TOCTOU
|
|
490
|
+
// window: a concurrent revoke/redemption rejects with no assistant mutation.
|
|
491
|
+
if (
|
|
492
|
+
(await claimGatewayRedemption(invite.id, {
|
|
493
|
+
redeemedByExternalUserId: callerExternalUserId,
|
|
494
|
+
})) === "rejected"
|
|
495
|
+
) {
|
|
496
|
+
return { ok: false, reason: "invalid_or_expired" };
|
|
497
|
+
}
|
|
498
|
+
|
|
381
499
|
// Atomic redemption: upsert member + consume invite use in a transaction
|
|
382
500
|
const STALE_INVITE = Symbol("stale_invite");
|
|
383
501
|
let memberId: string | undefined;
|
|
@@ -424,6 +542,12 @@ export function redeemVoiceInviteCode(params: {
|
|
|
424
542
|
if (err === STALE_INVITE) {
|
|
425
543
|
return { ok: false, reason: "invalid_or_expired" };
|
|
426
544
|
}
|
|
545
|
+
// Rare: gateway claim succeeded but the assistant mutation failed — a
|
|
546
|
+
// recoverable wasted gateway use; no cross-process rollback attempted.
|
|
547
|
+
log.error(
|
|
548
|
+
{ err, inviteId: invite.id },
|
|
549
|
+
"redeemVoiceInviteCode: assistant mutation failed AFTER gateway claim consumed the row (wasted use)",
|
|
550
|
+
);
|
|
427
551
|
throw err;
|
|
428
552
|
}
|
|
429
553
|
|
|
@@ -449,7 +573,7 @@ export function redeemVoiceInviteCode(params: {
|
|
|
449
573
|
* Validation: active status, not expired, uses remaining, channel match.
|
|
450
574
|
* On success: upserts/reactivates a member with status 'active', policy 'allow'.
|
|
451
575
|
*/
|
|
452
|
-
export function redeemInviteByCode(params: {
|
|
576
|
+
export async function redeemInviteByCode(params: {
|
|
453
577
|
code: string;
|
|
454
578
|
sourceChannel: string;
|
|
455
579
|
externalUserId?: string;
|
|
@@ -457,7 +581,7 @@ export function redeemInviteByCode(params: {
|
|
|
457
581
|
displayName?: string;
|
|
458
582
|
username?: string;
|
|
459
583
|
assistantId?: string;
|
|
460
|
-
}): InviteRedemptionOutcome {
|
|
584
|
+
}): Promise<InviteRedemptionOutcome> {
|
|
461
585
|
const {
|
|
462
586
|
code,
|
|
463
587
|
sourceChannel,
|
|
@@ -530,6 +654,19 @@ export function redeemInviteByCode(params: {
|
|
|
530
654
|
return { ok: false, reason: "invalid_token" };
|
|
531
655
|
}
|
|
532
656
|
|
|
657
|
+
// Authoritative gateway claim (by id). Consume the gateway-canonical row
|
|
658
|
+
// before mutating the assistant, so a concurrent revoke/redemption that
|
|
659
|
+
// consumes it first rejects here with no assistant mutation to roll back. No
|
|
660
|
+
// use is consumed for the already_member/blocked early returns above.
|
|
661
|
+
if (
|
|
662
|
+
(await claimGatewayRedemption(invite.id, {
|
|
663
|
+
redeemedByExternalUserId: externalUserId,
|
|
664
|
+
redeemedByExternalChatId: externalChatId,
|
|
665
|
+
})) === "rejected"
|
|
666
|
+
) {
|
|
667
|
+
return { ok: false, reason: "invalid_token" };
|
|
668
|
+
}
|
|
669
|
+
|
|
533
670
|
// Inactive member reactivation: reactivate via upsertContactChannel and consume
|
|
534
671
|
// an invite use atomically.
|
|
535
672
|
if (existingChannel && !targetMismatch) {
|
|
@@ -577,6 +714,12 @@ export function redeemInviteByCode(params: {
|
|
|
577
714
|
if (err === STALE_INVITE_REACTIVATE) {
|
|
578
715
|
return { ok: false, reason: "invalid_token" };
|
|
579
716
|
}
|
|
717
|
+
// Rare: gateway claim succeeded but the assistant mutation failed — a
|
|
718
|
+
// recoverable wasted gateway use; no cross-process rollback attempted.
|
|
719
|
+
log.error(
|
|
720
|
+
{ err, inviteId: invite.id },
|
|
721
|
+
"redeemInviteByCode: assistant mutation failed AFTER gateway claim consumed the row (wasted use)",
|
|
722
|
+
);
|
|
580
723
|
throw err;
|
|
581
724
|
}
|
|
582
725
|
|
|
@@ -633,6 +776,12 @@ export function redeemInviteByCode(params: {
|
|
|
633
776
|
if (err === STALE_INVITE_FRESH) {
|
|
634
777
|
return { ok: false, reason: "invalid_token" };
|
|
635
778
|
}
|
|
779
|
+
// Rare: gateway claim succeeded but the assistant mutation failed — a
|
|
780
|
+
// recoverable wasted gateway use; no cross-process rollback attempted.
|
|
781
|
+
log.error(
|
|
782
|
+
{ err, inviteId: invite.id },
|
|
783
|
+
"redeemInviteByCode: assistant mutation failed AFTER gateway claim consumed the row (wasted use)",
|
|
784
|
+
);
|
|
636
785
|
throw err;
|
|
637
786
|
}
|
|
638
787
|
|
|
@@ -10,16 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
import { startInviteCall } from "../calls/call-domain.js";
|
|
12
12
|
import { isChannelId } from "../channels/types.js";
|
|
13
|
+
import { getContact } from "../contacts/contact-store.js";
|
|
13
14
|
import {
|
|
14
15
|
createInvite,
|
|
15
16
|
findById,
|
|
16
17
|
findByTokenHash,
|
|
17
18
|
hashToken,
|
|
18
19
|
type IngressInvite,
|
|
19
|
-
type InviteStatus,
|
|
20
|
-
listInvites,
|
|
21
20
|
markInviteExpired,
|
|
22
|
-
revokeInvite,
|
|
23
21
|
} from "../memory/invite-store.js";
|
|
24
22
|
import {
|
|
25
23
|
DECLINED_BY_USER_SENTINEL,
|
|
@@ -43,6 +41,12 @@ import {
|
|
|
43
41
|
// Response shapes — used by both HTTP routes and message handlers
|
|
44
42
|
// ---------------------------------------------------------------------------
|
|
45
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Redemption outcome type surfaced to callers. `already_member` consumes no
|
|
46
|
+
* invite use, so the gateway must not mirror it into recordInviteRedemption.
|
|
47
|
+
*/
|
|
48
|
+
export type RedemptionType = "redeemed" | "already_member";
|
|
49
|
+
|
|
46
50
|
export interface InviteResponseData {
|
|
47
51
|
id: string;
|
|
48
52
|
sourceChannel: string;
|
|
@@ -151,13 +155,11 @@ export async function createIngressInvite(params: {
|
|
|
151
155
|
note?: string;
|
|
152
156
|
maxUses?: number;
|
|
153
157
|
expiresInMs?: number;
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
//
|
|
158
|
+
// Voice invite parameters. Display metadata is no longer accepted from
|
|
159
|
+
// callers: the invitee's name is resolved from the bound contact's
|
|
160
|
+
// displayName at every read site (voice greeting, instructions), and the
|
|
161
|
+
// guardian label is resolved at runtime via resolveGuardianName().
|
|
157
162
|
expectedExternalUserId?: string;
|
|
158
|
-
voiceCodeDigits?: number;
|
|
159
|
-
friendName?: string;
|
|
160
|
-
guardianName?: string;
|
|
161
163
|
contactId: string;
|
|
162
164
|
}): Promise<IngressResult<InviteResponseData>> {
|
|
163
165
|
if (!params.sourceChannel) {
|
|
@@ -168,6 +170,13 @@ export async function createIngressInvite(params: {
|
|
|
168
170
|
return { ok: false, error: "contactId is required for create" };
|
|
169
171
|
}
|
|
170
172
|
|
|
173
|
+
// Resolve the bound contact's displayName as the canonical invitee name.
|
|
174
|
+
// The greeting and instruction copy use this rather than a free-text flag.
|
|
175
|
+
const boundContact = getContact(params.contactId);
|
|
176
|
+
const resolvedContactName =
|
|
177
|
+
boundContact?.displayName?.trim() || undefined;
|
|
178
|
+
const resolvedFirstName = resolvedContactName?.split(/\s+/)[0];
|
|
179
|
+
|
|
171
180
|
// For voice invites: generate a one-time numeric code, hash it, and pass
|
|
172
181
|
// the hash to the store. The plaintext code is included in the response
|
|
173
182
|
// exactly once and never stored.
|
|
@@ -196,17 +205,13 @@ export async function createIngressInvite(params: {
|
|
|
196
205
|
"expectedExternalUserId must be in E.164 format (e.g., +15551234567)",
|
|
197
206
|
};
|
|
198
207
|
}
|
|
199
|
-
|
|
200
|
-
return { ok: false, error: "friendName is required for voice invites" };
|
|
201
|
-
}
|
|
202
|
-
effectiveGuardianName =
|
|
203
|
-
params.guardianName?.trim() || resolveGuardianName();
|
|
208
|
+
effectiveGuardianName = resolveGuardianName();
|
|
204
209
|
if (
|
|
205
210
|
!effectiveGuardianName ||
|
|
206
211
|
effectiveGuardianName === DEFAULT_USER_REFERENCE ||
|
|
207
212
|
effectiveGuardianName === DECLINED_BY_USER_SENTINEL
|
|
208
213
|
) {
|
|
209
|
-
|
|
214
|
+
effectiveGuardianName = undefined;
|
|
210
215
|
}
|
|
211
216
|
voiceCode = generateVoiceCode(6);
|
|
212
217
|
voiceCodeHash = hashVoiceCode(voiceCode);
|
|
@@ -226,7 +231,10 @@ export async function createIngressInvite(params: {
|
|
|
226
231
|
expectedExternalUserId: params.expectedExternalUserId,
|
|
227
232
|
voiceCodeHash,
|
|
228
233
|
voiceCodeDigits: 6,
|
|
229
|
-
|
|
234
|
+
// Mirror the contact-resolved names into the legacy columns so
|
|
235
|
+
// outbound invite calls (which still read invite.friendName /
|
|
236
|
+
// invite.guardianName) keep working without a separate lookup.
|
|
237
|
+
friendName: resolvedContactName,
|
|
230
238
|
guardianName: effectiveGuardianName,
|
|
231
239
|
}
|
|
232
240
|
: { inviteCodeHash }),
|
|
@@ -250,7 +258,7 @@ export async function createIngressInvite(params: {
|
|
|
250
258
|
channelHandle = adapter ? await resolveAdapterHandle(adapter) : undefined;
|
|
251
259
|
const share = buildSharePayload(params.sourceChannel, rawToken);
|
|
252
260
|
guardianInstruction = await generateInviteInstruction({
|
|
253
|
-
contactName:
|
|
261
|
+
contactName: resolvedContactName,
|
|
254
262
|
channelType: params.sourceChannel,
|
|
255
263
|
channelHandle,
|
|
256
264
|
hasShareUrl: !!share?.url,
|
|
@@ -258,8 +266,10 @@ export async function createIngressInvite(params: {
|
|
|
258
266
|
});
|
|
259
267
|
}
|
|
260
268
|
|
|
261
|
-
if (isVoice
|
|
262
|
-
guardianInstruction =
|
|
269
|
+
if (isVoice) {
|
|
270
|
+
guardianInstruction = resolvedFirstName
|
|
271
|
+
? `${resolvedFirstName} will need this code when they answer. Share it with them first.`
|
|
272
|
+
: "Share this code with them — they'll need it when they answer the call.";
|
|
263
273
|
}
|
|
264
274
|
|
|
265
275
|
// Voice invites must not expose the token — callers must redeem via the
|
|
@@ -276,31 +286,71 @@ export async function createIngressInvite(params: {
|
|
|
276
286
|
};
|
|
277
287
|
}
|
|
278
288
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
// ---------------------------------------------------------------------------
|
|
290
|
+
// Mint — gateway-facing projection
|
|
291
|
+
//
|
|
292
|
+
// The gateway owns the canonical invite lifecycle in its own DB, but token
|
|
293
|
+
// generation/hashing and voice fields are assistant-owned. `mintIngressInvite`
|
|
294
|
+
// runs the same `createIngressInvite` path and surfaces the raw token plus the
|
|
295
|
+
// minimal projection the gateway mirrors. The raw token is returned exactly
|
|
296
|
+
// once and never persisted in plaintext.
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
|
|
299
|
+
/** Minimal invite projection the gateway mirrors into its own store. */
|
|
300
|
+
export interface GatewayInviteProjection {
|
|
301
|
+
id: string;
|
|
302
|
+
/** Hash of whatever code redeems this invite: token hash for token invites,
|
|
303
|
+
* voice code hash for voice/phone invites. Always non-null and mirrorable. */
|
|
304
|
+
inviteCodeHash: string;
|
|
305
|
+
sourceChannel: string;
|
|
306
|
+
contactId: string;
|
|
307
|
+
note: string | null;
|
|
308
|
+
maxUses: number;
|
|
309
|
+
expiresAt: number;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export interface MintInviteResult {
|
|
313
|
+
invite: InviteResponseData;
|
|
314
|
+
rawToken?: string;
|
|
315
|
+
gateway: GatewayInviteProjection;
|
|
291
316
|
}
|
|
292
317
|
|
|
293
|
-
export function
|
|
294
|
-
|
|
295
|
-
): IngressResult<
|
|
296
|
-
|
|
297
|
-
|
|
318
|
+
export async function mintIngressInvite(
|
|
319
|
+
params: Parameters<typeof createIngressInvite>[0],
|
|
320
|
+
): Promise<IngressResult<MintInviteResult>> {
|
|
321
|
+
const result = await createIngressInvite(params);
|
|
322
|
+
if (!result.ok) return result;
|
|
323
|
+
|
|
324
|
+
// The persisted row carries fields the response projection omits
|
|
325
|
+
// (inviteCodeHash); read it back to build the gateway projection.
|
|
326
|
+
const row = findById(result.data.id);
|
|
327
|
+
if (!row) {
|
|
328
|
+
return { ok: false, error: "Invite not found after mint" };
|
|
298
329
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
330
|
+
|
|
331
|
+
// The gateway mirrors the hash of whatever code redeems this invite. Token
|
|
332
|
+
// invites carry inviteCodeHash; voice/phone invites gate on voiceCodeHash.
|
|
333
|
+
const inviteCodeHash = row.inviteCodeHash ?? row.voiceCodeHash;
|
|
334
|
+
if (!inviteCodeHash) {
|
|
335
|
+
return { ok: false, error: "Invite is missing a redemption code hash" };
|
|
302
336
|
}
|
|
303
|
-
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
ok: true,
|
|
340
|
+
data: {
|
|
341
|
+
invite: result.data,
|
|
342
|
+
rawToken: result.data.token,
|
|
343
|
+
gateway: {
|
|
344
|
+
id: row.id,
|
|
345
|
+
inviteCodeHash,
|
|
346
|
+
sourceChannel: row.sourceChannel,
|
|
347
|
+
contactId: row.contactId,
|
|
348
|
+
note: row.note,
|
|
349
|
+
maxUses: row.maxUses,
|
|
350
|
+
expiresAt: row.expiresAt,
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
};
|
|
304
354
|
}
|
|
305
355
|
|
|
306
356
|
export async function triggerInviteCall(
|
|
@@ -317,35 +367,42 @@ export async function triggerInviteCall(
|
|
|
317
367
|
}
|
|
318
368
|
if (invite.sourceChannel !== "phone")
|
|
319
369
|
return { ok: false, error: "Only phone invites support call triggering" };
|
|
320
|
-
if (
|
|
321
|
-
!invite.expectedExternalUserId ||
|
|
322
|
-
!invite.friendName ||
|
|
323
|
-
!invite.guardianName
|
|
324
|
-
) {
|
|
370
|
+
if (!invite.expectedExternalUserId) {
|
|
325
371
|
return { ok: false, error: "Invite is missing required voice metadata" };
|
|
326
372
|
}
|
|
373
|
+
// Resolve the invitee's name from the bound contact's displayName.
|
|
374
|
+
// `contact_id` is NOT NULL on the invite row, so every invite is bound;
|
|
375
|
+
// an empty displayName falls through to the neutral "Hi there" greeting
|
|
376
|
+
// downstream rather than a stale free-text `friend_name` label.
|
|
377
|
+
const boundContact = getContact(invite.contactId);
|
|
378
|
+
const friendName = boundContact?.displayName?.trim() || "";
|
|
379
|
+
// Guardian label is resolved at runtime by the relay; mirror the legacy
|
|
380
|
+
// value into the session so STT hints continue to seed correctly.
|
|
381
|
+
const guardianName = invite.guardianName || resolveGuardianName() || "";
|
|
327
382
|
const result = await startInviteCall({
|
|
328
383
|
phoneNumber: invite.expectedExternalUserId,
|
|
329
|
-
friendName
|
|
330
|
-
guardianName
|
|
384
|
+
friendName,
|
|
385
|
+
guardianName,
|
|
331
386
|
});
|
|
332
387
|
if (!result.ok) return { ok: false, error: result.error };
|
|
333
388
|
return { ok: true, data: { callSid: result.callSid } };
|
|
334
389
|
}
|
|
335
390
|
|
|
336
|
-
export function redeemIngressInvite(params: {
|
|
391
|
+
export async function redeemIngressInvite(params: {
|
|
337
392
|
token?: string;
|
|
338
393
|
externalUserId?: string;
|
|
339
394
|
externalChatId?: string;
|
|
340
395
|
sourceChannel?: string;
|
|
341
|
-
}):
|
|
396
|
+
}): Promise<
|
|
397
|
+
IngressResult<{ invite: InviteResponseData; type: RedemptionType }>
|
|
398
|
+
> {
|
|
342
399
|
if (!params.token) {
|
|
343
400
|
return { ok: false, error: "token is required for redeem" };
|
|
344
401
|
}
|
|
345
402
|
if (!params.sourceChannel) {
|
|
346
403
|
return { ok: false, error: "sourceChannel is required for redeem" };
|
|
347
404
|
}
|
|
348
|
-
const outcome = redeemInviteTyped({
|
|
405
|
+
const outcome = await redeemInviteTyped({
|
|
349
406
|
rawToken: params.token,
|
|
350
407
|
sourceChannel: params.sourceChannel,
|
|
351
408
|
externalUserId: params.externalUserId,
|
|
@@ -354,21 +411,15 @@ export function redeemIngressInvite(params: {
|
|
|
354
411
|
if (!outcome.ok) {
|
|
355
412
|
return { ok: false, error: outcome.reason };
|
|
356
413
|
}
|
|
357
|
-
//
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return { ok: false, error: "Invite not found after redemption" };
|
|
362
|
-
}
|
|
363
|
-
return { ok: true, data: inviteToResponse(inv) };
|
|
364
|
-
}
|
|
365
|
-
// Look up the invite by token hash — same approach as the already_member path
|
|
366
|
-
// above. Using findByTokenHash avoids the pagination limit of listInvites.
|
|
414
|
+
// Look up the invite by token hash for both outcomes (`redeemed` and
|
|
415
|
+
// `already_member`). Using findByTokenHash avoids the pagination limit of
|
|
416
|
+
// listInvites. The `type` is surfaced so the gateway can skip mirroring an
|
|
417
|
+
// `already_member` redemption (which consumes no use).
|
|
367
418
|
const inv = findByTokenHash(hashToken(params.token));
|
|
368
419
|
if (!inv) {
|
|
369
420
|
return { ok: false, error: "Invite not found after redemption" };
|
|
370
421
|
}
|
|
371
|
-
return { ok: true, data: inviteToResponse(inv) };
|
|
422
|
+
return { ok: true, data: { invite: inviteToResponse(inv), type: outcome.type } };
|
|
372
423
|
}
|
|
373
424
|
|
|
374
425
|
export function redeemVoiceInviteCode(params: {
|
|
@@ -376,6 +427,6 @@ export function redeemVoiceInviteCode(params: {
|
|
|
376
427
|
callerExternalUserId: string;
|
|
377
428
|
sourceChannel: "phone";
|
|
378
429
|
code: string;
|
|
379
|
-
}): VoiceRedemptionOutcome {
|
|
430
|
+
}): Promise<VoiceRedemptionOutcome> {
|
|
380
431
|
return redeemVoiceInviteCodeTyped(params);
|
|
381
432
|
}
|
|
@@ -119,5 +119,8 @@ describe("streamExportVBundle — descriptor lifecycle", () => {
|
|
|
119
119
|
// incidental descriptors (temp output file already cleaned up, jitter).
|
|
120
120
|
expect(delta).toBeLessThan(8);
|
|
121
121
|
},
|
|
122
|
+
// Two full exports of a multi-megabyte workspace (hash pass + tar pass each)
|
|
123
|
+
// do real disk I/O; the default 5s budget is tight under loaded CI runners.
|
|
124
|
+
30_000,
|
|
122
125
|
);
|
|
123
126
|
});
|
|
@@ -39,7 +39,7 @@ import type { RouteDefinition, RouteHandlerArgs } from "../types.js";
|
|
|
39
39
|
// DB bootstrap
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
41
|
|
|
42
|
-
initializeDb();
|
|
42
|
+
await initializeDb();
|
|
43
43
|
|
|
44
44
|
// ---------------------------------------------------------------------------
|
|
45
45
|
// Helpers
|