@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
|
@@ -6,6 +6,9 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
6
6
|
|
|
7
7
|
let mockProvider: Record<string, unknown> | undefined;
|
|
8
8
|
let mockConnection: Record<string, unknown> | undefined;
|
|
9
|
+
let mockConnections:
|
|
10
|
+
| Array<Record<string, unknown> & { clientId?: string; accountInfo?: string }>
|
|
11
|
+
| undefined;
|
|
9
12
|
let mockAccessToken: string | undefined;
|
|
10
13
|
let mockConfig: Record<string, unknown> = {};
|
|
11
14
|
let mockPlatformClient: Record<string, unknown> | null = null;
|
|
@@ -24,15 +27,17 @@ mock.module("../util/logger.js", () => ({
|
|
|
24
27
|
|
|
25
28
|
mock.module("./oauth-store.js", () => ({
|
|
26
29
|
getProvider: () => mockProvider,
|
|
27
|
-
|
|
30
|
+
getActiveConnections: (
|
|
28
31
|
_pk: string,
|
|
29
32
|
opts?: { clientId?: string; account?: string },
|
|
30
33
|
) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return
|
|
35
|
-
|
|
34
|
+
// Default to the single mockConnection unless a test sets an explicit list.
|
|
35
|
+
const rows = mockConnections ?? (mockConnection ? [mockConnection] : []);
|
|
36
|
+
return rows.filter((row) => {
|
|
37
|
+
if (opts?.clientId && row.clientId !== opts.clientId) return false;
|
|
38
|
+
if (opts?.account && row.accountInfo !== opts.account) return false;
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
36
41
|
},
|
|
37
42
|
}));
|
|
38
43
|
|
|
@@ -131,6 +136,7 @@ function setupDefaults(): void {
|
|
|
131
136
|
"google-oauth": { mode: "managed" },
|
|
132
137
|
},
|
|
133
138
|
};
|
|
139
|
+
mockConnections = undefined;
|
|
134
140
|
mockPlatformClient = makeMockClient();
|
|
135
141
|
syncManualTokenCalls = [];
|
|
136
142
|
}
|
|
@@ -294,6 +300,140 @@ describe("resolveOAuthConnection", () => {
|
|
|
294
300
|
});
|
|
295
301
|
});
|
|
296
302
|
|
|
303
|
+
describe("resolveOAuthConnection scope-awareness", () => {
|
|
304
|
+
const GMAIL_SCOPE = "https://www.googleapis.com/auth/gmail.readonly";
|
|
305
|
+
const CALENDAR_ONLY = [
|
|
306
|
+
"https://www.googleapis.com/auth/calendar.events",
|
|
307
|
+
"https://www.googleapis.com/auth/userinfo.email",
|
|
308
|
+
];
|
|
309
|
+
const FULL_BUNDLE = [GMAIL_SCOPE, ...CALENDAR_ONLY];
|
|
310
|
+
|
|
311
|
+
function clientReturning(results: unknown[]) {
|
|
312
|
+
return {
|
|
313
|
+
...makeMockClient(),
|
|
314
|
+
fetch: mock(
|
|
315
|
+
async () => new Response(JSON.stringify({ results }), { status: 200 }),
|
|
316
|
+
),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
beforeEach(() => {
|
|
321
|
+
setupDefaults();
|
|
322
|
+
mockProvider!.managedServiceConfigKey = "google-oauth";
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("managed: rejects a Calendar-only connection when Gmail scope is required", async () => {
|
|
326
|
+
mockPlatformClient = clientReturning([
|
|
327
|
+
{ id: "cal-only", account_label: null, scopes_granted: CALENDAR_ONLY },
|
|
328
|
+
]);
|
|
329
|
+
|
|
330
|
+
await expect(
|
|
331
|
+
resolveOAuthConnection("google", { requiredScopes: [GMAIL_SCOPE] }),
|
|
332
|
+
).rejects.toThrow(/missing required access.*gmail\.readonly/s);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test("managed: resolves when a connection carries the required Gmail scope", async () => {
|
|
336
|
+
mockPlatformClient = clientReturning([
|
|
337
|
+
{ id: "full", account_label: null, scopes_granted: FULL_BUNDLE },
|
|
338
|
+
]);
|
|
339
|
+
|
|
340
|
+
const result = await resolveOAuthConnection("google", {
|
|
341
|
+
requiredScopes: [GMAIL_SCOPE],
|
|
342
|
+
});
|
|
343
|
+
expect(result).toBeInstanceOf(PlatformOAuthConnection);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("managed: unknown scope data never blocks (back-compat)", async () => {
|
|
347
|
+
// Older connections report no scopes_granted — must not be rejected.
|
|
348
|
+
mockPlatformClient = clientReturning([
|
|
349
|
+
{ id: "legacy", account_label: null },
|
|
350
|
+
]);
|
|
351
|
+
|
|
352
|
+
const result = await resolveOAuthConnection("google", {
|
|
353
|
+
requiredScopes: [GMAIL_SCOPE],
|
|
354
|
+
});
|
|
355
|
+
expect(result).toBeInstanceOf(PlatformOAuthConnection);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test("managed: prefers a scope-satisfying connection over a narrow one", async () => {
|
|
359
|
+
const fullClient = clientReturning([
|
|
360
|
+
{ id: "cal-only", account_label: null, scopes_granted: CALENDAR_ONLY },
|
|
361
|
+
{ id: "full", account_label: null, scopes_granted: FULL_BUNDLE },
|
|
362
|
+
]);
|
|
363
|
+
mockPlatformClient = fullClient;
|
|
364
|
+
|
|
365
|
+
const result = await resolveOAuthConnection("google", {
|
|
366
|
+
requiredScopes: [GMAIL_SCOPE],
|
|
367
|
+
});
|
|
368
|
+
expect(result).toBeInstanceOf(PlatformOAuthConnection);
|
|
369
|
+
// PlatformOAuthConnection is keyed by provider, so assert resolution
|
|
370
|
+
// succeeded (it would have thrown if only the narrow connection matched).
|
|
371
|
+
expect(result.provider).toBe("google");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("managed: no requiredScopes preserves prior behavior", async () => {
|
|
375
|
+
mockPlatformClient = clientReturning([
|
|
376
|
+
{ id: "cal-only", account_label: null, scopes_granted: CALENDAR_ONLY },
|
|
377
|
+
]);
|
|
378
|
+
|
|
379
|
+
const result = await resolveOAuthConnection("google");
|
|
380
|
+
expect(result).toBeInstanceOf(PlatformOAuthConnection);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test("BYO: rejects when granted scopes are known and missing the requirement", async () => {
|
|
384
|
+
(mockConfig.services as Record<string, unknown>)["google-oauth"] = {
|
|
385
|
+
mode: "your-own",
|
|
386
|
+
};
|
|
387
|
+
mockConnection!.grantedScopes = JSON.stringify(CALENDAR_ONLY);
|
|
388
|
+
|
|
389
|
+
await expect(
|
|
390
|
+
resolveOAuthConnection("google", { requiredScopes: [GMAIL_SCOPE] }),
|
|
391
|
+
).rejects.toThrow(/missing required access/);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test("BYO: unknown granted scopes never block", async () => {
|
|
395
|
+
(mockConfig.services as Record<string, unknown>)["google-oauth"] = {
|
|
396
|
+
mode: "your-own",
|
|
397
|
+
};
|
|
398
|
+
mockConnection!.grantedScopes = JSON.stringify([]);
|
|
399
|
+
|
|
400
|
+
const result = await resolveOAuthConnection("google", {
|
|
401
|
+
requiredScopes: [GMAIL_SCOPE],
|
|
402
|
+
});
|
|
403
|
+
expect(result).toBeInstanceOf(BYOOAuthConnection);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test("BYO: picks an older scope-satisfying connection over a newer narrow one", async () => {
|
|
407
|
+
(mockConfig.services as Record<string, unknown>)["google-oauth"] = {
|
|
408
|
+
mode: "your-own",
|
|
409
|
+
};
|
|
410
|
+
// Newest first (matching the store's ordering): a Calendar-only row, then
|
|
411
|
+
// an older row that carries the Gmail scope. The narrow row must not win.
|
|
412
|
+
mockConnections = [
|
|
413
|
+
{
|
|
414
|
+
id: "cal-only",
|
|
415
|
+
provider: "google",
|
|
416
|
+
accountInfo: "user@example.com",
|
|
417
|
+
grantedScopes: JSON.stringify(CALENDAR_ONLY),
|
|
418
|
+
status: "active",
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
id: "full",
|
|
422
|
+
provider: "google",
|
|
423
|
+
accountInfo: "user@example.com",
|
|
424
|
+
grantedScopes: JSON.stringify(FULL_BUNDLE),
|
|
425
|
+
status: "active",
|
|
426
|
+
},
|
|
427
|
+
];
|
|
428
|
+
|
|
429
|
+
const result = await resolveOAuthConnection("google", {
|
|
430
|
+
requiredScopes: [GMAIL_SCOPE],
|
|
431
|
+
});
|
|
432
|
+
expect(result).toBeInstanceOf(BYOOAuthConnection);
|
|
433
|
+
expect(result.id).toBe("full");
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
297
437
|
describe("resolveEffectiveBaseUrl", () => {
|
|
298
438
|
const fallback = "https://login.salesforce.com";
|
|
299
439
|
|
|
@@ -10,8 +10,9 @@ import { BYOOAuthConnection } from "./byo-connection.js";
|
|
|
10
10
|
import type { OAuthConnection } from "./connection.js";
|
|
11
11
|
import { getConnectionAccessTokenResult } from "./credential-token-resolver.js";
|
|
12
12
|
import { syncManualTokenConnection } from "./manual-token-connection.js";
|
|
13
|
-
import {
|
|
13
|
+
import { getActiveConnections, getProvider } from "./oauth-store.js";
|
|
14
14
|
import { PlatformOAuthConnection } from "./platform-connection.js";
|
|
15
|
+
import { scopeDifference } from "./scope-utils.js";
|
|
15
16
|
|
|
16
17
|
const log = getLogger("connection-resolver");
|
|
17
18
|
|
|
@@ -23,6 +24,17 @@ export interface ResolveOAuthConnectionOptions {
|
|
|
23
24
|
* accounts are connected for the same provider. Best-effort: not guaranteed
|
|
24
25
|
* to be present on all connections. */
|
|
25
26
|
account?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Scopes the caller needs the connection to actually carry. A single provider
|
|
29
|
+
* key can bundle several products behind one OAuth app (notably Google: Gmail
|
|
30
|
+
* + Calendar + Drive), and a connection may have been granted only a subset.
|
|
31
|
+
* When set, resolution prefers a connection whose granted scopes cover these,
|
|
32
|
+
* and fails with an actionable "reconnect to grant X" error — instead of
|
|
33
|
+
* returning a token that 403s downstream — when the only active connection(s)
|
|
34
|
+
* positively lack a required scope. Scope data that is simply unknown never
|
|
35
|
+
* blocks (see {@link selectConnectionByScopes}).
|
|
36
|
+
*/
|
|
37
|
+
requiredScopes?: string[];
|
|
26
38
|
}
|
|
27
39
|
|
|
28
40
|
/**
|
|
@@ -46,7 +58,7 @@ export async function resolveOAuthConnection(
|
|
|
46
58
|
provider: string,
|
|
47
59
|
options?: ResolveOAuthConnectionOptions,
|
|
48
60
|
): Promise<OAuthConnection> {
|
|
49
|
-
const { clientId, account } = options ?? {};
|
|
61
|
+
const { clientId, account, requiredScopes } = options ?? {};
|
|
50
62
|
const providerRow = getProvider(provider);
|
|
51
63
|
const managedKey = providerRow?.managedServiceConfigKey;
|
|
52
64
|
|
|
@@ -68,6 +80,7 @@ export async function resolveOAuthConnection(
|
|
|
68
80
|
client,
|
|
69
81
|
provider,
|
|
70
82
|
account,
|
|
83
|
+
requiredScopes,
|
|
71
84
|
});
|
|
72
85
|
|
|
73
86
|
return new PlatformOAuthConnection({
|
|
@@ -87,8 +100,8 @@ export async function resolveOAuthConnection(
|
|
|
87
100
|
await syncManualTokenConnection(provider);
|
|
88
101
|
}
|
|
89
102
|
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
103
|
+
const candidates = getActiveConnections(provider, { clientId, account });
|
|
104
|
+
if (candidates.length === 0) {
|
|
92
105
|
const filters = [
|
|
93
106
|
account && `account "${account}"`,
|
|
94
107
|
clientId && `client ID "${clientId}"`,
|
|
@@ -101,6 +114,25 @@ export async function resolveOAuthConnection(
|
|
|
101
114
|
);
|
|
102
115
|
}
|
|
103
116
|
|
|
117
|
+
// Scope guard: when the caller needs specific scopes, pick a connection that
|
|
118
|
+
// actually carries them rather than blindly taking the newest row — a user
|
|
119
|
+
// may hold several active connections (e.g. one Calendar-only, one full).
|
|
120
|
+
// Only fail when EVERY active connection positively lacks a required scope;
|
|
121
|
+
// unknown scope data never blocks. Without requiredScopes, behavior is
|
|
122
|
+
// unchanged: take the most-recently-created connection.
|
|
123
|
+
let conn = candidates[0];
|
|
124
|
+
if (requiredScopes?.length) {
|
|
125
|
+
const { eligible, missingScopes } = partitionByScopes(
|
|
126
|
+
candidates,
|
|
127
|
+
requiredScopes,
|
|
128
|
+
(row) => parseGrantedScopes(row.grantedScopes),
|
|
129
|
+
);
|
|
130
|
+
if (eligible.length === 0) {
|
|
131
|
+
throw new Error(missingScopesMessage(provider, missingScopes));
|
|
132
|
+
}
|
|
133
|
+
conn = eligible[0];
|
|
134
|
+
}
|
|
135
|
+
|
|
104
136
|
const tokenResult = await getConnectionAccessTokenResult({
|
|
105
137
|
provider,
|
|
106
138
|
connectionId: conn.id,
|
|
@@ -188,11 +220,15 @@ interface ResolvePlatformConnectionIdOptions {
|
|
|
188
220
|
client: VellumPlatformClient;
|
|
189
221
|
provider: string;
|
|
190
222
|
account?: string;
|
|
223
|
+
requiredScopes?: string[];
|
|
191
224
|
}
|
|
192
225
|
|
|
193
226
|
interface PlatformConnectionEntry {
|
|
194
227
|
id: string;
|
|
195
228
|
account_label?: string | null;
|
|
229
|
+
/** Scopes the platform actually granted this connection. May be absent for
|
|
230
|
+
* older connections or providers that don't report scopes. */
|
|
231
|
+
scopes_granted?: string[] | null;
|
|
196
232
|
}
|
|
197
233
|
|
|
198
234
|
/**
|
|
@@ -241,7 +277,7 @@ async function fetchPlatformConnections(options: {
|
|
|
241
277
|
async function resolvePlatformConnectionId(
|
|
242
278
|
options: ResolvePlatformConnectionIdOptions,
|
|
243
279
|
): Promise<string> {
|
|
244
|
-
const { client, provider, account } = options;
|
|
280
|
+
const { client, provider, account, requiredScopes } = options;
|
|
245
281
|
|
|
246
282
|
let connections = await fetchPlatformConnections({
|
|
247
283
|
client,
|
|
@@ -268,6 +304,26 @@ async function resolvePlatformConnectionId(
|
|
|
268
304
|
);
|
|
269
305
|
}
|
|
270
306
|
|
|
307
|
+
// Narrow to connections that actually carry the scopes the caller needs.
|
|
308
|
+
// This is what keeps a narrowly-scoped Google connection (e.g. Calendar-only,
|
|
309
|
+
// created by the onboarding check-in flow) from being resolved as a full
|
|
310
|
+
// Gmail connection and 403-ing on the first Gmail API call.
|
|
311
|
+
if (requiredScopes?.length) {
|
|
312
|
+
const { eligible, missingScopes } = partitionByScopes(
|
|
313
|
+
connections,
|
|
314
|
+
requiredScopes,
|
|
315
|
+
(c) => c.scopes_granted ?? [],
|
|
316
|
+
);
|
|
317
|
+
if (eligible.length === 0) {
|
|
318
|
+
log.warn(
|
|
319
|
+
{ provider, count: connections.length, requiredScopes, missingScopes },
|
|
320
|
+
"Active platform connection(s) found but none carry the required scopes",
|
|
321
|
+
);
|
|
322
|
+
throw new Error(missingScopesMessage(provider, missingScopes));
|
|
323
|
+
}
|
|
324
|
+
connections = eligible;
|
|
325
|
+
}
|
|
326
|
+
|
|
271
327
|
if (connections.length > 1 && !account) {
|
|
272
328
|
const allAccounts = connections
|
|
273
329
|
.map((c) => c.account_label ?? c.id)
|
|
@@ -286,3 +342,74 @@ async function resolvePlatformConnectionId(
|
|
|
286
342
|
|
|
287
343
|
return connections[0].id;
|
|
288
344
|
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Partition connections into those eligible to serve a request needing
|
|
348
|
+
* `requiredScopes` versus the scopes positively missing across all of them.
|
|
349
|
+
*
|
|
350
|
+
* Scope data can be absent (older connections, providers that don't report
|
|
351
|
+
* granted scopes). We only ever REJECT a connection when we can positively see
|
|
352
|
+
* its granted-scope set AND that set is missing a required scope — never when
|
|
353
|
+
* scope data is simply unknown. This keeps the check from breaking existing
|
|
354
|
+
* working connections while still catching the real failure mode: a narrowly-
|
|
355
|
+
* scoped connection masquerading as a fully-capable one.
|
|
356
|
+
*
|
|
357
|
+
* `eligible` is ordered scope-satisfying first, then scope-unknown, preserving
|
|
358
|
+
* the caller's most-recent-first ordering within each group, so `eligible[0]`
|
|
359
|
+
* is the best connection to use. `missingScopes` is only meaningful when
|
|
360
|
+
* `eligible` is empty (every connection positively lacked a required scope).
|
|
361
|
+
*/
|
|
362
|
+
function partitionByScopes<T>(
|
|
363
|
+
items: T[],
|
|
364
|
+
requiredScopes: string[],
|
|
365
|
+
getGranted: (item: T) => string[],
|
|
366
|
+
): { eligible: T[]; missingScopes: string[] } {
|
|
367
|
+
const satisfying: T[] = [];
|
|
368
|
+
const scopeUnknown: T[] = [];
|
|
369
|
+
const missingPerItem: string[][] = [];
|
|
370
|
+
|
|
371
|
+
for (const item of items) {
|
|
372
|
+
const granted = getGranted(item);
|
|
373
|
+
if (granted.length === 0) {
|
|
374
|
+
// Unknown scope coverage — don't block on it.
|
|
375
|
+
scopeUnknown.push(item);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const missing = scopeDifference(requiredScopes, granted);
|
|
379
|
+
if (missing.length === 0) {
|
|
380
|
+
satisfying.push(item);
|
|
381
|
+
} else {
|
|
382
|
+
missingPerItem.push(missing);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
eligible: [...satisfying, ...scopeUnknown],
|
|
388
|
+
missingScopes: Array.from(new Set(missingPerItem.flat())),
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** Best-effort parse of a connection row's JSON-encoded granted-scopes column. */
|
|
393
|
+
function parseGrantedScopes(raw: string | null | undefined): string[] {
|
|
394
|
+
if (!raw) return [];
|
|
395
|
+
try {
|
|
396
|
+
const parsed = JSON.parse(raw);
|
|
397
|
+
return Array.isArray(parsed)
|
|
398
|
+
? parsed.filter((s): s is string => typeof s === "string")
|
|
399
|
+
: [];
|
|
400
|
+
} catch {
|
|
401
|
+
return [];
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** Actionable error shown when a connection is missing required scopes. */
|
|
406
|
+
function missingScopesMessage(
|
|
407
|
+
provider: string,
|
|
408
|
+
missingScopes: string[],
|
|
409
|
+
): string {
|
|
410
|
+
return (
|
|
411
|
+
`Your ${provider} account is connected but is missing required access ` +
|
|
412
|
+
`(${missingScopes.join(", ")}). Reconnect ${provider} and grant the ` +
|
|
413
|
+
`missing permission to continue.`
|
|
414
|
+
);
|
|
415
|
+
}
|
package/src/oauth/oauth-store.ts
CHANGED
|
@@ -821,6 +821,20 @@ export function getActiveConnection(
|
|
|
821
821
|
provider: string,
|
|
822
822
|
options?: { clientId?: string; account?: string },
|
|
823
823
|
): OAuthConnectionRow | undefined {
|
|
824
|
+
return getActiveConnections(provider, options)[0];
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Like {@link getActiveConnection}, but returns ALL matching active connections
|
|
829
|
+
* (most-recently-created first) instead of just the newest. Callers that must
|
|
830
|
+
* choose among multiple connections — e.g. picking the one that carries a
|
|
831
|
+
* specific set of granted scopes — use this; `getActiveConnection` is the
|
|
832
|
+
* single-result convenience over the same query.
|
|
833
|
+
*/
|
|
834
|
+
export function getActiveConnections(
|
|
835
|
+
provider: string,
|
|
836
|
+
options?: { clientId?: string; account?: string },
|
|
837
|
+
): OAuthConnectionRow[] {
|
|
824
838
|
const { clientId, account } = options ?? {};
|
|
825
839
|
const db = getDb();
|
|
826
840
|
|
|
@@ -835,7 +849,7 @@ export function getActiveConnection(
|
|
|
835
849
|
|
|
836
850
|
if (clientId) {
|
|
837
851
|
const app = getAppByProviderAndClientId(provider, clientId);
|
|
838
|
-
if (!app) return
|
|
852
|
+
if (!app) return [];
|
|
839
853
|
conditions.push(eq(oauthConnections.oauthAppId, app.id));
|
|
840
854
|
}
|
|
841
855
|
|
|
@@ -844,8 +858,7 @@ export function getActiveConnection(
|
|
|
844
858
|
.from(oauthConnections)
|
|
845
859
|
.where(and(...conditions))
|
|
846
860
|
.orderBy(desc(oauthConnections.createdAt), sql`rowid DESC`)
|
|
847
|
-
.
|
|
848
|
-
.get();
|
|
861
|
+
.all();
|
|
849
862
|
}
|
|
850
863
|
|
|
851
864
|
/** @deprecated Use {@link getActiveConnection} instead. */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for reasoning about OAuth scope coverage.
|
|
3
|
+
*
|
|
4
|
+
* A single provider key can bundle several products behind one OAuth app
|
|
5
|
+
* (notably Google: Gmail + Calendar + Drive + Contacts). A connection may be
|
|
6
|
+
* granted only a subset of those scopes, so callers that need a specific
|
|
7
|
+
* capability must compare what they require against what was actually granted
|
|
8
|
+
* rather than treating any active connection as fully capable.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Return the required scopes that are NOT present in the granted set.
|
|
13
|
+
* An empty result means every required scope is granted.
|
|
14
|
+
*/
|
|
15
|
+
export function scopeDifference(
|
|
16
|
+
required: string[],
|
|
17
|
+
granted: string[],
|
|
18
|
+
): string[] {
|
|
19
|
+
const grantedSet = new Set(granted);
|
|
20
|
+
return required.filter((s) => !grantedSet.has(s));
|
|
21
|
+
}
|
package/src/plugin-api/index.ts
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
* the host hands to plugin hooks, and the logger shape they include.
|
|
21
21
|
*
|
|
22
22
|
* Alongside those types, the module exposes a small set of **runtime
|
|
23
|
-
* handles
|
|
24
|
-
* (subscribe to runtime events,
|
|
23
|
+
* handles for plugins that need to reach the assistant's live singletons
|
|
24
|
+
* (subscribe to runtime events, inspect inference profiles). These resolve to the
|
|
25
25
|
* assistant's own instances: the host parks the loaded plugin-api namespace
|
|
26
26
|
* on `globalThis` at boot, and the workspace-level shim re-binds each
|
|
27
27
|
* runtime export from there — so a plugin's
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
* disjoint module copy.
|
|
32
32
|
*
|
|
33
33
|
* - {@link assistantEventHub} — the assistant's pub/sub hub for runtime events
|
|
34
|
-
* - {@link getSecureKeyAsync} — read a secret from secure storage
|
|
35
34
|
* - {@link getModelProfiles} — list the workspace inference profiles a plugin
|
|
36
35
|
* can route to (e.g. a model router building its category → profile map)
|
|
37
36
|
* - {@link getConfiguredProvider} — resolve a {@link Provider} for a call site
|
|
@@ -130,8 +129,14 @@ export type {
|
|
|
130
129
|
AssistantEventSubscription,
|
|
131
130
|
} from "../runtime/assistant-event-hub.js";
|
|
132
131
|
export { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
133
|
-
export { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
134
132
|
export { getModelProfiles } from "./model-profiles.js";
|
|
133
|
+
// Check whether a profile's resolved model can process image input. Resolves
|
|
134
|
+
// the effective (provider, model) by merging over the workspace default and
|
|
135
|
+
// inferring the provider for model-only profiles, then looks up the model
|
|
136
|
+
// catalog's `supportsVision` flag. Handles mix profiles (true if any arm
|
|
137
|
+
// supports vision). Fail-open for unknown models. Pair with
|
|
138
|
+
// `getModelProfiles()` to inspect the active or candidate profiles.
|
|
139
|
+
export { doesSupportVision } from "./vision-support.js";
|
|
135
140
|
// Resolve a provider for a call site (optionally overriding the profile) so a
|
|
136
141
|
// plugin can run inference through the workspace's configured profiles and
|
|
137
142
|
// credentials — managed-proxy or BYOK — without supplying its own API key.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// ─── Mock config ────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
interface MockProfileEntry {
|
|
6
|
+
label?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
provider?: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
status?: string;
|
|
11
|
+
mix?: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let mockProfiles: Record<string, MockProfileEntry> = {};
|
|
15
|
+
let mockActiveProfile: string | undefined;
|
|
16
|
+
let mockProfileOrder: string[] | undefined;
|
|
17
|
+
|
|
18
|
+
const realConfigLoader = await import("../config/loader.js");
|
|
19
|
+
|
|
20
|
+
mock.module("../config/loader.js", () => ({
|
|
21
|
+
...realConfigLoader,
|
|
22
|
+
getConfig: () => ({
|
|
23
|
+
llm: {
|
|
24
|
+
profiles: mockProfiles,
|
|
25
|
+
activeProfile: mockActiveProfile,
|
|
26
|
+
profileOrder: mockProfileOrder,
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
getConfigReadOnly: () => ({
|
|
30
|
+
llm: {
|
|
31
|
+
profiles: mockProfiles,
|
|
32
|
+
activeProfile: mockActiveProfile,
|
|
33
|
+
profileOrder: mockProfileOrder,
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
const { getModelProfiles } = await import("./model-profiles.js");
|
|
39
|
+
|
|
40
|
+
// ─── Setup ──────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
mockProfiles = {};
|
|
44
|
+
mockActiveProfile = undefined;
|
|
45
|
+
mockProfileOrder = undefined;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// ─── Tests ──────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
describe("getModelProfiles", () => {
|
|
51
|
+
test("returns all configured profiles in order", () => {
|
|
52
|
+
mockProfiles = {
|
|
53
|
+
balanced: { label: "Balanced", provider: "anthropic" },
|
|
54
|
+
"quality-optimized": { label: "Quality", provider: "anthropic" },
|
|
55
|
+
};
|
|
56
|
+
mockProfileOrder = ["balanced", "quality-optimized"];
|
|
57
|
+
|
|
58
|
+
const result = getModelProfiles();
|
|
59
|
+
expect(result.map((p) => p.key)).toEqual(["balanced", "quality-optimized"]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("includes disabled profiles (flagged via isDisabled)", () => {
|
|
63
|
+
mockProfiles = {
|
|
64
|
+
balanced: { label: "Balanced", provider: "anthropic" },
|
|
65
|
+
disabled: {
|
|
66
|
+
label: "Disabled",
|
|
67
|
+
provider: "anthropic",
|
|
68
|
+
status: "disabled",
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const result = getModelProfiles();
|
|
73
|
+
expect(result).toHaveLength(2);
|
|
74
|
+
const disabled = result.find((p) => p.key === "disabled");
|
|
75
|
+
expect(disabled?.isDisabled).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("flags mix profiles via isMix", () => {
|
|
79
|
+
mockProfiles = {
|
|
80
|
+
"mix-profile": {
|
|
81
|
+
label: "Mix",
|
|
82
|
+
mix: [{ profile: "balanced", weight: 1 }],
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const result = getModelProfiles();
|
|
87
|
+
expect(result[0].isMix).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("skips metadata-only profiles that cannot route plugin calls", () => {
|
|
91
|
+
mockProfiles = {
|
|
92
|
+
metadata: { label: "Metadata Only" },
|
|
93
|
+
"model-only": { label: "Model Only", model: "claude-opus-4-6" },
|
|
94
|
+
"provider-only": { label: "Provider Only", provider: "anthropic" },
|
|
95
|
+
mix: {
|
|
96
|
+
label: "Mix",
|
|
97
|
+
mix: [{ profile: "model-only", weight: 1 }],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
mockProfileOrder = ["metadata", "model-only", "provider-only", "mix"];
|
|
101
|
+
|
|
102
|
+
const result = getModelProfiles();
|
|
103
|
+
expect(result.map((p) => p.key)).toEqual([
|
|
104
|
+
"model-only",
|
|
105
|
+
"provider-only",
|
|
106
|
+
"mix",
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("marks the active profile with isActive", () => {
|
|
111
|
+
mockProfiles = {
|
|
112
|
+
balanced: { label: "Balanced", provider: "anthropic" },
|
|
113
|
+
"quality-optimized": { label: "Quality", provider: "anthropic" },
|
|
114
|
+
};
|
|
115
|
+
mockActiveProfile = "balanced";
|
|
116
|
+
|
|
117
|
+
const result = getModelProfiles();
|
|
118
|
+
expect(result.find((p) => p.key === "balanced")?.isActive).toBe(true);
|
|
119
|
+
expect(result.find((p) => p.key === "quality-optimized")?.isActive).toBe(
|
|
120
|
+
false,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { getConfig } from "../config/loader.js";
|
|
2
|
+
import { isDispatchableProfile } from "../config/profile-dispatchability.js";
|
|
2
3
|
import { orderProfileKeys } from "../config/profile-order.js";
|
|
3
4
|
import type { ModelProfileInfo } from "./types.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* List the workspace inference profiles a plugin can route to, in the order the
|
|
7
8
|
* `/model` picker presents them (`llm.profileOrder` first, then the rest
|
|
8
|
-
* alphabetically).
|
|
9
|
+
* alphabetically). Metadata-only entries without a provider, model, or mix are
|
|
10
|
+
* not routing targets, so plugins never see them. Disabled profiles are
|
|
11
|
+
* included and flagged via
|
|
9
12
|
* {@link ModelProfileInfo.isDisabled}; weighted "mix" profiles are included and
|
|
10
13
|
* flagged via {@link ModelProfileInfo.isMix}, since a mix is itself a valid
|
|
11
14
|
* routing target (it resolves to one constituent per conversation).
|
|
@@ -20,6 +23,7 @@ export function getModelProfiles(): ModelProfileInfo[] {
|
|
|
20
23
|
for (const key of orderProfileKeys(profiles, llm.profileOrder)) {
|
|
21
24
|
const entry = profiles[key];
|
|
22
25
|
if (entry == null) continue;
|
|
26
|
+
if (!isDispatchableProfile(entry)) continue;
|
|
23
27
|
result.push({
|
|
24
28
|
key,
|
|
25
29
|
label: entry.label ?? key,
|