@vellumai/assistant 0.11.8 → 0.11.9-staging.2
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 +2 -2
- package/Dockerfile +8 -48
- package/docker-entrypoint.sh +5 -1
- package/docker-kata-apt-env.sh +3 -0
- package/docker-kata-apt-shims.sh +127 -0
- package/docker-kata-apt-wrapper.sh +45 -0
- package/docker-kata-chroot-exec.sh +35 -0
- package/docker-kata-pip.sh +8 -2
- package/docs/architecture/memory.md +8 -4
- package/docs/guardian-request-flow.md +18 -14
- package/docs/trusted-contact-access.md +1 -7
- package/node_modules/@vellumai/avatar-catalog/src/catalog.ts +7 -1
- package/node_modules/@vellumai/avatar-catalog/src/index.ts +1 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -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/platform-credential.ts +41 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -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/platform-credential.ts +41 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +0 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +110 -5
- package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +5 -33
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +3 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +100 -9
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -2
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +9 -0
- package/node_modules/@vellumai/service-contracts/package.json +4 -1
- package/node_modules/@vellumai/service-contracts/src/guardian-requests.ts +18 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/platform-credential.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/reactions.ts +60 -0
- package/openapi.yaml +193 -4
- package/package.json +2 -2
- package/src/__tests__/access-request-card-view.test.ts +6 -5
- package/src/__tests__/access-request-seed-content-blocks.test.ts +5 -2
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +43 -82
- package/src/__tests__/always-loaded-tools-guard.test.ts +8 -2
- package/src/__tests__/attachment-stored-path-annotation.test.ts +80 -0
- package/src/__tests__/attachments-store.test.ts +27 -0
- package/src/__tests__/attachments.test.ts +43 -0
- package/src/__tests__/canned-reply-release.test.ts +48 -5
- package/src/__tests__/channel-delivery-store.test.ts +129 -18
- package/src/__tests__/channel-reply-delivery.test.ts +553 -93
- package/src/__tests__/channel-retry-sweep.test.ts +199 -0
- package/src/__tests__/client-os-metadata-persistence.test.ts +32 -3
- package/src/__tests__/conversation-error.test.ts +15 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +118 -0
- package/src/__tests__/conversation-pairing.test.ts +141 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +28 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +125 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +79 -0
- package/src/__tests__/conversation-store-ephemeral.test.ts +323 -10
- package/src/__tests__/conversation-sync-tags.test.ts +2 -37
- package/src/__tests__/credential-health-service.test.ts +98 -10
- package/src/__tests__/delete-propagation.test.ts +469 -0
- package/src/__tests__/dm-persistence.test.ts +16 -0
- package/src/__tests__/docker-kata-apt-shims.test.ts +135 -0
- package/src/__tests__/forbidden-legacy-symbols.test.ts +12 -0
- package/src/__tests__/gemini-provider.test.ts +46 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +0 -22
- package/src/__tests__/guardian-gateway-sim.ts +0 -23
- package/src/__tests__/guardian-question-mode.test.ts +108 -0
- package/src/__tests__/guardian-reply-router-answer-mode.test.ts +0 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +26 -12
- package/src/__tests__/host-proxy-interface.test.ts +10 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +16 -0
- package/src/__tests__/injector-v3-suppression.test.ts +43 -9
- package/src/__tests__/list-messages-page-latest.test.ts +127 -0
- package/src/__tests__/list-messages-system-card.test.ts +103 -0
- package/src/__tests__/media-resolve-image-validation.test.ts +77 -0
- package/src/__tests__/notification-decision-fallback.test.ts +199 -77
- package/src/__tests__/notification-decision-strategy.test.ts +198 -213
- package/src/__tests__/notification-discord-adapter.test.ts +25 -0
- package/src/__tests__/notification-slack-adapter.test.ts +133 -0
- package/src/__tests__/notification-telegram-adapter.test.ts +36 -0
- package/src/__tests__/openai-provider.test.ts +5 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +85 -72
- package/src/__tests__/persist-user-message-set-processing-failure.test.ts +83 -65
- package/src/__tests__/platform-client-verify-credential.test.ts +100 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -1
- package/src/__tests__/pricing.test.ts +11 -0
- package/src/__tests__/process-message-display-content.test.ts +20 -9
- package/src/__tests__/processing-acquire-fenced-guard.test.ts +56 -0
- package/src/__tests__/provider-meta-persistence.test.ts +67 -0
- package/src/__tests__/reaction-persistence.test.ts +22 -198
- package/src/__tests__/run-conversation-turn-persistence.test.ts +5 -2
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +16 -0
- package/src/__tests__/skill-load-tool.test.ts +27 -0
- package/src/__tests__/skills.test.ts +34 -1
- package/src/__tests__/strip-memory-injections.test.ts +3 -4
- package/src/__tests__/terminal-tools.test.ts +9 -0
- package/src/__tests__/thread-backfill.test.ts +5 -3
- package/src/__tests__/unified-turn-context-location.test.ts +76 -0
- package/src/__tests__/voice-session-bridge.test.ts +18 -0
- package/src/__tests__/watch-retro-report-payload.test.ts +185 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +82 -0
- package/src/__tests__/workspace-migration-151-repair-renamed-fireworks-deepseek-pro-model-id.test.ts +235 -0
- package/src/__tests__/workspace-migration-152-repair-retired-fireworks-minimax-m2p7-model-id.test.ts +233 -0
- package/src/agent/attachments.ts +13 -2
- package/src/agent/loop.ts +3 -19
- package/src/api/README.md +9 -5
- package/src/api/index.ts +9 -8
- package/src/api/package.json +1 -0
- package/src/api/responses/conversation-message.ts +8 -0
- package/src/api/responses/home.ts +7 -18
- package/src/api/surfaces.ts +114 -7
- package/src/approvals/AGENTS.md +1 -1
- package/src/channels/__tests__/gateway-guardian-requests.test.ts +1 -23
- package/src/channels/__tests__/types.test.ts +22 -1
- package/src/channels/gateway-guardian-requests.ts +0 -22
- package/src/channels/types.ts +8 -6
- package/src/cli/__tests__/catalog-search-help.test.ts +18 -0
- package/src/cli/commands/channels/__tests__/channels.test.ts +26 -0
- package/src/cli/commands/channels/index.help.ts +19 -6
- package/src/cli/commands/channels/index.ts +6 -2
- package/src/cli/commands/db/__tests__/status.test.ts +22 -0
- package/src/cli/commands/db/index.help.ts +1 -1
- package/src/cli/commands/db/status.ts +172 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +29 -0
- package/src/cli/commands/platform/connect.ts +14 -5
- package/src/cli/commands/plugins.help.ts +6 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +0 -8
- package/src/cli/lib/__tests__/install-from-platform.test.ts +72 -0
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +33 -0
- package/src/cli/lib/bundled-marketplace.json +14 -0
- package/src/cli/lib/install-from-github.ts +9 -5
- package/src/cli/lib/install-from-platform.ts +12 -1
- package/src/config/__tests__/assistant-initiated-threads-gate.test.ts +61 -0
- package/src/config/assistant-initiated-threads-gate.ts +50 -0
- package/src/config/bundled-skills/acp/SKILL.md +10 -3
- package/src/config/bundled-skills/schedule/SKILL.md +25 -11
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +3 -1
- package/src/config/call-site-defaults.ts +4 -1
- package/src/config/feature-flag-registry.json +21 -4
- package/src/config/schemas/memory-v3.ts +4 -3
- package/src/context/strip-injections.ts +17 -56
- package/src/conversations/__tests__/message-consolidation.test.ts +39 -0
- package/src/conversations/message-consolidation.ts +4 -3
- package/src/credential-health/credential-health-service.ts +130 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +31 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +55 -59
- package/src/daemon/conversation-error.ts +2 -0
- package/src/daemon/conversation-messaging.ts +195 -47
- package/src/daemon/conversation-process.ts +20 -5
- package/src/daemon/conversation-runtime-assembly.ts +48 -39
- package/src/daemon/conversation-store.ts +159 -11
- package/src/daemon/conversation-tool-setup.ts +9 -2
- package/src/daemon/conversation.ts +290 -19
- package/src/daemon/dictation-text-processing.ts +2 -7
- package/src/daemon/handlers/config-channels.ts +33 -3
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/shared.ts +9 -0
- package/src/daemon/port-oversized-content.test.ts +117 -0
- package/src/daemon/port-oversized-content.ts +110 -0
- package/src/daemon/process-message.ts +87 -16
- package/src/daemon/reaction-record.test.ts +23 -11
- package/src/daemon/reaction-record.ts +6 -9
- package/src/documents/document-store.ts +1 -5
- package/src/home/conversation-starter-validation.ts +1 -5
- package/src/live-voice/__tests__/live-voice-photo.test.ts +910 -50
- package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +31 -25
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +70 -1
- package/src/live-voice/live-voice-photo.ts +544 -61
- package/src/live-voice/live-voice-session.ts +6 -2
- package/src/live-voice/protocol.ts +20 -1
- package/src/messaging/provider-message-metadata.ts +40 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +10 -2
- package/src/messaging/providers/channel-transport.ts +28 -0
- package/src/messaging/providers/discord/send.ts +3 -2
- package/src/messaging/providers/slack/api.ts +11 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +133 -0
- package/src/messaging/providers/slack/message-metadata.ts +141 -1
- package/src/messaging/providers/slack/send.test.ts +58 -0
- package/src/messaging/providers/slack/send.ts +4 -4
- package/src/messaging/providers/slack/transport.ts +28 -2
- package/src/messaging/providers/telegram-bot/send.test.ts +159 -0
- package/src/messaging/providers/telegram-bot/send.ts +93 -0
- package/src/messaging/providers/telegram-bot/transport.ts +71 -0
- package/src/messaging/reaction-envelopes.test.ts +73 -0
- package/src/messaging/reaction-envelopes.ts +33 -5
- package/src/messaging/read-provider-metadata.ts +4 -3
- package/src/monitoring/recovery/__tests__/stranded-delivery-events.test.ts +208 -0
- package/src/monitoring/recovery/db.ts +35 -0
- package/src/monitoring/recovery/orphaned-channel-events.ts +3 -22
- package/src/monitoring/recovery/run-recovery.ts +6 -2
- package/src/monitoring/recovery/stale-processing.ts +3 -20
- package/src/monitoring/recovery/stranded-delivery-events.ts +68 -0
- package/src/notifications/AGENTS.md +2 -2
- package/src/notifications/README.md +2 -2
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +11 -9
- package/src/notifications/__tests__/broadcaster.test.ts +35 -4
- package/src/notifications/__tests__/notification-utils.test.ts +31 -0
- package/src/notifications/access-request-copy.ts +126 -114
- package/src/notifications/adapters/discord.ts +9 -5
- package/src/notifications/adapters/shared.ts +17 -4
- package/src/notifications/adapters/slack.ts +14 -4
- package/src/notifications/adapters/telegram.ts +8 -4
- package/src/notifications/approval-card-data.ts +5 -2
- package/src/notifications/assistant-reply-producer.ts +7 -7
- package/src/notifications/broadcaster.ts +57 -25
- package/src/notifications/conversation-pairing.ts +68 -2
- package/src/notifications/copy-composer.ts +13 -32
- package/src/notifications/decision-engine.ts +56 -237
- package/src/notifications/guardian-delivery-recorder.ts +5 -9
- package/src/notifications/guardian-feed-projection.ts +0 -15
- package/src/notifications/guardian-question-mode.ts +147 -6
- package/src/notifications/notification-utils.ts +117 -1
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -1
- package/src/persistence/conversation-crud.ts +93 -14
- package/src/persistence/conversation-queries.ts +144 -17
- package/src/persistence/conversation-types.ts +39 -5
- package/src/persistence/delivery-crud.ts +224 -28
- package/src/persistence/delivery-status.ts +21 -2
- package/src/persistence/migrations/374-channel-inbound-message-id-index.ts +26 -0
- package/src/persistence/migrations/375-create-channel-outbound-posts.ts +52 -0
- package/src/persistence/migrations/__tests__/375-create-channel-outbound-posts.test.ts +84 -0
- package/src/persistence/schema/conversations.ts +71 -2
- package/src/persistence/schema-contract.test.ts +78 -0
- package/src/persistence/schema-contract.ts +96 -0
- package/src/persistence/steps.ts +4 -0
- package/src/platform/client.ts +55 -0
- package/src/plugins/__tests__/mcp-servers.test.ts +46 -0
- package/src/plugins/defaults/injector-order.ts +2 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +2 -2
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +13 -15
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +10 -0
- package/src/plugins/defaults/memory/injectors.ts +1 -1
- package/src/plugins/defaults/memory/memory-marker.ts +17 -7
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +1 -5
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +17 -0
- package/src/plugins/defaults/memory/substrate/skill-content.ts +7 -0
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +54 -41
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +3 -3
- package/src/plugins/defaults/memory/v3/__tests__/render-injection.test.ts +22 -0
- package/src/plugins/defaults/memory/v3/injector.ts +14 -15
- package/src/plugins/defaults/memory/v3/prune.test.ts +20 -2
- package/src/plugins/defaults/memory/v3/render-injection.ts +10 -1
- package/src/plugins/defaults/memory/v3/types.ts +15 -4
- package/src/plugins/defaults/turn-context/injectors.ts +3 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +24 -0
- package/src/plugins/mcp-servers.ts +14 -2
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +50 -0
- package/src/providers/__tests__/registry-native-web-search.test.ts +49 -2
- package/src/providers/anthropic/client.ts +21 -24
- package/src/providers/call-site-routing.ts +5 -4
- package/src/providers/connection-resolution.ts +29 -1
- package/src/providers/content-block-size.test.ts +82 -0
- package/src/providers/content-block-size.ts +99 -0
- package/src/providers/file-block-text.test.ts +64 -0
- package/src/providers/file-block-text.ts +28 -0
- package/src/providers/gemini/client.ts +15 -9
- package/src/providers/inference/auth.ts +6 -6
- package/src/providers/media-resolve.ts +14 -0
- package/src/providers/model-catalog.ts +47 -34
- package/src/providers/openai/chat-completions-provider.ts +9 -25
- package/src/providers/openai/responses-provider.ts +11 -18
- package/src/providers/registry.ts +10 -7
- package/src/providers/routing-identity.ts +2 -1
- package/src/providers/types.ts +1 -4
- package/src/providers/vellum-model-routing.ts +3 -2
- package/src/runtime/AGENTS.md +40 -15
- package/src/runtime/approval-message-composer.ts +1 -6
- package/src/runtime/assistant-event-hub.ts +2 -39
- package/src/runtime/channel-approval-types.ts +1 -5
- package/src/runtime/channel-reply-delivery.ts +182 -115
- package/src/runtime/{slack-reply-session.test.ts → channel-reply-session.test.ts} +294 -156
- package/src/runtime/{slack-reply-session.ts → channel-reply-session.ts} +107 -86
- package/src/runtime/channel-retry-sweep.ts +102 -1
- package/src/runtime/finalize-event-delivery.ts +4 -4
- package/src/runtime/guardian-action-message-composer.ts +1 -4
- package/src/runtime/guardian-reply-router.ts +4 -75
- package/src/runtime/http-router.ts +1 -5
- package/src/runtime/question-request-guardian-bridge.ts +2 -3
- package/src/runtime/routes/__tests__/conversation-list-assistant-section.test.ts +359 -0
- package/src/runtime/routes/__tests__/dictation-command-mode.test.ts +120 -0
- package/src/runtime/routes/__tests__/sight-frame-routes.test.ts +487 -0
- package/src/runtime/routes/acp-routes.ts +1 -1
- package/src/runtime/routes/canned-reply-release.ts +19 -9
- package/src/runtime/routes/channel-route-shared.ts +0 -39
- package/src/runtime/routes/channel-verification-routes.ts +4 -1
- package/src/runtime/routes/conversation-list-routes.ts +44 -3
- package/src/runtime/routes/conversation-management-routes.ts +2 -1
- package/src/runtime/routes/conversation-routes.ts +138 -59
- package/src/runtime/routes/diagnostics-routes.ts +111 -60
- package/src/runtime/routes/guardian-action-routes.ts +10 -14
- package/src/runtime/routes/guardian-approval-interception.ts +0 -5
- package/src/runtime/routes/inbound-message-handler.ts +170 -48
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +4 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +169 -8
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +82 -14
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +23 -48
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +454 -94
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +309 -102
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/platform-routes.ts +99 -10
- package/src/runtime/routes/sight-frame-routes.ts +136 -0
- package/src/runtime/sync/resource-sync-events.ts +14 -37
- package/src/runtime/sync/sync-publisher.test.ts +5 -4
- package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
- package/src/tools/client-os.ts +11 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/terminal/safe-env.ts +4 -0
- package/src/tools/tool-input-schemas.ts +2 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +4 -3
- package/src/tools/watch/watch-retro-report.ts +205 -0
- package/src/watch/__tests__/watch-retro.test.ts +268 -40
- package/src/watch/watch-retro.ts +190 -77
- package/src/workspace/migrations/151-repair-renamed-fireworks-deepseek-pro-model-id.ts +195 -0
- package/src/workspace/migrations/152-repair-retired-fireworks-minimax-m2p7-model-id.ts +198 -0
- package/src/workspace/migrations/__tests__/150-stt-flux-provider-to-model-family.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +4 -0
- package/docker-kata-pip-chroot.sh +0 -22
- package/src/__tests__/slack-reaction-approvals.test.ts +0 -97
- package/src/__tests__/slack-reaction-guardian-approval.test.ts +0 -307
- package/src/api/events/conversation-list-invalidated.ts +0 -38
package/src/__tests__/workspace-migration-152-repair-retired-fireworks-minimax-m2p7-model-id.test.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { Database } from "bun:sqlite";
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
12
|
+
|
|
13
|
+
import { repairRetiredFireworksMinimaxM2p7ModelIdMigration } from "../workspace/migrations/152-repair-retired-fireworks-minimax-m2p7-model-id.js";
|
|
14
|
+
import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
|
|
15
|
+
import { assertNotLiveDb } from "./assert-not-live-db.js";
|
|
16
|
+
|
|
17
|
+
const STALE = "accounts/fireworks/models/minimax-m2p7";
|
|
18
|
+
const REPLACEMENT = "accounts/fireworks/models/minimax-m3";
|
|
19
|
+
|
|
20
|
+
let workspaceDir: string;
|
|
21
|
+
|
|
22
|
+
function freshWorkspace(): void {
|
|
23
|
+
workspaceDir = join(
|
|
24
|
+
tmpdir(),
|
|
25
|
+
`vellum-migration-152-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
26
|
+
);
|
|
27
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function writeConfig(data: Record<string, unknown>): void {
|
|
31
|
+
writeFileSync(
|
|
32
|
+
join(workspaceDir, "config.json"),
|
|
33
|
+
JSON.stringify(data, null, 2) + "\n",
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readConfig(): Record<string, unknown> {
|
|
38
|
+
return JSON.parse(readFileSync(join(workspaceDir, "config.json"), "utf-8"));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function seedRows(rows: Array<{ name: string; provider: string }>): void {
|
|
42
|
+
mkdirSync(join(workspaceDir, "data", "db"), { recursive: true });
|
|
43
|
+
const db = new Database(join(workspaceDir, "data", "db", "assistant.db"));
|
|
44
|
+
db.run(`CREATE TABLE IF NOT EXISTS provider_connections (
|
|
45
|
+
name TEXT PRIMARY KEY,
|
|
46
|
+
provider TEXT NOT NULL,
|
|
47
|
+
auth TEXT NOT NULL,
|
|
48
|
+
created_at INTEGER NOT NULL,
|
|
49
|
+
updated_at INTEGER NOT NULL
|
|
50
|
+
)`);
|
|
51
|
+
for (const row of rows) {
|
|
52
|
+
db.query(
|
|
53
|
+
`INSERT INTO provider_connections (name, provider, auth, created_at, updated_at) VALUES (?, ?, '{"type":"api_key"}', 1, 1)`,
|
|
54
|
+
).run(row.name, row.provider);
|
|
55
|
+
}
|
|
56
|
+
db.close();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
freshWorkspace();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
if (existsSync(workspaceDir)) {
|
|
65
|
+
assertNotLiveDb(workspaceDir);
|
|
66
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("152-repair-retired-fireworks-minimax-m2p7-model-id migration", () => {
|
|
71
|
+
test("has correct migration id and is registered", () => {
|
|
72
|
+
expect(repairRetiredFireworksMinimaxM2p7ModelIdMigration.id).toBe(
|
|
73
|
+
"152-repair-retired-fireworks-minimax-m2p7-model-id",
|
|
74
|
+
);
|
|
75
|
+
expect(WORKSPACE_MIGRATIONS.map((m) => m.id)).toContain(
|
|
76
|
+
"152-repair-retired-fireworks-minimax-m2p7-model-id",
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("repairs the stale ID in default, call sites, and profiles", () => {
|
|
81
|
+
writeConfig({
|
|
82
|
+
llm: {
|
|
83
|
+
default: { provider: "fireworks", model: STALE },
|
|
84
|
+
callSites: {
|
|
85
|
+
recall: { model: STALE, maxTokens: 4096 },
|
|
86
|
+
heartbeat: { model: `${STALE}-tuned` },
|
|
87
|
+
malformed: STALE,
|
|
88
|
+
},
|
|
89
|
+
profiles: {
|
|
90
|
+
"cost-optimized": { provider: "vellum", model: STALE },
|
|
91
|
+
legacy: { model: STALE },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir);
|
|
97
|
+
|
|
98
|
+
const llm = readConfig().llm as Record<string, any>;
|
|
99
|
+
expect(llm.default.model).toBe(REPLACEMENT);
|
|
100
|
+
expect(llm.callSites.recall.model).toBe(REPLACEMENT);
|
|
101
|
+
expect(llm.callSites.recall.maxTokens).toBe(4096);
|
|
102
|
+
// Non-exact matches and malformed leaves are untouched.
|
|
103
|
+
expect(llm.callSites.heartbeat.model).toBe(`${STALE}-tuned`);
|
|
104
|
+
expect(llm.callSites.malformed).toBe(STALE);
|
|
105
|
+
// Managed profiles stamped provider "vellum" carry Fireworks model IDs.
|
|
106
|
+
expect(llm.profiles["cost-optimized"].model).toBe(REPLACEMENT);
|
|
107
|
+
expect(llm.profiles.legacy.model).toBe(REPLACEMENT);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("repairs entry-bound fragments whose row kind is fireworks or vellum", () => {
|
|
111
|
+
seedRows([
|
|
112
|
+
{ name: "my-fireworks", provider: "fireworks" },
|
|
113
|
+
{ name: "managed-alt", provider: "vellum" },
|
|
114
|
+
]);
|
|
115
|
+
writeConfig({
|
|
116
|
+
llm: {
|
|
117
|
+
profiles: {
|
|
118
|
+
bound: { provider: "my-fireworks", model: STALE },
|
|
119
|
+
managed: { provider: "managed-alt", model: STALE },
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir);
|
|
125
|
+
|
|
126
|
+
const llm = readConfig().llm as Record<string, any>;
|
|
127
|
+
expect(llm.profiles.bound.model).toBe(REPLACEMENT);
|
|
128
|
+
expect(llm.profiles.managed.model).toBe(REPLACEMENT);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("leaves fragments with an explicit other provider untouched", () => {
|
|
132
|
+
seedRows([{ name: "byo-compat", provider: "openai-compatible" }]);
|
|
133
|
+
writeConfig({
|
|
134
|
+
llm: {
|
|
135
|
+
default: { provider: "openai-compatible", model: STALE },
|
|
136
|
+
profiles: {
|
|
137
|
+
byo: { provider: "openai-compatible", model: STALE },
|
|
138
|
+
// Entry-bound profile whose row kind is not repairable.
|
|
139
|
+
compatBound: { provider: "byo-compat", model: STALE },
|
|
140
|
+
// Entry name with no row: dangling, so nothing proves it is a
|
|
141
|
+
// fireworks-kind route.
|
|
142
|
+
dangling: { provider: "ghost", model: STALE },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir);
|
|
148
|
+
|
|
149
|
+
const llm = readConfig().llm as Record<string, any>;
|
|
150
|
+
expect(llm.default.model).toBe(STALE);
|
|
151
|
+
expect(llm.profiles.byo.model).toBe(STALE);
|
|
152
|
+
expect(llm.profiles.compatBound.model).toBe(STALE);
|
|
153
|
+
expect(llm.profiles.dangling.model).toBe(STALE);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("leaves entry-name providers untouched when no DB file exists", () => {
|
|
157
|
+
writeConfig({
|
|
158
|
+
llm: {
|
|
159
|
+
profiles: { bound: { provider: "my-fireworks", model: STALE } },
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir);
|
|
164
|
+
|
|
165
|
+
const llm = readConfig().llm as Record<string, any>;
|
|
166
|
+
expect(llm.profiles.bound.model).toBe(STALE);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("throws when an entry-name provider needs rows and the DB is unreadable", () => {
|
|
170
|
+
// A DB file without the provider_connections table is unqueryable, so
|
|
171
|
+
// the run must fail (and retry later) instead of skipping the profile.
|
|
172
|
+
mkdirSync(join(workspaceDir, "data", "db"), { recursive: true });
|
|
173
|
+
writeFileSync(join(workspaceDir, "data", "db", "assistant.db"), "");
|
|
174
|
+
writeConfig({
|
|
175
|
+
llm: {
|
|
176
|
+
profiles: { bound: { provider: "my-fireworks", model: STALE } },
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(() =>
|
|
181
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir),
|
|
182
|
+
).toThrow();
|
|
183
|
+
const llm = readConfig().llm as Record<string, any>;
|
|
184
|
+
expect(llm.profiles.bound.model).toBe(STALE);
|
|
185
|
+
|
|
186
|
+
// Vendor and absent providers never need the rows, so the same broken
|
|
187
|
+
// DB does not block their repair.
|
|
188
|
+
writeConfig({
|
|
189
|
+
llm: {
|
|
190
|
+
default: { provider: "fireworks", model: STALE },
|
|
191
|
+
profiles: { legacy: { model: STALE } },
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir);
|
|
195
|
+
const repaired = readConfig().llm as Record<string, any>;
|
|
196
|
+
expect(repaired.default.model).toBe(REPLACEMENT);
|
|
197
|
+
expect(repaired.profiles.legacy.model).toBe(REPLACEMENT);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("is idempotent and a no-op without the stale ID", () => {
|
|
201
|
+
writeConfig({
|
|
202
|
+
llm: {
|
|
203
|
+
default: { provider: "fireworks", model: STALE },
|
|
204
|
+
profiles: { fine: { provider: "fireworks", model: REPLACEMENT } },
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir);
|
|
209
|
+
const first = readConfig();
|
|
210
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir);
|
|
211
|
+
expect(readConfig()).toEqual(first);
|
|
212
|
+
|
|
213
|
+
const llm = first.llm as Record<string, any>;
|
|
214
|
+
expect(llm.default.model).toBe(REPLACEMENT);
|
|
215
|
+
expect(llm.profiles.fine.model).toBe(REPLACEMENT);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("handles missing config, missing llm block, and invalid JSON", () => {
|
|
219
|
+
// No config.json at all.
|
|
220
|
+
expect(() =>
|
|
221
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir),
|
|
222
|
+
).not.toThrow();
|
|
223
|
+
|
|
224
|
+
writeConfig({ theme: "dark" });
|
|
225
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir);
|
|
226
|
+
expect(readConfig()).toEqual({ theme: "dark" });
|
|
227
|
+
|
|
228
|
+
writeFileSync(join(workspaceDir, "config.json"), "{not json");
|
|
229
|
+
expect(() =>
|
|
230
|
+
repairRetiredFireworksMinimaxM2p7ModelIdMigration.run(workspaceDir),
|
|
231
|
+
).not.toThrow();
|
|
232
|
+
});
|
|
233
|
+
});
|
package/src/agent/attachments.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { extractedTextForFileBlock } from "../providers/content-block-size.js";
|
|
1
2
|
import {
|
|
2
3
|
attachmentIdFragment,
|
|
3
4
|
type ContentBlock,
|
|
@@ -64,6 +65,10 @@ export function attachmentsToReferenceBlocks(
|
|
|
64
65
|
} as ContentBlock;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
const extractedText = extractedTextForFileBlock(
|
|
69
|
+
ref.mimeType,
|
|
70
|
+
ref.extractedText,
|
|
71
|
+
);
|
|
67
72
|
return {
|
|
68
73
|
type: "file",
|
|
69
74
|
source: {
|
|
@@ -73,7 +78,7 @@ export function attachmentsToReferenceBlocks(
|
|
|
73
78
|
sizeBytes: ref.sizeBytes,
|
|
74
79
|
filename: ref.filename,
|
|
75
80
|
},
|
|
76
|
-
extracted_text:
|
|
81
|
+
...(extractedText !== undefined ? { extracted_text: extractedText } : {}),
|
|
77
82
|
} as ContentBlock;
|
|
78
83
|
});
|
|
79
84
|
}
|
|
@@ -99,6 +104,10 @@ export function attachmentsToContentBlocks(
|
|
|
99
104
|
} as ContentBlock;
|
|
100
105
|
}
|
|
101
106
|
|
|
107
|
+
const extractedText = extractedTextForFileBlock(
|
|
108
|
+
attachment.mimeType,
|
|
109
|
+
attachment.extractedText,
|
|
110
|
+
);
|
|
102
111
|
return {
|
|
103
112
|
type: "file",
|
|
104
113
|
source: {
|
|
@@ -107,7 +116,9 @@ export function attachmentsToContentBlocks(
|
|
|
107
116
|
data: attachment.data,
|
|
108
117
|
filename: attachment.filename,
|
|
109
118
|
},
|
|
110
|
-
|
|
119
|
+
...(extractedText !== undefined
|
|
120
|
+
? { extracted_text: extractedText }
|
|
121
|
+
: {}),
|
|
111
122
|
...attachmentIdFragment(attachment.id),
|
|
112
123
|
} as ContentBlock;
|
|
113
124
|
}),
|
package/src/agent/loop.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { AnsweredQuestion } from "../api/events/question-answered.js";
|
|
|
2
2
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
3
3
|
import { recordEstimate } from "../context/estimator-calibration.js";
|
|
4
4
|
import { preModelCallSanitize } from "../context/outbound-sanitize.js";
|
|
5
|
-
import { turnStartUserMessageHasSpotlight } from "../context/strip-injections.js";
|
|
6
5
|
import {
|
|
7
6
|
estimatePromptTokensRaw,
|
|
8
7
|
estimatePromptTokensWithTools,
|
|
@@ -1551,21 +1550,6 @@ export class AgentLoop {
|
|
|
1551
1550
|
providerConfig.cacheTtl = this.config.cacheTtl;
|
|
1552
1551
|
}
|
|
1553
1552
|
|
|
1554
|
-
// Cache-anchor signal for turns whose opening message is volatile. The
|
|
1555
|
-
// memory-v3 `<memory_spotlight>` block is the only injected block that
|
|
1556
|
-
// is strip-and-replaced from every user message each turn, so when it
|
|
1557
|
-
// is present that message's bytes do not recur next turn and a
|
|
1558
|
-
// long-TTL breakpoint on it could never be read back. The provider
|
|
1559
|
-
// marks it at the short TTL instead. Derived from the history actually
|
|
1560
|
-
// being sent rather than from configuration, so turns where memory
|
|
1561
|
-
// contributed no spotlight keep a normal anchor. Read off the
|
|
1562
|
-
// turn-starting message, so the signal holds for every request in the
|
|
1563
|
-
// turn rather than flipping once tool results arrive. Only set when
|
|
1564
|
-
// true so the wire/config stays byte-identical when absent.
|
|
1565
|
-
if (turnStartUserMessageHasSpotlight(history)) {
|
|
1566
|
-
providerConfig.mutableLatestUserMessage = true;
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
1553
|
// Per-call LLM call-site identifier. Surfaces on the per-call
|
|
1570
1554
|
// `config.callSite` so `RetryProvider.normalizeSendMessageOptions`
|
|
1571
1555
|
// can route through `resolveCallSiteConfig` against
|
|
@@ -1652,7 +1636,7 @@ export class AgentLoop {
|
|
|
1652
1636
|
// Sanitize the outbound history right before sending: drop accumulated
|
|
1653
1637
|
// media, collapse old AX-tree snapshots, and convert historical
|
|
1654
1638
|
// web-search results to text. See {@link preModelCallSanitize}.
|
|
1655
|
-
const
|
|
1639
|
+
const sanitizedHistory = timeSyncSection(
|
|
1656
1640
|
"agent-loop:pre-model-call-sanitize",
|
|
1657
1641
|
() => preModelCallSanitize(history),
|
|
1658
1642
|
(sanitized) => ({ messageCount: sanitized.length }),
|
|
@@ -1837,7 +1821,7 @@ export class AgentLoop {
|
|
|
1837
1821
|
let response: ProviderResponse;
|
|
1838
1822
|
try {
|
|
1839
1823
|
response = await traceAsyncSection("agent-loop:provider-send", () =>
|
|
1840
|
-
this.provider.sendMessage(
|
|
1824
|
+
this.provider.sendMessage(sanitizedHistory, providerOptions),
|
|
1841
1825
|
);
|
|
1842
1826
|
} catch (llmCallError) {
|
|
1843
1827
|
// Skip recording on abort — the user cancelled the request and
|
|
@@ -1855,7 +1839,7 @@ export class AgentLoop {
|
|
|
1855
1839
|
// misrepresent both.
|
|
1856
1840
|
const rawRequest = {
|
|
1857
1841
|
provider: this.provider.name,
|
|
1858
|
-
messages:
|
|
1842
|
+
messages: sanitizedHistory,
|
|
1859
1843
|
tools: providerOptions.tools,
|
|
1860
1844
|
systemPrompt: providerOptions.systemPrompt,
|
|
1861
1845
|
config: providerOptions.config,
|
package/src/api/README.md
CHANGED
|
@@ -5,9 +5,12 @@ evals, future external clients. This directory is the **source of truth** for
|
|
|
5
5
|
the wire contracts the assistant exposes: schemas, types, and pure helpers.
|
|
6
6
|
|
|
7
7
|
Internal assistant code imports the files in this directory via relative paths
|
|
8
|
-
(e.g. `../../api/events/open-url.js`). External consumers import the
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
(e.g. `../../api/events/open-url.js`). External consumers import it as the
|
|
9
|
+
workspace package `@vellumai/assistant-api`, a `workspace:*` symlink to this
|
|
10
|
+
directory, so an edit here reaches them immediately. The generated HTTP
|
|
11
|
+
client is separate: it comes from `assistant/openapi.yaml`, regenerated with
|
|
12
|
+
`cd assistant && bun run generate:openapi` and consumed by
|
|
13
|
+
`cd clients/web && bun run openapi-ts`.
|
|
11
14
|
|
|
12
15
|
## Architecture
|
|
13
16
|
|
|
@@ -110,8 +113,9 @@ Handler-level tests in the consuming domain modules typically need no change
|
|
|
110
113
|
Run before push, in order:
|
|
111
114
|
|
|
112
115
|
```bash
|
|
113
|
-
#
|
|
114
|
-
bun run
|
|
116
|
+
# Regenerate the OpenAPI spec and the web client from it
|
|
117
|
+
( cd assistant && bun run generate:openapi )
|
|
118
|
+
( cd clients/web && bun run openapi-ts )
|
|
115
119
|
|
|
116
120
|
# Type-check both packages
|
|
117
121
|
( cd assistant && bunx tsc --noEmit )
|
package/src/api/index.ts
CHANGED
|
@@ -31,7 +31,6 @@ import { ContextCompactedEventSchema } from "./events/context-compacted.js";
|
|
|
31
31
|
import { ContextWindowUsageEventSchema } from "./events/context-window-usage.js";
|
|
32
32
|
import { ConversationErrorEventSchema } from "./events/conversation-error.js";
|
|
33
33
|
import { ConversationInferenceProfileUpdatedEventSchema } from "./events/conversation-inference-profile-updated.js";
|
|
34
|
-
import { ConversationListInvalidatedEventSchema } from "./events/conversation-list-invalidated.js";
|
|
35
34
|
import { ConversationNoticeEventSchema } from "./events/conversation-notice.js";
|
|
36
35
|
import { ConversationTitleUpdatedEventSchema } from "./events/conversation-title-updated.js";
|
|
37
36
|
import { DiskPressureStatusChangedEventSchema } from "./events/disk-pressure-status-changed.js";
|
|
@@ -321,12 +320,6 @@ export {
|
|
|
321
320
|
type ConversationInferenceProfileUpdatedEvent,
|
|
322
321
|
ConversationInferenceProfileUpdatedEventSchema,
|
|
323
322
|
} from "./events/conversation-inference-profile-updated.js";
|
|
324
|
-
export {
|
|
325
|
-
type ConversationListInvalidatedEvent,
|
|
326
|
-
ConversationListInvalidatedEventSchema,
|
|
327
|
-
type ConversationListInvalidatedReason,
|
|
328
|
-
ConversationListInvalidatedReasonSchema,
|
|
329
|
-
} from "./events/conversation-list-invalidated.js";
|
|
330
323
|
export {
|
|
331
324
|
type ConversationNoticeEvent,
|
|
332
325
|
ConversationNoticeEventSchema,
|
|
@@ -913,6 +906,15 @@ export {
|
|
|
913
906
|
TableSurfaceDataSchema,
|
|
914
907
|
type VisualSurfaceData,
|
|
915
908
|
VisualSurfaceDataSchema,
|
|
909
|
+
WATCH_RETRO_MAX_QUESTIONS,
|
|
910
|
+
type WatchRetroOption,
|
|
911
|
+
WatchRetroOptionSchema,
|
|
912
|
+
type WatchRetroQuestion,
|
|
913
|
+
type WatchRetroQuestionKind,
|
|
914
|
+
WatchRetroQuestionKindSchema,
|
|
915
|
+
WatchRetroQuestionSchema,
|
|
916
|
+
type WatchRetroSurfaceData,
|
|
917
|
+
WatchRetroSurfaceDataSchema,
|
|
916
918
|
type WorkResultDiff,
|
|
917
919
|
WorkResultDiffSchema,
|
|
918
920
|
type WorkResultItem,
|
|
@@ -978,7 +980,6 @@ export const AssistantEventSchema = z.discriminatedUnion("type", [
|
|
|
978
980
|
ContextWindowUsageEventSchema,
|
|
979
981
|
ConversationErrorEventSchema,
|
|
980
982
|
ConversationInferenceProfileUpdatedEventSchema,
|
|
981
|
-
ConversationListInvalidatedEventSchema,
|
|
982
983
|
ConversationNoticeEventSchema,
|
|
983
984
|
ConversationTitleUpdatedEventSchema,
|
|
984
985
|
DiskPressureStatusChangedEventSchema,
|
package/src/api/package.json
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* gateway, evals) import via `@vellumai/assistant-api`.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
+
import { ReactionEmojiFieldsSchema } from "@vellumai/service-contracts/reactions";
|
|
25
26
|
import { z } from "zod";
|
|
26
27
|
|
|
27
28
|
import {
|
|
@@ -358,6 +359,7 @@ const SlackMessageLinkSchema = z.object({
|
|
|
358
359
|
|
|
359
360
|
const SlackReactionSchema = z.object({
|
|
360
361
|
emoji: z.string(),
|
|
362
|
+
...ReactionEmojiFieldsSchema.shape,
|
|
361
363
|
op: z.enum(["added", "removed"]),
|
|
362
364
|
actorDisplayName: z.string().optional(),
|
|
363
365
|
targetChannelTs: z.string(),
|
|
@@ -621,6 +623,7 @@ export const ConversationMessageSchema = z.object({
|
|
|
621
623
|
reaction: z
|
|
622
624
|
.object({
|
|
623
625
|
emoji: z.string(),
|
|
626
|
+
...ReactionEmojiFieldsSchema.shape,
|
|
624
627
|
op: z.enum(["added", "removed"]),
|
|
625
628
|
targetMessageId: z.string(),
|
|
626
629
|
actorDisplayName: z.string().optional(),
|
|
@@ -639,6 +642,11 @@ export const ConversationMessageSchema = z.object({
|
|
|
639
642
|
})
|
|
640
643
|
.optional(),
|
|
641
644
|
slackMessage: ConversationSlackMessageSchema.optional(),
|
|
645
|
+
/** Unix ms at which the message was deleted on its channel after the daemon
|
|
646
|
+
* stored it (a Slack or Discord deletion the gateway forwarded). The stored
|
|
647
|
+
* content stays for audit and the Inspect view; clients render a tombstone
|
|
648
|
+
* in place of the content, mirroring what the channel now shows. */
|
|
649
|
+
deletedAt: z.number().optional(),
|
|
642
650
|
/**
|
|
643
651
|
* Queue state for a user message that is still waiting in the daemon's
|
|
644
652
|
* in-memory queue (enqueued while the agent was mid-turn, not yet drained or
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
* (web client, gateway, evals) import via `@vellumai/assistant-api`.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import {
|
|
21
|
+
type GuardianRequestStatus,
|
|
22
|
+
GuardianRequestStatusSchema,
|
|
23
|
+
} from "@vellumai/service-contracts/guardian-requests";
|
|
20
24
|
import { z } from "zod";
|
|
21
25
|
|
|
22
26
|
// ---------------------------------------------------------------------------
|
|
@@ -107,24 +111,9 @@ export const FeedItemDetailPanelSchema = z.object({
|
|
|
107
111
|
});
|
|
108
112
|
export type FeedItemDetailPanel = z.infer<typeof FeedItemDetailPanelSchema>;
|
|
109
113
|
|
|
110
|
-
/**
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
* Values mirror `GuardianRequestStatusSchema` in
|
|
114
|
-
* `@vellumai/gateway-client` (this file is copied verbatim into client
|
|
115
|
-
* packages, so it cannot import the contract; the daemon's projection
|
|
116
|
-
* writer asserts the two stay aligned at compile time).
|
|
117
|
-
*/
|
|
118
|
-
export const FeedItemGuardianStatusSchema = z.enum([
|
|
119
|
-
"pending",
|
|
120
|
-
"approved",
|
|
121
|
-
"denied",
|
|
122
|
-
"expired",
|
|
123
|
-
"cancelled",
|
|
124
|
-
]);
|
|
125
|
-
export type FeedItemGuardianStatus = z.infer<
|
|
126
|
-
typeof FeedItemGuardianStatusSchema
|
|
127
|
-
>;
|
|
114
|
+
/** Canonical guardian-request status projected onto a feed item. */
|
|
115
|
+
export const FeedItemGuardianStatusSchema = GuardianRequestStatusSchema;
|
|
116
|
+
export type FeedItemGuardianStatus = GuardianRequestStatus;
|
|
128
117
|
|
|
129
118
|
/** Whether the guardian is being asked to approve or to answer. */
|
|
130
119
|
export const FeedItemGuardianIntentSchema = z.enum(["approval", "question"]);
|
package/src/api/surfaces.ts
CHANGED
|
@@ -86,6 +86,23 @@ export function describeSurfaceDataStringParseFailure(
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Required string that coerces a scalar, treating an absent field as blank.
|
|
91
|
+
*
|
|
92
|
+
* `z.coerce.string()` is not a validator on a missing field: it stringifies
|
|
93
|
+
* whatever it is handed, so `undefined` parses successfully as the literal
|
|
94
|
+
* "undefined" and a trailing `.catch()` never runs. That string then renders,
|
|
95
|
+
* and a field the model simply failed to send reads to the user as a word
|
|
96
|
+
* nobody wrote. A blank is what the callers already handle: an empty title
|
|
97
|
+
* falls back to a default, and an empty prompt or label is what the watch
|
|
98
|
+
* retro card filters an unanswerable question out on.
|
|
99
|
+
*/
|
|
100
|
+
const coercedString = () =>
|
|
101
|
+
z.preprocess(
|
|
102
|
+
(value) => (value === undefined || value === null ? "" : value),
|
|
103
|
+
z.coerce.string().catch(""),
|
|
104
|
+
);
|
|
105
|
+
|
|
89
106
|
/** Optional string that drops (rather than rejects on) a non-string value. */
|
|
90
107
|
const tolerantString = () => z.string().optional().catch(undefined);
|
|
91
108
|
|
|
@@ -111,7 +128,7 @@ export const CardSurfaceDataSchema = z.object({
|
|
|
111
128
|
subtitle: z.string().optional(),
|
|
112
129
|
body: z.string().optional(),
|
|
113
130
|
metadata: z
|
|
114
|
-
.array(z.object({ label:
|
|
131
|
+
.array(z.object({ label: coercedString(), value: coercedString() }))
|
|
115
132
|
.optional(),
|
|
116
133
|
/** Optional template name for specialized rendering (e.g. "weather_forecast"). */
|
|
117
134
|
template: z.string().optional(),
|
|
@@ -298,7 +315,7 @@ export const FormFieldSchema = z.object({
|
|
|
298
315
|
.optional()
|
|
299
316
|
.catch(undefined),
|
|
300
317
|
options: z
|
|
301
|
-
.array(z.object({ label:
|
|
318
|
+
.array(z.object({ label: coercedString(), value: coercedString() }))
|
|
302
319
|
.optional()
|
|
303
320
|
.catch(undefined),
|
|
304
321
|
});
|
|
@@ -353,7 +370,7 @@ export const TableColumnSchema = z.object({
|
|
|
353
370
|
export type TableColumn = z.infer<typeof TableColumnSchema>;
|
|
354
371
|
|
|
355
372
|
export const TableCellValueSchema = z.object({
|
|
356
|
-
text:
|
|
373
|
+
text: coercedString(),
|
|
357
374
|
/** SF Symbol name. */
|
|
358
375
|
icon: tolerantString(),
|
|
359
376
|
/** Semantic token: "success" | "warning" | "error" | "muted". */
|
|
@@ -402,7 +419,7 @@ export const DynamicPagePreviewSchema = z.object({
|
|
|
402
419
|
description: tolerantString(),
|
|
403
420
|
icon: tolerantString(),
|
|
404
421
|
metrics: z
|
|
405
|
-
.array(z.object({ label:
|
|
422
|
+
.array(z.object({ label: coercedString(), value: coercedString() }))
|
|
406
423
|
.optional()
|
|
407
424
|
.catch(undefined),
|
|
408
425
|
context: z.enum(["app_create", "general"]).optional().catch(undefined),
|
|
@@ -467,7 +484,7 @@ export const WorkResultSectionTypeSchema = z.enum([
|
|
|
467
484
|
export type WorkResultSectionType = z.infer<typeof WorkResultSectionTypeSchema>;
|
|
468
485
|
|
|
469
486
|
const workResultLabelValue = () => ({
|
|
470
|
-
label:
|
|
487
|
+
label: coercedString(),
|
|
471
488
|
value: z.union([z.string(), z.number()]).catch(""),
|
|
472
489
|
});
|
|
473
490
|
|
|
@@ -483,7 +500,7 @@ export type WorkResultMetadata = z.infer<typeof WorkResultMetadataSchema>;
|
|
|
483
500
|
|
|
484
501
|
export const WorkResultItemSchema = z.object({
|
|
485
502
|
id: tolerantString(),
|
|
486
|
-
title:
|
|
503
|
+
title: coercedString(),
|
|
487
504
|
description: tolerantString(),
|
|
488
505
|
status: tolerantString(),
|
|
489
506
|
tone: WorkResultToneSchema.optional().catch(undefined),
|
|
@@ -506,7 +523,7 @@ export type WorkResultDiff = z.infer<typeof WorkResultDiffSchema>;
|
|
|
506
523
|
|
|
507
524
|
export const WorkResultSectionSchema = z.object({
|
|
508
525
|
id: tolerantString(),
|
|
509
|
-
title:
|
|
526
|
+
title: coercedString(),
|
|
510
527
|
description: tolerantString(),
|
|
511
528
|
type: WorkResultSectionTypeSchema.optional().catch(undefined),
|
|
512
529
|
items: recordArray(WorkResultItemSchema).optional().catch(undefined),
|
|
@@ -523,6 +540,96 @@ export const WorkResultSurfaceDataSchema = z.object({
|
|
|
523
540
|
});
|
|
524
541
|
export type WorkResultSurfaceData = z.infer<typeof WorkResultSurfaceDataSchema>;
|
|
525
542
|
|
|
543
|
+
/**
|
|
544
|
+
* A watch retrospective: what a teach session recorded, and the few things the
|
|
545
|
+
* recording could not settle.
|
|
546
|
+
*
|
|
547
|
+
* Carried as the `templateData` of a `card` surface under the `watch_retro`
|
|
548
|
+
* template, rather than as a surface type of its own. A renderer that does not
|
|
549
|
+
* know the template still knows `card`, so it draws the surface's `title`,
|
|
550
|
+
* `subtitle` and `body` instead of an unsupported-surface notice: the retro is
|
|
551
|
+
* the whole of what a finished session gives the user, so it has to survive a
|
|
552
|
+
* client older than the template.
|
|
553
|
+
*
|
|
554
|
+
* Rendered as a paged card, one thing per page: the record first, then a page
|
|
555
|
+
* per question. That shape is the payload's only real constraint: a question
|
|
556
|
+
* list long enough to scroll is a questionnaire, so `questions` is capped at
|
|
557
|
+
* three and the cap is enforced here rather than asked for in the prompt.
|
|
558
|
+
*
|
|
559
|
+
* Tolerant like every other surface schema. A malformed question is dropped
|
|
560
|
+
* and the rest of the card still renders, because the record is the half the
|
|
561
|
+
* user is owed and it does not depend on the questions parsing.
|
|
562
|
+
*/
|
|
563
|
+
export const WatchRetroQuestionKindSchema = z.enum(["fill", "pick", "gate"]);
|
|
564
|
+
export type WatchRetroQuestionKind = z.infer<
|
|
565
|
+
typeof WatchRetroQuestionKindSchema
|
|
566
|
+
>;
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* One answer to a `pick` or `gate` question.
|
|
570
|
+
*
|
|
571
|
+
* **The first option is the default**, and that is the whole of the
|
|
572
|
+
* preselection contract. There is no `selected` flag to disagree with the
|
|
573
|
+
* ordering. On a `pick` it is the reading the recording supports; on a `gate`
|
|
574
|
+
* it is the cautious answer, which is the one case where the default is
|
|
575
|
+
* deliberately not the model's guess.
|
|
576
|
+
*/
|
|
577
|
+
export const WatchRetroOptionSchema = z.object({
|
|
578
|
+
id: coercedString(),
|
|
579
|
+
label: coercedString(),
|
|
580
|
+
/** Short qualifier under the label, e.g. marking the model's own reading. */
|
|
581
|
+
note: tolerantString(),
|
|
582
|
+
});
|
|
583
|
+
export type WatchRetroOption = z.infer<typeof WatchRetroOptionSchema>;
|
|
584
|
+
|
|
585
|
+
export const WatchRetroQuestionSchema = z.object({
|
|
586
|
+
id: coercedString(),
|
|
587
|
+
kind: WatchRetroQuestionKindSchema.catch("pick"),
|
|
588
|
+
prompt: coercedString(),
|
|
589
|
+
/** Eyebrow above the prompt, naming why this one is being asked. */
|
|
590
|
+
eyebrow: tolerantString(),
|
|
591
|
+
/** `fill` only: the pre-filled value, so skipping keeps a working answer. */
|
|
592
|
+
suggestion: tolerantString(),
|
|
593
|
+
/** `pick` and `gate`: the alternatives. First is the default. */
|
|
594
|
+
options: recordArray(WatchRetroOptionSchema).optional().catch(undefined),
|
|
595
|
+
});
|
|
596
|
+
export type WatchRetroQuestion = z.infer<typeof WatchRetroQuestionSchema>;
|
|
597
|
+
|
|
598
|
+
/** How many questions one card may carry. See the schema note. */
|
|
599
|
+
export const WATCH_RETRO_MAX_QUESTIONS = 3;
|
|
600
|
+
|
|
601
|
+
export const WatchRetroSurfaceDataSchema = z.object({
|
|
602
|
+
/** The task, named in one line. The card's title. */
|
|
603
|
+
task: coercedString(),
|
|
604
|
+
/** What the task is for. The one sentence that makes the steps mean something. */
|
|
605
|
+
purpose: tolerantString(),
|
|
606
|
+
/**
|
|
607
|
+
* The steps, in order, as imperative fragments rather than sentences:
|
|
608
|
+
* "Open the Sentry issue", not "You opened the Sentry issue from the alert
|
|
609
|
+
* email". It is about to become a procedure, so it reads as one already.
|
|
610
|
+
*/
|
|
611
|
+
steps: z.preprocess(
|
|
612
|
+
(value) =>
|
|
613
|
+
Array.isArray(value)
|
|
614
|
+
? value.filter((entry) => typeof entry === "string" && entry.trim())
|
|
615
|
+
: [],
|
|
616
|
+
z.array(z.string()),
|
|
617
|
+
),
|
|
618
|
+
/** Session facts for the eyebrow, e.g. "Watched 4 min · 11 screens". */
|
|
619
|
+
eyebrow: tolerantString(),
|
|
620
|
+
/** Set when the recording was bounded, naming which end is missing. */
|
|
621
|
+
coverage: tolerantString(),
|
|
622
|
+
questions: z
|
|
623
|
+
.preprocess(
|
|
624
|
+
(value) =>
|
|
625
|
+
Array.isArray(value) ? value.slice(0, WATCH_RETRO_MAX_QUESTIONS) : [],
|
|
626
|
+
recordArray(WatchRetroQuestionSchema),
|
|
627
|
+
)
|
|
628
|
+
.optional()
|
|
629
|
+
.catch(undefined),
|
|
630
|
+
});
|
|
631
|
+
export type WatchRetroSurfaceData = z.infer<typeof WatchRetroSurfaceDataSchema>;
|
|
632
|
+
|
|
526
633
|
/**
|
|
527
634
|
* Inline visual: a self-contained html fragment rendered in a sandboxed
|
|
528
635
|
* frame. Model-invokable via `ui_show`, which validates the fragment (no
|