@vellumai/assistant 0.11.7 → 0.11.8-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +10 -10
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +1 -1
- package/docs/error-handling.md +1 -2
- package/docs/guardian-request-flow.md +51 -5
- package/docs/trusted-contact-access.md +13 -12
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-catalog/package.json +19 -0
- package/{src/avatar/character-components.ts → node_modules/@vellumai/avatar-catalog/src/catalog.ts} +12 -44
- package/node_modules/@vellumai/avatar-catalog/src/colors.ts +19 -0
- package/node_modules/@vellumai/avatar-catalog/src/index.ts +25 -0
- package/node_modules/@vellumai/avatar-catalog/src/types.ts +47 -0
- package/node_modules/@vellumai/avatar-catalog/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +43 -12
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/test-path-guard.test.ts +89 -0
- package/node_modules/@vellumai/environments/src/test-path-guard.ts +113 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +8 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +46 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +46 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +62 -21
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +53 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +11 -5
- package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +1 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/openapi.yaml +481 -9
- package/package.json +3 -1
- package/scripts/sync-llm-catalog.ts +3 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +16 -0
- package/src/__tests__/agent-loop-compacted-history-transform.test.ts +304 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +3 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +3 -0
- package/src/__tests__/anthropic-provider.test.ts +56 -0
- package/src/__tests__/approval-cascade.test.ts +0 -13
- package/src/__tests__/attachments-store.test.ts +117 -6
- package/src/__tests__/attachments.test.ts +122 -0
- package/src/__tests__/bundled-asset.test.ts +22 -1
- package/src/__tests__/channel-approval.test.ts +14 -7
- package/src/__tests__/channel-approvals.test.ts +3 -3
- package/src/__tests__/channel-delivery-store.test.ts +58 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +1 -0
- package/src/__tests__/channel-invite-transport.test.ts +1 -0
- package/src/__tests__/channel-readiness-slack-remote.test.ts +3 -3
- package/src/__tests__/compaction-events.test.ts +0 -12
- package/src/__tests__/compactor-call-site-logging.test.ts +7 -1
- package/src/__tests__/compactor-retained-image-validation.test.ts +15 -0
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/contacts-write.test.ts +141 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -13
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -12
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -13
- package/src/__tests__/conversation-agent-loop.test.ts +95 -13
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -13
- package/src/__tests__/conversation-load-history-repair.test.ts +418 -0
- package/src/__tests__/conversation-media-retry.test.ts +33 -2
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -12
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -13
- package/src/__tests__/conversation-queue.test.ts +4 -13
- package/src/__tests__/conversation-retry-route.test.ts +5 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-sight-frames.test.ts +615 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -13
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -13
- package/src/__tests__/conversation-speed-override.test.ts +0 -12
- package/src/__tests__/conversation-store-ephemeral.test.ts +0 -12
- package/src/__tests__/conversation-summarize-route.test.ts +5 -0
- package/src/__tests__/conversation-summarize-up-to.test.ts +102 -0
- package/src/__tests__/conversation-wait-for-idle.test.ts +0 -12
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -17
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -17
- package/src/__tests__/credential-health-service.test.ts +45 -0
- package/src/__tests__/credential-record-retire.test.ts +256 -0
- package/src/__tests__/credential-record-write-through.test.ts +8 -4
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +0 -4
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +0 -4
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +0 -8
- package/src/__tests__/delete-propagation.test.ts +121 -3
- package/src/__tests__/evict-conversations-for-reload.test.ts +46 -6
- package/src/__tests__/file-ops-service.test.ts +8 -3
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/guardian-action-service-scope.test.ts +118 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +299 -5
- package/src/__tests__/guardian-expiry-notifier.test.ts +117 -13
- package/src/__tests__/guardian-gateway-sim.test.ts +119 -0
- package/src/__tests__/guardian-gateway-sim.ts +56 -20
- package/src/__tests__/guardian-reply-router-answer-mode.test.ts +6 -0
- package/src/__tests__/guardian-request-status-sync.test.ts +162 -0
- package/src/__tests__/guardian-wait-controller.test.ts +6 -0
- package/src/__tests__/headless-browser-mode.test.ts +27 -0
- package/src/__tests__/helpers/mock-conversation.ts +2 -0
- package/src/__tests__/list-messages-system-card.test.ts +70 -0
- package/src/__tests__/llm-catalog-parity.test.ts +2 -0
- package/src/__tests__/managed-profile-guard.test.ts +2 -2
- package/src/__tests__/media-resolve-image-validation.test.ts +106 -0
- package/src/__tests__/media-stream-server-integration.test.ts +6 -0
- package/src/__tests__/non-member-access-request.test.ts +12 -0
- package/src/__tests__/notification-discord-adapter.test.ts +76 -4
- package/src/__tests__/oauth-commands-routes.test.ts +21 -0
- package/src/__tests__/openai-provider.test.ts +63 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +114 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +63 -1
- package/src/__tests__/persist-unsendable-image.test.ts +19 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +2 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -1
- package/src/__tests__/process-message-display-content.test.ts +1 -1
- package/src/__tests__/processing-flag-persist-failure.test.ts +0 -5
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +0 -17
- package/src/__tests__/qdrant-manager.test.ts +6 -0
- package/src/__tests__/reaction-persistence.test.ts +58 -62
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +67 -0
- package/src/__tests__/server-history-render.test.ts +22 -0
- package/src/__tests__/sight-frame-retention.test.ts +362 -0
- package/src/__tests__/slack-reaction-approvals.test.ts +44 -24
- package/src/__tests__/slack-reaction-guardian-approval.test.ts +29 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-notify-stale-rebuild.test.ts +85 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +4 -4
- package/src/__tests__/terminal-tools.test.ts +9 -9
- package/src/__tests__/tool-result-spool.test.ts +5 -8
- package/src/__tests__/usage-attribution.test.ts +26 -0
- package/src/acp/__tests__/acp-auth-marker-store.test.ts +191 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +262 -0
- package/src/acp/__tests__/acp-connect-card-state.test.ts +17 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +10 -3
- package/src/acp/acp-auth-marker-store.ts +196 -0
- package/src/acp/acp-claude-oauth.ts +154 -4
- package/src/acp/acp-connect-card-state.ts +49 -8
- package/src/acp/agent-process.ts +1 -0
- package/src/acp/auto-install.ts +1 -0
- package/src/acp/client-handler.ts +1 -0
- package/src/acp/prepare-agent-env.ts +79 -1
- package/src/acp/resolve-agent.ts +12 -0
- package/src/acp/session-manager.test.ts +183 -0
- package/src/acp/session-manager.ts +110 -6
- package/src/acp/types.ts +17 -0
- package/src/agent/attachments.ts +7 -2
- package/src/agent/loop.ts +59 -1
- package/src/api/events/contact-form-closed.ts +32 -0
- package/src/api/events/contact-record-request.ts +63 -0
- package/src/api/events/contact-request.ts +10 -3
- package/src/api/index.ts +20 -0
- package/src/api/responses/conversation-message.ts +20 -0
- package/src/api/responses/home.ts +101 -0
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-card-withdrawal.ts +127 -30
- package/src/approvals/guardian-decision-primitive.ts +9 -0
- package/src/approvals/guardian-expiry-notifier.ts +22 -16
- package/src/approvals/guardian-request-status-sync.ts +98 -0
- package/src/avatar/svg-compositor.ts +1 -1
- package/src/avatar/traits-png-sync.ts +1 -1
- package/src/bundler/app-compiler.ts +1 -0
- package/src/bundler/compiler-tools.ts +1 -0
- package/src/bundler/package-resolver.ts +1 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +183 -3
- package/src/calls/guardian-action-sweep.ts +6 -1
- package/src/calls/voice-session-bridge.ts +46 -1
- package/src/channels/__tests__/gateway-guardian-requests.test.ts +13 -18
- package/src/channels/gateway-guardian-requests.ts +37 -18
- package/src/cli/__tests__/contacts-record-prompt-cli.test.ts +329 -0
- package/src/cli/commands/__tests__/browser.test.ts +65 -1
- package/src/cli/commands/browser.ts +30 -10
- package/src/cli/commands/contacts.help.ts +173 -11
- package/src/cli/commands/contacts.ts +343 -4
- package/src/cli/commands/db/refresh.ts +1 -0
- package/src/cli/commands/oauth/index.help.ts +6 -3
- package/src/cli/commands/oauth/request.test.ts +37 -0
- package/src/cli/commands/oauth/request.ts +7 -6
- package/src/cli/lib/bundled-marketplace.json +1 -1
- package/src/cli/lib/install-from-github.ts +20 -19
- package/src/cli/lib/install-plugin-dependencies.ts +3 -5
- package/src/cli/lib/merge-plugin-tree.ts +1 -0
- package/src/cli/lib/publish-plugin.ts +4 -1
- package/src/config/__tests__/balanced-model-experiment.test.ts +63 -31
- package/src/config/__tests__/default-profile-catalog.test.ts +2 -2
- package/src/config/balanced-model-experiment.ts +15 -9
- package/src/config/bundled-skills/contacts/SKILL.md +61 -18
- package/src/config/default-profile-catalog.ts +12 -11
- package/src/config/feature-flag-registry.json +22 -13
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +13 -0
- package/src/config/schemas/live-voice.ts +7 -0
- package/src/config/schemas/sight.ts +45 -0
- package/src/config/skills.ts +2 -1
- package/src/contacts/__tests__/mirror-reconciler.test.ts +352 -0
- package/src/contacts/contact-store.ts +47 -4
- package/src/contacts/contacts-write.ts +5 -2
- package/src/contacts/mirror-reconciler.ts +210 -0
- package/src/contacts/mirror-tombstones.ts +42 -0
- package/src/contacts/notify-contacts-changed.ts +11 -2
- package/src/context/compactor.ts +47 -19
- package/src/context/tool-result-spool.ts +21 -14
- package/src/conversations/message-consolidation.ts +2 -2
- package/src/credential-health/credential-health-service.ts +37 -6
- package/src/daemon/conversation-agent-loop-handlers.ts +19 -2
- package/src/daemon/conversation-agent-loop.ts +100 -1
- package/src/daemon/conversation-media-retry.ts +11 -6
- package/src/daemon/conversation-messaging.ts +293 -30
- package/src/daemon/conversation-runtime-assembly.ts +67 -0
- package/src/daemon/conversation-sight-frames.ts +187 -0
- package/src/daemon/conversation-store.ts +18 -28
- package/src/daemon/conversation-surfaces.ts +12 -2
- package/src/daemon/conversation-tool-setup.ts +39 -10
- package/src/daemon/conversation.ts +159 -16
- package/src/daemon/daemon-control.ts +6 -17
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +10 -11
- package/src/daemon/install-assistant-command.ts +1 -0
- package/src/daemon/lifecycle.ts +10 -13
- package/src/daemon/message-types/sync.ts +4 -0
- package/src/daemon/process-message.ts +2 -17
- package/src/daemon/reaction-history-render.test.ts +162 -0
- package/src/daemon/reaction-history-render.ts +115 -0
- package/src/daemon/reaction-record.test.ts +162 -0
- package/src/daemon/reaction-record.ts +83 -0
- package/src/daemon/shutdown-handlers.ts +0 -2
- package/src/daemon/summarize-boundary.ts +12 -0
- package/src/daemon/video-thumbnail.ts +1 -1
- package/src/home/feed-types.ts +4 -0
- package/src/home/feed-writer.ts +25 -0
- package/src/live-voice/__tests__/live-voice-attach-frame.test.ts +679 -0
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +8 -5
- package/src/live-voice/__tests__/live-voice-photo.test.ts +637 -1
- package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +787 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +347 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +109 -0
- package/src/live-voice/live-voice-photo.ts +420 -65
- package/src/live-voice/live-voice-session.ts +311 -27
- package/src/live-voice/protocol.ts +147 -11
- package/src/messaging/provider-message-metadata.ts +7 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +113 -1
- package/src/messaging/providers/channel-transport.ts +37 -0
- package/src/messaging/providers/discord/api.ts +4 -4
- package/src/messaging/providers/discord/send.test.ts +177 -2
- package/src/messaging/providers/discord/send.ts +197 -10
- package/src/messaging/providers/discord/transport.ts +10 -0
- package/src/messaging/providers/index.ts +42 -0
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +129 -3
- package/src/messaging/providers/slack/__tests__/auth.test.ts +35 -0
- package/src/messaging/providers/slack/__tests__/message-content.test.ts +152 -0
- package/src/messaging/providers/slack/adapter.ts +112 -27
- package/src/messaging/providers/slack/api.test.ts +10 -0
- package/src/messaging/providers/slack/auth.ts +32 -6
- package/src/messaging/providers/slack/client.ts +0 -15
- package/src/messaging/providers/slack/message-content.ts +137 -0
- package/src/messaging/providers/slack/send.test.ts +43 -2
- package/src/messaging/providers/slack/send.ts +18 -5
- package/src/messaging/providers/slack/transport.ts +10 -0
- package/src/messaging/providers/slack/types.ts +21 -2
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -1
- package/src/messaging/providers/telegram-bot/send.test.ts +49 -2
- package/src/messaging/providers/telegram-bot/send.ts +41 -1
- package/src/messaging/providers/telegram-bot/transport.ts +10 -0
- package/src/messaging/providers/telegram-bot/withdraw.ts +13 -5
- package/src/messaging/reaction-envelopes.ts +79 -0
- package/src/monitoring/control.ts +2 -2
- package/src/monitoring/page-cache.ts +5 -1
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +360 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +8 -0
- package/src/notifications/adapters/discord.ts +62 -9
- package/src/notifications/conversation-pairing.ts +31 -4
- package/src/notifications/emit-signal.ts +8 -5
- package/src/notifications/guardian-delivery-recorder.ts +47 -6
- package/src/notifications/guardian-feed-projection.ts +492 -0
- package/src/notifications/home-feed-side-effect.ts +59 -11
- package/src/oauth/byo-connection.test.ts +22 -0
- package/src/oauth/byo-connection.ts +11 -5
- package/src/oauth/connection.ts +3 -2
- package/src/oauth/platform-connection.test.ts +35 -0
- package/src/oauth/platform-connection.ts +18 -13
- package/src/oauth/scope-utils.ts +48 -0
- package/src/oauth/seed-providers.ts +12 -0
- package/src/outbound-proxy/certs.ts +5 -4
- package/src/permissions/question-guardian-request.test.ts +7 -1
- package/src/persistence/attachments-store.ts +134 -31
- package/src/persistence/conversation-crud.ts +137 -4
- package/src/persistence/conversation-types.ts +109 -0
- package/src/persistence/db-connection.ts +3 -21
- package/src/persistence/delivery-crud.ts +42 -12
- package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +15 -2
- package/src/persistence/embeddings/embedding-local.ts +7 -112
- package/src/persistence/embeddings/qdrant-manager.ts +4 -1
- package/src/persistence/llm-request-log-store.ts +1 -1
- package/src/persistence/migrations/370-acp-session-history-auth-error-code.ts +36 -0
- package/src/persistence/migrations/371-acp-session-history-auth-error-credential.ts +40 -0
- package/src/persistence/migrations/372-create-acp-refused-credentials.ts +34 -0
- package/src/persistence/migrations/373-acp-auth-marker-index.ts +27 -0
- package/src/persistence/migrations/__tests__/370-acp-session-history-auth-error-code.test.ts +65 -0
- package/src/persistence/migrations/__tests__/371-acp-session-history-auth-error-credential.test.ts +68 -0
- package/src/persistence/migrations/__tests__/372-create-acp-refused-credentials.test.ts +64 -0
- package/src/persistence/migrations/__tests__/373-acp-auth-marker-index.test.ts +82 -0
- package/src/persistence/schema/acp.ts +25 -0
- package/src/persistence/steps.ts +8 -0
- package/src/platform/platform-patch-queue.test.ts +43 -41
- package/src/plugin-api/index.ts +12 -0
- package/src/plugins/defaults/compaction/window-manager.ts +2 -0
- package/src/plugins/defaults/image-recovery/recover.ts +12 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +166 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +3 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +3 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +53 -9
- package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +4 -7
- package/src/plugins/defaults/memory/src/memory-worker-routes.ts +1 -1
- package/src/plugins/defaults/memory/v1/job-handlers/index-maintenance.ts +15 -1
- package/src/plugins/defaults/memory/worker-control.ts +1 -2
- package/src/providers/content-block-schema.ts +1 -0
- package/src/providers/fireworks/client.ts +9 -1
- package/src/providers/gemini/client.ts +22 -15
- package/src/providers/media-resolve.ts +24 -10
- package/src/providers/model-catalog.ts +91 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -5
- package/src/providers/retry.ts +16 -11
- package/src/providers/types.ts +56 -0
- package/src/routes/__tests__/control.test.ts +4 -3
- package/src/routes/control.ts +1 -1
- package/src/routes/route-host-client.ts +1 -1
- package/src/runtime/AGENTS.md +30 -12
- package/src/runtime/__tests__/agent-wake.test.ts +71 -6
- package/src/runtime/__tests__/guardian-form-registry.test.ts +242 -0
- package/src/runtime/access-request-helper.ts +15 -7
- package/src/runtime/agent-wake.ts +16 -4
- package/src/runtime/channel-approval-types.ts +8 -5
- package/src/runtime/channel-invite-transport.ts +2 -0
- package/src/runtime/channel-invite-transports/discord.ts +27 -0
- package/src/runtime/channel-readiness-service.ts +4 -4
- package/src/runtime/channel-reply-delivery.ts +72 -28
- package/src/runtime/guardian-action-service.ts +9 -5
- package/src/runtime/guardian-form-registry.ts +256 -0
- package/src/runtime/guardian-reply-router.ts +33 -14
- package/src/runtime/invite-instruction-generator.ts +14 -2
- package/src/runtime/migrations/vbundle-metadata-merge.ts +4 -7
- package/src/runtime/no-response.ts +13 -74
- package/src/runtime/routes/__tests__/acp-routes.test.ts +527 -0
- package/src/runtime/routes/__tests__/app-management-shared-apps.test.ts +74 -0
- package/src/runtime/routes/__tests__/contact-form-kinds.test.ts +22 -0
- package/src/runtime/routes/__tests__/contact-record-prompt-route.test.ts +303 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +6 -0
- package/src/runtime/routes/__tests__/route-host-worker-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +5 -6
- package/src/runtime/routes/acp-routes-list.test.ts +58 -0
- package/src/runtime/routes/acp-routes.ts +253 -47
- package/src/runtime/routes/app-management-routes.ts +80 -47
- package/src/runtime/routes/approval-routes.ts +9 -0
- package/src/runtime/routes/archive-utils.ts +6 -1
- package/src/runtime/routes/avatar-routes.ts +1 -1
- package/src/runtime/routes/channel-route-shared.ts +18 -20
- package/src/runtime/routes/contact-prompt-routes.ts +271 -105
- package/src/runtime/routes/conversation-routes.ts +41 -32
- package/src/runtime/routes/credential-prompt-routes.ts +10 -1
- package/src/runtime/routes/debug-bash-routes.ts +1 -0
- package/src/runtime/routes/guardian-expiry-sweep.ts +89 -31
- package/src/runtime/routes/guardian-form-routes.ts +115 -0
- package/src/runtime/routes/inbound-message-handler.ts +82 -29
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +6 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +6 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +6 -1
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +50 -17
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +97 -94
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/channel.ts +39 -18
- package/src/runtime/routes/migration-routes.ts +1 -3
- package/src/runtime/routes/monitoring-routes.ts +0 -3
- package/src/runtime/routes/oauth-commands-routes.ts +18 -5
- package/src/runtime/routes/route-host-worker-routes.ts +1 -1
- package/src/runtime/routes/schedule-worker-routes.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +4 -0
- package/src/schedule/run-script.ts +1 -0
- package/src/schedule/scheduler.ts +1 -1
- package/src/schedule/worker-control.ts +2 -2
- package/src/security/oauth2.ts +2 -11
- package/src/security/secure-keys.ts +55 -8
- package/src/skills/catalog-install.ts +7 -4
- package/src/skills/clawhub.ts +1 -0
- package/src/skills/inline-command-runner.ts +1 -0
- package/src/stt/__tests__/room-noise-floor.test.ts +108 -0
- package/src/stt/room-noise-floor.ts +140 -0
- package/src/subagent/notify.ts +49 -3
- package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
- package/src/tools/acp/spawn.ts +2 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +9 -0
- package/src/tools/browser/browser-execution.ts +10 -43
- package/src/tools/browser/browser-manager.ts +1 -0
- package/src/tools/browser/browser-status-constants.ts +6 -5
- package/src/tools/browser/runtime-check.ts +2 -0
- package/src/tools/channel/react-to-message.test.ts +193 -0
- package/src/tools/channel/react-to-message.ts +159 -0
- package/src/tools/credentials/metadata-store.ts +115 -19
- package/src/tools/credentials/store.ts +0 -2
- package/src/tools/filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +11 -38
- package/src/tools/shared/filesystem/file-ops-service.ts +10 -7
- package/src/tools/shared/shell-output.test.ts +104 -0
- package/src/tools/shared/shell-output.ts +112 -39
- package/src/tools/skills/sandbox-runner.ts +1 -0
- package/src/tools/system/request-permission.test.ts +34 -0
- package/src/tools/system/request-permission.ts +29 -5
- package/src/tools/terminal/shell.test.ts +63 -0
- package/src/tools/terminal/shell.ts +12 -44
- package/src/tools/tool-input-schemas.ts +2 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/usage/attribution.ts +12 -4
- package/src/util/__tests__/worker-ownership.test.ts +77 -0
- package/src/util/__tests__/worker-process-command.test.ts +246 -1
- package/src/util/bun-runtime.ts +44 -13
- package/src/util/bundled-asset.ts +9 -1
- package/src/util/disk-usage.ts +1 -0
- package/src/util/file-use.ts +4 -1
- package/src/util/guardian-form-timeouts.ts +50 -0
- package/src/util/image-conversion.ts +1 -0
- package/src/util/oauth-request-body.test.ts +59 -0
- package/src/util/oauth-request-body.ts +56 -5
- package/src/util/platform.ts +69 -73
- package/src/util/process-table.ts +16 -5
- package/src/util/read-stdin.ts +5 -0
- package/src/util/spawn.ts +1 -0
- package/src/util/sqlite3-runtime.ts +25 -5
- package/src/util/worker-ownership.ts +193 -0
- package/src/util/worker-process.ts +375 -37
- package/src/workspace/git-service.ts +4 -12
- package/src/workspace/migrations/010-app-dir-rename.ts +1 -0
|
@@ -182,6 +182,43 @@ describe("oauth request body encoding", () => {
|
|
|
182
182
|
);
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
+
test("reads a binary @file as a Buffer without UTF-8 replacement", async () => {
|
|
186
|
+
const filePath = join(tempDir, "upload.bin");
|
|
187
|
+
writeFileSync(filePath, PNG_MAGIC);
|
|
188
|
+
|
|
189
|
+
const parsed = readBodyData(`@${filePath}`, {
|
|
190
|
+
"Content-Type": "application/octet-stream",
|
|
191
|
+
});
|
|
192
|
+
expect(Buffer.isBuffer(parsed)).toBe(true);
|
|
193
|
+
expect(Buffer.from(parsed as Uint8Array).equals(PNG_MAGIC)).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("forwards a binary @file to the route handler as a Buffer", async () => {
|
|
197
|
+
const filePath = join(tempDir, "report.pdf");
|
|
198
|
+
writeFileSync(filePath, PNG_MAGIC);
|
|
199
|
+
|
|
200
|
+
const { exitCode } = await runRequestCommand([
|
|
201
|
+
"--provider",
|
|
202
|
+
"google",
|
|
203
|
+
"-s",
|
|
204
|
+
"-X",
|
|
205
|
+
"POST",
|
|
206
|
+
"-H",
|
|
207
|
+
"Content-Type: application/pdf",
|
|
208
|
+
"-d",
|
|
209
|
+
`@${filePath}`,
|
|
210
|
+
"https://www.googleapis.com/upload/drive/v3/files?uploadType=media",
|
|
211
|
+
]);
|
|
212
|
+
|
|
213
|
+
expect(exitCode).toBe(0);
|
|
214
|
+
expect(handleRequestCalls).toHaveLength(1);
|
|
215
|
+
const call = handleRequestCalls[0] as { body: { parsed_data: unknown } };
|
|
216
|
+
expect(Buffer.isBuffer(call.body.parsed_data)).toBe(true);
|
|
217
|
+
expect(
|
|
218
|
+
Buffer.from(call.body.parsed_data as Uint8Array).equals(PNG_MAGIC),
|
|
219
|
+
).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
185
222
|
test("forwards a multipart body to the route handler as a string", async () => {
|
|
186
223
|
const { exitCode } = await runRequestCommand([
|
|
187
224
|
"--provider",
|
|
@@ -5,9 +5,10 @@ import type { Command } from "commander";
|
|
|
5
5
|
import { exitFromIpcResult } from "../../../ipc/cli-client.js";
|
|
6
6
|
import {
|
|
7
7
|
findContentTypeHeader,
|
|
8
|
+
parseRequestBodyBytes,
|
|
8
9
|
parseRequestBodyData,
|
|
9
10
|
} from "../../../util/oauth-request-body.js";
|
|
10
|
-
import {
|
|
11
|
+
import { readStdinBytesSync } from "../../../util/read-stdin.js";
|
|
11
12
|
import { subcommand } from "../../lib/cli-command-help.js";
|
|
12
13
|
import { shouldOutputJson, writeOutput } from "../../output.js";
|
|
13
14
|
|
|
@@ -49,8 +50,8 @@ function parseHeader(raw: string): [string, string] {
|
|
|
49
50
|
*
|
|
50
51
|
* A non-JSON `Content-Type` keeps the payload as the exact string the caller
|
|
51
52
|
* gave, so multipart, XML, and form-encoded bodies reach the provider
|
|
52
|
-
* unchanged. Files are read as
|
|
53
|
-
* (
|
|
53
|
+
* unchanged. Files and stdin are read as raw bytes: valid UTF-8 stays text
|
|
54
|
+
* (or JSON), and anything else travels as a Buffer.
|
|
54
55
|
*/
|
|
55
56
|
export function readBodyData(
|
|
56
57
|
data: string,
|
|
@@ -59,12 +60,12 @@ export function readBodyData(
|
|
|
59
60
|
const contentType = findContentTypeHeader(headers);
|
|
60
61
|
|
|
61
62
|
if (data === "@-") {
|
|
62
|
-
return
|
|
63
|
+
return parseRequestBodyBytes(readStdinBytesSync(), contentType);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
if (data.startsWith("@")) {
|
|
66
67
|
const filePath = data.slice(1);
|
|
67
|
-
return
|
|
68
|
+
return parseRequestBodyBytes(readFileSync(filePath), contentType);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
return parseRequestBodyData(data, contentType);
|
|
@@ -90,7 +91,7 @@ export function registerRequestCommand(oauth: Command): void {
|
|
|
90
91
|
)
|
|
91
92
|
.option(
|
|
92
93
|
"-d, --data <data>",
|
|
93
|
-
"Request body: inline JSON, @filename, or @- for stdin. Sent raw when Content-Type is not JSON",
|
|
94
|
+
"Request body: inline JSON, @filename, or @- for stdin. Sent raw when Content-Type is not JSON. Files keep their original bytes, including binary",
|
|
94
95
|
)
|
|
95
96
|
.option("-G, --get", "Force GET; body data becomes query params")
|
|
96
97
|
.option("-I, --head", "Send a HEAD request")
|
|
@@ -219,7 +219,7 @@
|
|
|
219
219
|
"source": {
|
|
220
220
|
"source": "github",
|
|
221
221
|
"repo": "vellum-ai/imessage",
|
|
222
|
-
"ref": "
|
|
222
|
+
"ref": "01701eb1be4074f99371af90fc4340fdd1674efb"
|
|
223
223
|
},
|
|
224
224
|
"description": "iMessage and SMS channel for the Vellum assistant, backed by a Photon line.",
|
|
225
225
|
"category": "productivity",
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
writeFileSync,
|
|
37
37
|
} from "node:fs";
|
|
38
38
|
import { tmpdir } from "node:os";
|
|
39
|
-
import { dirname, join, resolve, sep } from "node:path";
|
|
39
|
+
import { delimiter, dirname, join, resolve, sep } from "node:path";
|
|
40
40
|
import { promisify } from "node:util";
|
|
41
41
|
|
|
42
42
|
import {
|
|
@@ -46,7 +46,12 @@ import {
|
|
|
46
46
|
stripPreservedUserState,
|
|
47
47
|
} from "../../plugins/plugin-tree-walk.js";
|
|
48
48
|
import { ensureBun } from "../../util/bun-runtime.js";
|
|
49
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
addToPathEnv,
|
|
51
|
+
getExtraToolPathDirs,
|
|
52
|
+
getWorkspacePluginsDir,
|
|
53
|
+
isWindows,
|
|
54
|
+
} from "../../util/platform.js";
|
|
50
55
|
import type { FetchLike } from "./fetch-like.js";
|
|
51
56
|
import {
|
|
52
57
|
type DependencyInstaller,
|
|
@@ -1254,23 +1259,23 @@ export const defaultPostinstallRunner: PostinstallRunner = async ({
|
|
|
1254
1259
|
timeout: POSTINSTALL_TIMEOUT_MS,
|
|
1255
1260
|
maxBuffer: 16 * 1024 * 1024,
|
|
1256
1261
|
env: pluginPostinstallEnv(bun),
|
|
1262
|
+
windowsHide: true,
|
|
1257
1263
|
});
|
|
1258
1264
|
};
|
|
1259
1265
|
|
|
1260
1266
|
function pluginPostinstallEnv(bun: string): NodeJS.ProcessEnv {
|
|
1267
|
+
const systemDirs = isWindows()
|
|
1268
|
+
? [process.env.SystemRoot ?? "C:\\Windows"]
|
|
1269
|
+
: [...getExtraToolPathDirs(), "/usr/bin", "/bin"];
|
|
1261
1270
|
const env: NodeJS.ProcessEnv = {
|
|
1262
|
-
PATH: [
|
|
1263
|
-
dirname(bun),
|
|
1264
|
-
"/opt/homebrew/bin",
|
|
1265
|
-
"/usr/local/bin",
|
|
1266
|
-
"/usr/bin",
|
|
1267
|
-
"/bin",
|
|
1268
|
-
]
|
|
1269
|
-
.filter(Boolean)
|
|
1270
|
-
.join(":"),
|
|
1271
|
+
PATH: [dirname(bun), ...systemDirs].join(delimiter),
|
|
1271
1272
|
};
|
|
1272
|
-
|
|
1273
|
-
|
|
1273
|
+
for (const key of isWindows()
|
|
1274
|
+
? ["USERPROFILE", "APPDATA", "LOCALAPPDATA", "TEMP", "SystemRoot"]
|
|
1275
|
+
: ["HOME"]) {
|
|
1276
|
+
if (process.env[key]) {
|
|
1277
|
+
env[key] = process.env[key];
|
|
1278
|
+
}
|
|
1274
1279
|
}
|
|
1275
1280
|
return env;
|
|
1276
1281
|
}
|
|
@@ -1373,6 +1378,7 @@ export const defaultGitRunner: GitRunner = async (args, opts) => {
|
|
|
1373
1378
|
timeout: GIT_TIMEOUT_MS,
|
|
1374
1379
|
maxBuffer: 64 * 1024 * 1024,
|
|
1375
1380
|
env: pluginGitEnv(),
|
|
1381
|
+
windowsHide: true,
|
|
1376
1382
|
});
|
|
1377
1383
|
return { stdout };
|
|
1378
1384
|
};
|
|
@@ -1385,12 +1391,7 @@ function pluginGitEnv(): NodeJS.ProcessEnv {
|
|
|
1385
1391
|
}
|
|
1386
1392
|
}
|
|
1387
1393
|
env.GIT_TERMINAL_PROMPT = "0";
|
|
1388
|
-
|
|
1389
|
-
const current = (env.PATH ?? "").split(":").filter(Boolean);
|
|
1390
|
-
const missing = extraPaths.filter((p) => !current.includes(p));
|
|
1391
|
-
if (missing.length > 0) {
|
|
1392
|
-
env.PATH = [...current, ...missing].join(":");
|
|
1393
|
-
}
|
|
1394
|
+
addToPathEnv(env, getExtraToolPathDirs(), "back");
|
|
1394
1395
|
return env;
|
|
1395
1396
|
}
|
|
1396
1397
|
|
|
@@ -59,6 +59,7 @@ import { promisify } from "node:util";
|
|
|
59
59
|
|
|
60
60
|
import { ensureBun } from "../../util/bun-runtime.js";
|
|
61
61
|
import { getLogger } from "../../util/logger.js";
|
|
62
|
+
import { addToPathEnv } from "../../util/platform.js";
|
|
62
63
|
|
|
63
64
|
const execFileAsync = promisify(execFile);
|
|
64
65
|
|
|
@@ -318,6 +319,7 @@ export const defaultDependencyInstaller: DependencyInstaller = async ({
|
|
|
318
319
|
timeout: DEPENDENCY_INSTALL_TIMEOUT_MS,
|
|
319
320
|
maxBuffer: 32 * 1024 * 1024,
|
|
320
321
|
env: dependencyInstallEnv(bun),
|
|
322
|
+
windowsHide: true,
|
|
321
323
|
});
|
|
322
324
|
};
|
|
323
325
|
|
|
@@ -331,10 +333,6 @@ export const defaultDependencyInstaller: DependencyInstaller = async ({
|
|
|
331
333
|
*/
|
|
332
334
|
function dependencyInstallEnv(bun: string): NodeJS.ProcessEnv {
|
|
333
335
|
const env: NodeJS.ProcessEnv = { ...process.env };
|
|
334
|
-
|
|
335
|
-
const current = (env.PATH ?? "").split(":").filter(Boolean);
|
|
336
|
-
if (!current.includes(bunDir)) {
|
|
337
|
-
env.PATH = [bunDir, ...current].join(":");
|
|
338
|
-
}
|
|
336
|
+
addToPathEnv(env, [dirname(bun)]);
|
|
339
337
|
return env;
|
|
340
338
|
}
|
|
@@ -199,7 +199,10 @@ export function validatePluginForPublish(dir: string): PublishValidation {
|
|
|
199
199
|
// ---------------------------------------------------------------------------
|
|
200
200
|
|
|
201
201
|
async function runGit(dir: string, args: string[]): Promise<string> {
|
|
202
|
-
const { stdout } = await execFileAsync("git", args, {
|
|
202
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
203
|
+
cwd: dir,
|
|
204
|
+
windowsHide: true,
|
|
205
|
+
});
|
|
203
206
|
return stdout.trim();
|
|
204
207
|
}
|
|
205
208
|
|
|
@@ -24,15 +24,16 @@ import {
|
|
|
24
24
|
type ProfileEntry,
|
|
25
25
|
} from "../schemas/llm.js";
|
|
26
26
|
|
|
27
|
-
// The `experiment-balanced-model-2026-08-
|
|
27
|
+
// The `experiment-balanced-model-2026-08-31` arm repoints the model the managed
|
|
28
28
|
// Balanced profile resolves to. Both the runtime resolver and the client-facing
|
|
29
29
|
// profile listing go through `defaultProfileBodyForProvider`, so both are
|
|
30
30
|
// exercised here: a disagreement between them shows the user one model beside a
|
|
31
31
|
// profile that runs another.
|
|
32
32
|
|
|
33
33
|
const FLAG = BALANCED_MODEL_EXPERIMENT_FLAG_KEY;
|
|
34
|
-
const SHIPPED_MODEL = "accounts/fireworks/models/glm-
|
|
35
|
-
const
|
|
34
|
+
const SHIPPED_MODEL = "accounts/fireworks/models/glm-5p3-flash";
|
|
35
|
+
const GLM_53_MODEL = "accounts/fireworks/models/glm-5p3";
|
|
36
|
+
const GLM_52_MODEL = "accounts/fireworks/models/glm-5p2";
|
|
36
37
|
|
|
37
38
|
function setArm(value: boolean | string): void {
|
|
38
39
|
setOverridesForTesting({ [FLAG]: value });
|
|
@@ -57,22 +58,22 @@ afterEach(() => {
|
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
describe("balanced-model experiment arms", () => {
|
|
60
|
-
test("
|
|
61
|
-
setArm("
|
|
61
|
+
test("glm-5p3 repoints mainAgent at GLM 5.3 on the managed connection", () => {
|
|
62
|
+
setArm("glm-5p3");
|
|
62
63
|
const resolved = resolveCallSiteConfig(
|
|
63
64
|
"mainAgent",
|
|
64
65
|
llmWithActiveBalanced(),
|
|
65
66
|
);
|
|
66
|
-
expect(resolved.model).toBe(
|
|
67
|
+
expect(resolved.model).toBe(GLM_53_MODEL);
|
|
67
68
|
expect(resolved.provider).toBe("vellum");
|
|
68
|
-
expect(getManagedUpstream(
|
|
69
|
+
expect(getManagedUpstream(GLM_53_MODEL)).toBe("fireworks");
|
|
69
70
|
|
|
70
71
|
const entry = resolveDefaultProfileForProvider(
|
|
71
72
|
undefined,
|
|
72
73
|
"balanced",
|
|
73
74
|
managed,
|
|
74
75
|
);
|
|
75
|
-
expect(entry?.model).toBe(
|
|
76
|
+
expect(entry?.model).toBe(GLM_53_MODEL);
|
|
76
77
|
expect(entry?.provider).toBe("vellum");
|
|
77
78
|
// Routing-identity providers derive their connection per request.
|
|
78
79
|
expect(entry?.provider_connection).toBeUndefined();
|
|
@@ -85,12 +86,12 @@ describe("balanced-model experiment arms", () => {
|
|
|
85
86
|
"mainAgent",
|
|
86
87
|
llmWithActiveBalanced(),
|
|
87
88
|
);
|
|
88
|
-
expect(resolved.model).toBe(
|
|
89
|
+
expect(resolved.model).toBe(GLM_52_MODEL);
|
|
89
90
|
expect(resolved.provider).toBe("vellum");
|
|
90
91
|
|
|
91
|
-
const upstream = getManagedUpstream(
|
|
92
|
+
const upstream = getManagedUpstream(GLM_52_MODEL);
|
|
92
93
|
expect(upstream).toBe("fireworks");
|
|
93
|
-
const cap = catalogMaxOutputTokens(upstream as string,
|
|
94
|
+
const cap = catalogMaxOutputTokens(upstream as string, GLM_52_MODEL);
|
|
94
95
|
expect(cap).toBeDefined();
|
|
95
96
|
expect(resolved.maxTokens).toBeLessThanOrEqual(cap as number);
|
|
96
97
|
// The shipped token budget stands.
|
|
@@ -99,19 +100,26 @@ describe("balanced-model experiment arms", () => {
|
|
|
99
100
|
);
|
|
100
101
|
});
|
|
101
102
|
|
|
103
|
+
test("the glm-5p3-flash arm names the shipped pin", () => {
|
|
104
|
+
setArm("glm-5p3-flash");
|
|
105
|
+
expect(
|
|
106
|
+
resolveDefaultProfileForProvider(undefined, "balanced", managed)?.model,
|
|
107
|
+
).toBe(SHIPPED_MODEL);
|
|
108
|
+
});
|
|
109
|
+
|
|
102
110
|
test("an arm changes only the model of the managed balanced body", () => {
|
|
103
|
-
setArm("
|
|
111
|
+
setArm("glm-5p2");
|
|
104
112
|
const entry = resolveDefaultProfileForProvider(
|
|
105
113
|
undefined,
|
|
106
114
|
"balanced",
|
|
107
115
|
managed,
|
|
108
116
|
);
|
|
109
117
|
const shipped = CODE_DEFAULT_PROFILE_ENTRIES.balanced;
|
|
110
|
-
expect(entry).toEqual({ ...shipped, model:
|
|
118
|
+
expect(entry).toEqual({ ...shipped, model: GLM_52_MODEL });
|
|
111
119
|
});
|
|
112
120
|
|
|
113
121
|
test("the other default profiles are untouched by an arm", () => {
|
|
114
|
-
setArm("
|
|
122
|
+
setArm("glm-5p2");
|
|
115
123
|
for (const key of [
|
|
116
124
|
"quality-optimized",
|
|
117
125
|
"cost-optimized",
|
|
@@ -124,7 +132,7 @@ describe("balanced-model experiment arms", () => {
|
|
|
124
132
|
});
|
|
125
133
|
|
|
126
134
|
test("every pinned arm model is managed-routable and in its upstream catalog", () => {
|
|
127
|
-
for (const model of [
|
|
135
|
+
for (const model of [SHIPPED_MODEL, GLM_53_MODEL, GLM_52_MODEL]) {
|
|
128
136
|
const upstream = getManagedUpstream(model);
|
|
129
137
|
expect(upstream).not.toBeNull();
|
|
130
138
|
expect(isModelInCatalog(upstream as string, model)).toBe(true);
|
|
@@ -136,7 +144,10 @@ describe("balanced-model experiment fallbacks", () => {
|
|
|
136
144
|
const shippedCases: [string, () => void][] = [
|
|
137
145
|
["the flag is unset", () => clearCachedOverrides()],
|
|
138
146
|
["no override is present", () => setOverridesForTesting({})],
|
|
147
|
+
// Arms of the retired 2026-08-06 experiment are no longer pinned, so a
|
|
148
|
+
// stale value cannot strand an install on a model this test no longer runs.
|
|
139
149
|
["the arm is control", () => setArm("control")],
|
|
150
|
+
["the arm is terra", () => setArm("terra")],
|
|
140
151
|
["the arm is an unknown string", () => setArm("gpt-9-does-not-exist")],
|
|
141
152
|
["the arm is the empty string", () => setArm("")],
|
|
142
153
|
["the value is boolean true", () => setArm(true)],
|
|
@@ -162,11 +173,11 @@ describe("balanced-model experiment fallbacks", () => {
|
|
|
162
173
|
});
|
|
163
174
|
}
|
|
164
175
|
|
|
165
|
-
test("the shipped catalog body is the
|
|
176
|
+
test("the shipped catalog body is the glm-5p3-flash arm", () => {
|
|
166
177
|
expect(CODE_DEFAULT_PROFILE_ENTRIES.balanced.model).toBe(SHIPPED_MODEL);
|
|
167
178
|
});
|
|
168
179
|
|
|
169
|
-
test("the registry default
|
|
180
|
+
test("the registry default names the shipped pin, which the override-only flag read assumes", () => {
|
|
170
181
|
const registryPath = join(
|
|
171
182
|
import.meta.dirname,
|
|
172
183
|
"..",
|
|
@@ -188,14 +199,35 @@ describe("balanced-model experiment fallbacks", () => {
|
|
|
188
199
|
const flag = registry.flags.find((entry) => entry.key === FLAG);
|
|
189
200
|
expect(flag).toBeDefined();
|
|
190
201
|
expect(flag?.scope).toBe("assistant");
|
|
191
|
-
expect(flag?.defaultEnabled).toBe("
|
|
192
|
-
expect(flag?.values).toEqual(["
|
|
202
|
+
expect(flag?.defaultEnabled).toBe("glm-5p3-flash");
|
|
203
|
+
expect(flag?.values).toEqual(["glm-5p3-flash", "glm-5p3", "glm-5p2"]);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("the retired experiment's flag is gone from the registry", () => {
|
|
207
|
+
const registryPath = join(
|
|
208
|
+
import.meta.dirname,
|
|
209
|
+
"..",
|
|
210
|
+
"..",
|
|
211
|
+
"..",
|
|
212
|
+
"..",
|
|
213
|
+
"meta",
|
|
214
|
+
"feature-flags",
|
|
215
|
+
"feature-flag-registry.json",
|
|
216
|
+
);
|
|
217
|
+
const registry = JSON.parse(readFileSync(registryPath, "utf-8")) as {
|
|
218
|
+
flags: { key: string }[];
|
|
219
|
+
};
|
|
220
|
+
expect(
|
|
221
|
+
registry.flags.some(
|
|
222
|
+
(entry) => entry.key === "experiment-balanced-model-2026-08-06",
|
|
223
|
+
),
|
|
224
|
+
).toBe(false);
|
|
193
225
|
});
|
|
194
226
|
});
|
|
195
227
|
|
|
196
228
|
describe("balanced-model experiment boundaries", () => {
|
|
197
229
|
test("a user-owned balanced shadow wins over the arm", () => {
|
|
198
|
-
setArm("
|
|
230
|
+
setArm("glm-5p2");
|
|
199
231
|
const profiles: Record<string, ProfileEntry> = {
|
|
200
232
|
balanced: { source: "user", provider: "openai", model: "gpt-5.5" },
|
|
201
233
|
};
|
|
@@ -209,7 +241,7 @@ describe("balanced-model experiment boundaries", () => {
|
|
|
209
241
|
});
|
|
210
242
|
|
|
211
243
|
test("a managed-source stub still resolves to the arm's body", () => {
|
|
212
|
-
setArm("
|
|
244
|
+
setArm("glm-5p2");
|
|
213
245
|
const profiles: Record<string, ProfileEntry> = {
|
|
214
246
|
balanced: { source: "managed", label: "My Balanced" },
|
|
215
247
|
};
|
|
@@ -218,12 +250,12 @@ describe("balanced-model experiment boundaries", () => {
|
|
|
218
250
|
"balanced",
|
|
219
251
|
managed,
|
|
220
252
|
);
|
|
221
|
-
expect(entry?.model).toBe(
|
|
253
|
+
expect(entry?.model).toBe(GLM_52_MODEL);
|
|
222
254
|
expect(entry?.label).toBe("My Balanced");
|
|
223
255
|
});
|
|
224
256
|
|
|
225
257
|
test("the chatgpt and BYOK columns stay out of the experiment", () => {
|
|
226
|
-
setArm("
|
|
258
|
+
setArm("glm-5p2");
|
|
227
259
|
for (const provider of ["anthropic", "openai", "chatgpt"] as const) {
|
|
228
260
|
const armed = resolveDefaultProfileForProvider(undefined, "balanced", {
|
|
229
261
|
provider,
|
|
@@ -234,37 +266,37 @@ describe("balanced-model experiment boundaries", () => {
|
|
|
234
266
|
});
|
|
235
267
|
expect(armed).toEqual(shipped as ProfileEntry);
|
|
236
268
|
expect(armed?.provider).toBe(provider);
|
|
237
|
-
setArm("
|
|
269
|
+
setArm("glm-5p2");
|
|
238
270
|
}
|
|
239
271
|
});
|
|
240
272
|
|
|
241
273
|
test("an install with no defaultProvider still picks up the arm", () => {
|
|
242
|
-
setArm("
|
|
274
|
+
setArm("glm-5p2");
|
|
243
275
|
expect(
|
|
244
276
|
resolveDefaultProfileForProvider(undefined, "balanced", null)?.model,
|
|
245
|
-
).toBe(
|
|
277
|
+
).toBe(GLM_52_MODEL);
|
|
246
278
|
});
|
|
247
279
|
});
|
|
248
280
|
|
|
249
281
|
describe("client-facing profile listing", () => {
|
|
250
282
|
test("reports the arm's model so the UI matches what runs", () => {
|
|
251
|
-
setArm("
|
|
283
|
+
setArm("glm-5p2");
|
|
252
284
|
const profiles = getEffectiveProfilesForProvider(undefined, managed);
|
|
253
|
-
expect(profiles.balanced?.model).toBe(
|
|
285
|
+
expect(profiles.balanced?.model).toBe(GLM_52_MODEL);
|
|
254
286
|
expect(profiles["quality-optimized"]?.model).toBe(
|
|
255
287
|
CODE_DEFAULT_PROFILE_ENTRIES["quality-optimized"].model,
|
|
256
288
|
);
|
|
257
289
|
});
|
|
258
290
|
|
|
259
|
-
test("reports the shipped model on
|
|
260
|
-
setArm("
|
|
291
|
+
test("reports the shipped model on an arm this build does not know", () => {
|
|
292
|
+
setArm("terra");
|
|
261
293
|
expect(
|
|
262
294
|
getEffectiveProfilesForProvider(undefined, managed).balanced?.model,
|
|
263
295
|
).toBe(SHIPPED_MODEL);
|
|
264
296
|
});
|
|
265
297
|
|
|
266
298
|
test("agrees with the runtime resolver on every arm", () => {
|
|
267
|
-
for (const arm of ["
|
|
299
|
+
for (const arm of ["glm-5p3-flash", "glm-5p3", "glm-5p2", "nonsense"]) {
|
|
268
300
|
setArm(arm);
|
|
269
301
|
const listed = getEffectiveProfilesForProvider(undefined, managed)
|
|
270
302
|
.balanced?.model;
|
|
@@ -59,9 +59,9 @@ describe("getEffectiveProfiles", () => {
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
test("the managed Balanced profile routes GLM 5.
|
|
62
|
+
test("the managed Balanced profile routes GLM 5.3 Flash through Fireworks", () => {
|
|
63
63
|
const balanced = CODE_DEFAULT_PROFILE_ENTRIES.balanced;
|
|
64
|
-
expect(balanced.model).toBe("accounts/fireworks/models/glm-
|
|
64
|
+
expect(balanced.model).toBe("accounts/fireworks/models/glm-5p3-flash");
|
|
65
65
|
expect(resolveRoutingIdentity(balanced.provider, balanced.model)).toEqual({
|
|
66
66
|
connectionName: "vellum",
|
|
67
67
|
expectedProvider: "fireworks",
|
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Flag read for the managed Balanced profile's model A/B test.
|
|
3
3
|
*
|
|
4
|
-
* `experiment-balanced-model-2026-08-
|
|
4
|
+
* `experiment-balanced-model-2026-08-31` is a multivariate LaunchDarkly flag
|
|
5
5
|
* whose arm repoints the model the managed (`vellum`) implementation of the
|
|
6
6
|
* `balanced` default profile resolves to. The arm to model pins live beside
|
|
7
7
|
* the profile bodies in `default-profile-catalog.ts`, which validates their
|
|
8
8
|
* managed routability at load time; this module owns only the flag read.
|
|
9
9
|
*
|
|
10
|
+
* A flag key per experiment, rather than new variations on the previous one,
|
|
11
|
+
* is what keeps a readout honest: the activation cohort query keys a user on
|
|
12
|
+
* their first-ever assignment for a flag, so every user the 2026-08-06 test
|
|
13
|
+
* already stamped would stay pinned to that arm forever and never enter a new
|
|
14
|
+
* arm on the same key.
|
|
15
|
+
*
|
|
10
16
|
* The read goes straight to the override cache rather than through
|
|
11
17
|
* `assistant-feature-flags.ts`. `feature-flag-cache.ts` is a stdlib-only leaf,
|
|
12
18
|
* so the profile catalog stays free of the pino logger and the gateway IPC
|
|
13
19
|
* client that resolver would pull into its import graph, and the registry
|
|
14
|
-
* declares `defaultEnabled: "
|
|
15
|
-
* shipped body an absent override
|
|
16
|
-
* pinned by `__tests__/balanced-model-experiment.test.ts` so it
|
|
20
|
+
* declares `defaultEnabled: "glm-5p3-flash"` for this flag, which pins the same
|
|
21
|
+
* model the shipped body an absent override resolves to already carries. That
|
|
22
|
+
* equivalence is pinned by `__tests__/balanced-model-experiment.test.ts` so it
|
|
23
|
+
* cannot drift.
|
|
17
24
|
*/
|
|
18
25
|
|
|
19
26
|
import { getCachedOverrides } from "./feature-flag-cache.js";
|
|
20
27
|
|
|
21
28
|
export const BALANCED_MODEL_EXPERIMENT_FLAG_KEY =
|
|
22
|
-
"experiment-balanced-model-2026-08-
|
|
29
|
+
"experiment-balanced-model-2026-08-31";
|
|
23
30
|
|
|
24
31
|
/**
|
|
25
32
|
* The experiment arm in force, or `undefined` when the flag resolves to
|
|
26
33
|
* anything that is not an arm name: unset, a boolean, or the empty string.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* exist.
|
|
34
|
+
* Any arm this build does not know is returned as-is and misses the pin table,
|
|
35
|
+
* which is what keeps a stale or malformed LaunchDarkly value on the shipped
|
|
36
|
+
* model rather than stranding an install on one that does not exist.
|
|
31
37
|
*/
|
|
32
38
|
export function getBalancedModelExperimentArm(): string | undefined {
|
|
33
39
|
const value = getCachedOverrides()?.[BALANCED_MODEL_EXPERIMENT_FLAG_KEY];
|