@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
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
* without reaching up into the daemon orchestrator.
|
|
13
13
|
*/
|
|
14
14
|
import {
|
|
15
|
+
isMemorySpotlightText,
|
|
15
16
|
MEMORY_SPOTLIGHT_PREFIX,
|
|
16
17
|
MEMORY_SPOTLIGHT_SUFFIX,
|
|
17
18
|
} from "../plugins/defaults/memory/memory-marker.js";
|
|
18
19
|
import type { Message } from "../providers/types.js";
|
|
19
20
|
|
|
21
|
+
export { isMemorySpotlightText };
|
|
22
|
+
|
|
20
23
|
/**
|
|
21
24
|
* A matcher for an injected text block. A plain string matches by prefix
|
|
22
25
|
* (`startsWith`). A `{ prefix, suffix }` wrapper requires BOTH the opening
|
|
@@ -115,69 +118,27 @@ export function stripTailUserTextBlocksByPrefix(
|
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
/**
|
|
118
|
-
* Full-wrapper matcher for the memory-v3
|
|
119
|
-
* Shared by the
|
|
120
|
-
* the compaction pipeline
|
|
121
|
+
* Full-wrapper matcher for the memory-v3 per-turn `<memory_spotlight>` block.
|
|
122
|
+
* Shared by the leftover cleanup ({@link stripSpotlightInjections}), the
|
|
123
|
+
* tail-only assembly strip, and the compaction pipeline so every path
|
|
124
|
+
* recognizes exactly the same wrapper.
|
|
121
125
|
*/
|
|
122
|
-
const MEMORY_SPOTLIGHT_MATCHER: InjectionMatcher = {
|
|
126
|
+
export const MEMORY_SPOTLIGHT_MATCHER: InjectionMatcher = {
|
|
123
127
|
prefix: MEMORY_SPOTLIGHT_PREFIX,
|
|
124
128
|
suffix: MEMORY_SPOTLIGHT_SUFFIX,
|
|
125
129
|
};
|
|
126
130
|
|
|
127
131
|
/**
|
|
128
|
-
* Remove
|
|
129
|
-
*
|
|
130
|
-
* strip
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* whole-layer `stripAllMemoryInjections` replace is gone.
|
|
132
|
+
* Remove leftover `<memory_spotlight>` blocks from every user message, and
|
|
133
|
+
* only those blocks. Compaction and overflow recovery rewrite history, so
|
|
134
|
+
* they strip every runtime injection including historical spotlights.
|
|
135
|
+
* Frozen `<memory>` card blocks stay byte-identical. Assembly uses the
|
|
136
|
+
* tail-scoped variant so live history keeps each prior turn's spotlight.
|
|
134
137
|
*/
|
|
135
138
|
export function stripSpotlightInjections(messages: Message[]): Message[] {
|
|
136
139
|
return stripUserTextBlocksByPrefix(messages, [MEMORY_SPOTLIGHT_MATCHER]);
|
|
137
140
|
}
|
|
138
141
|
|
|
139
|
-
/**
|
|
140
|
-
* Whether the TURN-STARTING user message carries a memory-v3
|
|
141
|
-
* `<memory_spotlight>` block.
|
|
142
|
-
*
|
|
143
|
-
* The spotlight is the only injected block that is strip-and-replaced from
|
|
144
|
-
* every user message each turn, so its presence is exactly what makes that
|
|
145
|
-
* message volatile across turns. Every other injected block (turn context,
|
|
146
|
-
* workspace, `<info>`, `<memory>` cards, NOW.md) is frozen into history and
|
|
147
|
-
* re-renders byte-identically, so a message without a spotlight is a stable
|
|
148
|
-
* cache anchor. Providers consume this through the `mutableLatestUserMessage`
|
|
149
|
-
* config field when placing cache breakpoints.
|
|
150
|
-
*
|
|
151
|
-
* The turn start is the most recent user message carrying TEXT content, which
|
|
152
|
-
* is the same message the Anthropic client anchors on. Tool-result messages
|
|
153
|
-
* are skipped: they are user-role but carry no injected blocks, and letting
|
|
154
|
-
* them answer this question would flip the signal to `false` partway through a
|
|
155
|
-
* tool loop. The provider would then mark the very same turn-start block at a
|
|
156
|
-
* different TTL than it did on the turn's first request, billing a second
|
|
157
|
-
* write for one reusable prefix. Anchoring on the turn start keeps the signal
|
|
158
|
-
* constant for every request in the turn.
|
|
159
|
-
*
|
|
160
|
-
* Detection uses {@link MEMORY_SPOTLIGHT_MATCHER}, the same full-wrapper
|
|
161
|
-
* matcher {@link stripSpotlightInjections} strips with, so the two can never
|
|
162
|
-
* disagree about which blocks are ephemeral.
|
|
163
|
-
*/
|
|
164
|
-
export function turnStartUserMessageHasSpotlight(messages: Message[]): boolean {
|
|
165
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
166
|
-
const message = messages[i];
|
|
167
|
-
if (message.role !== "user") {
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
const textBlocks = message.content.filter((block) => block.type === "text");
|
|
171
|
-
if (textBlocks.length === 0) {
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
return textBlocks.some((block) =>
|
|
175
|
-
textBlockMatchesInjection(block.text, [MEMORY_SPOTLIGHT_MATCHER]),
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
142
|
/** `<NOW.md>` scratchpad prefixes (current tag, pre-line-limit variant, legacy `<now_scratchpad>`) — shared with `stripNowScratchpad` so the two strip paths can't drift. */
|
|
182
143
|
export const NOW_SCRATCHPAD_STRIP_PREFIXES: InjectionMatcher[] = [
|
|
183
144
|
"<NOW.md Always keep this up to date",
|
|
@@ -217,10 +178,10 @@ export const RUNTIME_INJECTION_PREFIXES: InjectionMatcher[] = [
|
|
|
217
178
|
// matches the full-wrapper requirement in `countMemoryPrefixBlocks`.
|
|
218
179
|
{ prefix: "<memory>\n", suffix: "\n</memory>" },
|
|
219
180
|
{ prefix: "<info>\n", suffix: "\n</info>" },
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
181
|
+
// Memory-v3 per-turn spotlight blocks. Compaction rewrites history, so
|
|
182
|
+
// it strips them along with the other runtime injections and re-injects
|
|
183
|
+
// a fresh copy on the new tail. Full-wrapper shape for the same reason
|
|
184
|
+
// as `<memory>`.
|
|
224
185
|
MEMORY_SPOTLIGHT_MATCHER,
|
|
225
186
|
"<voice_call_control>",
|
|
226
187
|
"<workspace_top_level>", // backward-compat: strip legacy workspace blocks
|
|
@@ -312,6 +312,45 @@ describe("mergeConsecutiveAssistantMessages", () => {
|
|
|
312
312
|
expect(mergedIdMap.get("anchor")).toEqual(["tail"]);
|
|
313
313
|
});
|
|
314
314
|
|
|
315
|
+
test("a row deleted on its channel never merges, in either direction", () => {
|
|
316
|
+
// The deletion lives in the provider envelope, not in `messageKind`, and
|
|
317
|
+
// a merged run keeps the anchor's metadata: folding would drop the
|
|
318
|
+
// deletion or claim it over text the channel still shows.
|
|
319
|
+
const deleted = makeMsg(
|
|
320
|
+
"assistant",
|
|
321
|
+
JSON.stringify([{ type: "text", text: "gone from Slack" }]),
|
|
322
|
+
{
|
|
323
|
+
id: "deleted",
|
|
324
|
+
metadata: JSON.stringify({
|
|
325
|
+
slackMeta: JSON.stringify({
|
|
326
|
+
source: "slack",
|
|
327
|
+
channelId: "C1",
|
|
328
|
+
channelTs: "1725100000.000100",
|
|
329
|
+
eventKind: "message",
|
|
330
|
+
deletedAt: 1725100001000,
|
|
331
|
+
}),
|
|
332
|
+
}),
|
|
333
|
+
},
|
|
334
|
+
);
|
|
335
|
+
const { messages, mergedIdMap } = mergeConsecutiveAssistantMessages([
|
|
336
|
+
makeMsg("user", "hi"),
|
|
337
|
+
makeMsg("assistant", JSON.stringify([{ type: "text", text: "A" }]), {
|
|
338
|
+
id: "before",
|
|
339
|
+
}),
|
|
340
|
+
deleted,
|
|
341
|
+
makeMsg("assistant", JSON.stringify([{ type: "text", text: "B" }]), {
|
|
342
|
+
id: "after",
|
|
343
|
+
}),
|
|
344
|
+
]);
|
|
345
|
+
expect(messages.map((m) => m.id)).toEqual([
|
|
346
|
+
messages[0].id,
|
|
347
|
+
"before",
|
|
348
|
+
"deleted",
|
|
349
|
+
"after",
|
|
350
|
+
]);
|
|
351
|
+
expect(mergedIdMap.size).toBe(0);
|
|
352
|
+
});
|
|
353
|
+
|
|
315
354
|
test("leaves a single assistant row unchanged", () => {
|
|
316
355
|
const messages = [
|
|
317
356
|
makeMsg("user", "hi"),
|
|
@@ -40,10 +40,11 @@ const log = getLogger("message-consolidation");
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* True when an assistant row is a standalone display turn (system card,
|
|
43
|
-
* provider-error notice,
|
|
43
|
+
* provider-error notice, deliberate-silence marker, reaction, or a row
|
|
44
|
+
* deleted on its channel) that never merges with adjacent assistant rows in
|
|
44
45
|
* either direction. Merging one into an adjacent run would let the anchor's
|
|
45
|
-
* metadata win and drop the
|
|
46
|
-
*
|
|
46
|
+
* metadata win and drop the marker from the wire (or stamp it onto a bubble
|
|
47
|
+
* containing real assistant text).
|
|
47
48
|
*/
|
|
48
49
|
function isStandaloneAssistantRow(msg: MessageRow): boolean {
|
|
49
50
|
return isStandaloneAssistantMessage(msg.role, msg.metadata);
|
|
@@ -38,6 +38,11 @@ import {
|
|
|
38
38
|
expectedScopesForStoredToken,
|
|
39
39
|
scopeDifference,
|
|
40
40
|
} from "../oauth/scope-utils.js";
|
|
41
|
+
import { credentialKey } from "../security/credential-key.js";
|
|
42
|
+
import {
|
|
43
|
+
getSecureKeyAsync,
|
|
44
|
+
getSecureKeyResultAsync,
|
|
45
|
+
} from "../security/secure-keys.js";
|
|
41
46
|
import {
|
|
42
47
|
TokenExpiredError,
|
|
43
48
|
withValidToken,
|
|
@@ -610,6 +615,131 @@ async function checkManagedProvider(
|
|
|
610
615
|
return results;
|
|
611
616
|
}
|
|
612
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Connection id for the Vellum-managed assistant credential.
|
|
620
|
+
*
|
|
621
|
+
* The credential is a single workspace-wide slot rather than one row per
|
|
622
|
+
* account, so it carries the credential-store key as its id instead of a
|
|
623
|
+
* connection uuid.
|
|
624
|
+
*/
|
|
625
|
+
const ASSISTANT_API_KEY_CONNECTION_ID = "vellum:assistant_api_key";
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Health of the platform-provisioned assistant API key, the credential that
|
|
629
|
+
* authenticates Vellum-managed inference.
|
|
630
|
+
*
|
|
631
|
+
* Deliberately not part of {@link checkAllCredentials} yet. Adding it there
|
|
632
|
+
* makes the heartbeat raise a `credential.health_alert` for this credential,
|
|
633
|
+
* and that alert has nowhere to send the reader until the notification carries
|
|
634
|
+
* the repair. It is used on demand for now, by the route a client calls to
|
|
635
|
+
* confirm a credential it just wrote.
|
|
636
|
+
*
|
|
637
|
+
* This is the one credential the assistant cannot mint for itself: the
|
|
638
|
+
* platform issues it and a signed-in client writes it in. For a self-hosted
|
|
639
|
+
* or local assistant the platform deliberately does not self-heal a dead key
|
|
640
|
+
* (`recover_expired_assistant_api_key` excludes registrations of that kind,
|
|
641
|
+
* because the client owns the reprovision flow), so its health has to be
|
|
642
|
+
* observed here and acted on by a client.
|
|
643
|
+
*
|
|
644
|
+
* Statuses map onto the shared vocabulary:
|
|
645
|
+
* - `missing_token`: no key is stored, so managed inference cannot run.
|
|
646
|
+
* - `revoked`: the platform settled that the stored key is not accepted.
|
|
647
|
+
* - `unreachable`: the credential store or the platform did not answer, so
|
|
648
|
+
* health is unknown. The heartbeat drops these before notifying, which is
|
|
649
|
+
* what keeps a transient outage from raising a credential alert.
|
|
650
|
+
*
|
|
651
|
+
* `canAutoRecover` is true for both failure statuses: recovery needs no
|
|
652
|
+
* re-authorization from the user, only a signed-in client to rotate the key.
|
|
653
|
+
*/
|
|
654
|
+
export async function checkAssistantApiKey(): Promise<CredentialHealthResult | null> {
|
|
655
|
+
const base: Omit<
|
|
656
|
+
CredentialHealthResult,
|
|
657
|
+
"status" | "details" | "canAutoRecover"
|
|
658
|
+
> = {
|
|
659
|
+
connectionId: ASSISTANT_API_KEY_CONNECTION_ID,
|
|
660
|
+
provider: "vellum",
|
|
661
|
+
accountInfo: null,
|
|
662
|
+
missingScopes: [],
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
const stored = await getSecureKeyResultAsync(
|
|
666
|
+
credentialKey("vellum", "assistant_api_key"),
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
if (stored.unreachable) {
|
|
670
|
+
return {
|
|
671
|
+
...base,
|
|
672
|
+
status: "unreachable",
|
|
673
|
+
details:
|
|
674
|
+
"The credential store is unreachable, so Vellum's managed credentials could not be checked.",
|
|
675
|
+
canAutoRecover: false,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (stored.value == null) {
|
|
680
|
+
// A workspace that was never connected to the platform has no managed
|
|
681
|
+
// credential and is not unhealthy. Only an install that already carries
|
|
682
|
+
// the rest of a platform identity is missing something.
|
|
683
|
+
const baseUrl = await getSecureKeyAsync(
|
|
684
|
+
credentialKey("vellum", "platform_base_url"),
|
|
685
|
+
);
|
|
686
|
+
if (!baseUrl) {
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
return {
|
|
690
|
+
...base,
|
|
691
|
+
status: "missing_token",
|
|
692
|
+
details:
|
|
693
|
+
"No Vellum-managed credentials are stored yet. Log in to the Vellum platform to set them up.",
|
|
694
|
+
canAutoRecover: true,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const { VellumPlatformClient } = await import("../platform/client.js");
|
|
699
|
+
const client = await VellumPlatformClient.create();
|
|
700
|
+
if (!client) {
|
|
701
|
+
return {
|
|
702
|
+
...base,
|
|
703
|
+
status: "unreachable",
|
|
704
|
+
details:
|
|
705
|
+
"The Vellum platform client could not be built, so Vellum's managed credentials could not be checked.",
|
|
706
|
+
canAutoRecover: false,
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const verdict = await client.verifyCredential();
|
|
711
|
+
if (verdict === "rejected") {
|
|
712
|
+
return {
|
|
713
|
+
...base,
|
|
714
|
+
status: "revoked",
|
|
715
|
+
// Names the recovery the app actually performs. A replacement is
|
|
716
|
+
// provisioned only when someone asks for one, from the repair action or
|
|
717
|
+
// by logging in from the command line, so the copy points at that
|
|
718
|
+
// action rather than at reconnecting something the reader never
|
|
719
|
+
// connected.
|
|
720
|
+
details:
|
|
721
|
+
"Vellum's managed credentials stopped working, so chat and background tasks that use them are paused. Restoring takes a moment and will not affect anything else.",
|
|
722
|
+
canAutoRecover: true,
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
if (verdict === "unknown") {
|
|
726
|
+
return {
|
|
727
|
+
...base,
|
|
728
|
+
status: "unreachable",
|
|
729
|
+
details:
|
|
730
|
+
"Vellum did not answer the managed-credential check. Health is unknown until the next check.",
|
|
731
|
+
canAutoRecover: false,
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return {
|
|
736
|
+
...base,
|
|
737
|
+
status: "healthy",
|
|
738
|
+
details: "Vellum's managed credentials are working.",
|
|
739
|
+
canAutoRecover: true,
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
613
743
|
// ── Public API ────────────────────────────────────────────────────────
|
|
614
744
|
|
|
615
745
|
/**
|
|
@@ -171,6 +171,37 @@ describe("isToolActiveForContext - client OS eligibility", () => {
|
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
+
test("falls back to linux client OS from the linux transport interface", () => {
|
|
175
|
+
const skillId = "linux-transport-client-os-test-skill";
|
|
176
|
+
registerSkillTools(skillId, [
|
|
177
|
+
finalizeTool({
|
|
178
|
+
name: "linux_transport_os_test_tool",
|
|
179
|
+
supportedClientOs: ["linux"],
|
|
180
|
+
}),
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
expect(
|
|
185
|
+
isToolActiveForContext(
|
|
186
|
+
"linux_transport_os_test_tool",
|
|
187
|
+
makeCtx({
|
|
188
|
+
transportInterface: "linux",
|
|
189
|
+
}),
|
|
190
|
+
),
|
|
191
|
+
).toBe(true);
|
|
192
|
+
expect(
|
|
193
|
+
isToolActiveForContext(
|
|
194
|
+
"linux_transport_os_test_tool",
|
|
195
|
+
makeCtx({
|
|
196
|
+
transportInterface: "windows",
|
|
197
|
+
}),
|
|
198
|
+
),
|
|
199
|
+
).toBe(false);
|
|
200
|
+
} finally {
|
|
201
|
+
unregisterSkillTools(skillId);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
174
205
|
test("uses pinned client OS without falling through to live context", () => {
|
|
175
206
|
const skillId = "pinned-client-os-test-skill";
|
|
176
207
|
registerSkillTools(skillId, [
|
|
@@ -26,8 +26,6 @@ import type { ProviderMessageMetadata } from "../messaging/provider-message-meta
|
|
|
26
26
|
import {
|
|
27
27
|
formatSlackTimezoneLabel,
|
|
28
28
|
isSlackTs,
|
|
29
|
-
type SlackMessageMetadata,
|
|
30
|
-
writeSlackMetadata,
|
|
31
29
|
} from "../messaging/providers/slack/message-metadata.js";
|
|
32
30
|
import {
|
|
33
31
|
recordCompactionEndBestEffort,
|
|
@@ -1345,9 +1343,9 @@ function resolveAssistantReplyTimestampTimezone(ctx: Conversation): string {
|
|
|
1345
1343
|
* snapshot that handleMessageComplete used to compute at end-of-turn. All
|
|
1346
1344
|
* inputs (channel context, trust context, turnStartedAt) are stable across
|
|
1347
1345
|
* the LLM call, so building this once at reserve is equivalent to building
|
|
1348
|
-
* it at complete.
|
|
1349
|
-
*
|
|
1350
|
-
*
|
|
1346
|
+
* it at complete. The envelope's `messageId` stays absent here and is
|
|
1347
|
+
* back-filled by `deliverReplyViaCallback` after the transport returns the
|
|
1348
|
+
* sent message's id.
|
|
1351
1349
|
*/
|
|
1352
1350
|
function buildAssistantChannelMetadata(
|
|
1353
1351
|
state: EventHandlerState,
|
|
@@ -1363,61 +1361,59 @@ function buildAssistantChannelMetadata(
|
|
|
1363
1361
|
sentAt: state.turnStartedAt,
|
|
1364
1362
|
};
|
|
1365
1363
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
//
|
|
1369
|
-
|
|
1370
|
-
// inbound repoint the reply mid-turn, and could stamp one actor's class
|
|
1371
|
-
// beside another actor's routing identity.
|
|
1372
|
-
const rowTrust = turnOrRestingTrust(deps.ctx);
|
|
1373
|
-
const channelId = rowTrust?.requesterChatId;
|
|
1374
|
-
if (channelId) {
|
|
1375
|
-
// This turn's own inbound thread id (the same field guardian-approval
|
|
1376
|
-
// cards read). Deliberately not the shared conversation binding: on a
|
|
1377
|
-
// legacy flat→thread aliased Slack conversation a concurrent inbound
|
|
1378
|
-
// can rewrite the binding's externalThreadId mid-turn.
|
|
1379
|
-
const turnThreadTs = rowTrust?.sourceThreadId;
|
|
1380
|
-
const threadTs = isSlackTs(turnThreadTs) ? turnThreadTs : undefined;
|
|
1381
|
-
const timestampTimezone = resolveAssistantReplyTimestampTimezone(
|
|
1382
|
-
deps.ctx,
|
|
1383
|
-
);
|
|
1384
|
-
const timestampTimezoneLabel = formatSlackTimezoneLabel(
|
|
1385
|
-
timestampTimezone,
|
|
1386
|
-
{ nowMs: state.turnStartedAt },
|
|
1387
|
-
);
|
|
1388
|
-
const partialSlackMeta: Partial<SlackMessageMetadata> = {
|
|
1389
|
-
source: "slack",
|
|
1390
|
-
eventKind: "message",
|
|
1391
|
-
channelId,
|
|
1392
|
-
...(threadTs ? { threadTs } : {}),
|
|
1393
|
-
timestampTimezone,
|
|
1394
|
-
...(timestampTimezoneLabel ? { timestampTimezoneLabel } : {}),
|
|
1395
|
-
};
|
|
1396
|
-
// `channelTs` is filled in by the post-send reconciliation step in
|
|
1397
|
-
// `deliverReplyViaCallback`; cast through the Partial to satisfy
|
|
1398
|
-
// the writer's type at this pre-send boundary.
|
|
1399
|
-
metadata.slackMeta = writeSlackMetadata(
|
|
1400
|
-
partialSlackMeta as SlackMessageMetadata,
|
|
1401
|
-
);
|
|
1402
|
-
}
|
|
1403
|
-
} else if (deps.turnChannelContext.assistantMessageChannel !== "vellum") {
|
|
1404
|
-
// Every other channel row describes itself in the neutral envelope
|
|
1405
|
-
// `readProviderMetadata` serves, the shape a plugin channel writes too.
|
|
1406
|
-
// `messageId` stays absent here and is back-filled by the post-send
|
|
1407
|
-
// reconciliation once the transport returns the sent message's id, which
|
|
1408
|
-
// is what lets a later reaction on this row resolve back to it. No
|
|
1409
|
-
// `threadId`: a value there asserts a thread exists, and the reply's
|
|
1410
|
-
// routing thread is delivery state, not a fact about this row.
|
|
1411
|
-
const chatId = turnOrRestingTrust(deps.ctx)?.requesterChatId;
|
|
1412
|
-
if (chatId) {
|
|
1413
|
-
metadata.providerMeta = JSON.stringify({
|
|
1414
|
-
source: deps.turnChannelContext.assistantMessageChannel,
|
|
1415
|
-
conversationExternalId: chatId,
|
|
1416
|
-
eventKind: "message",
|
|
1417
|
-
} satisfies ProviderMessageMetadata);
|
|
1418
|
-
}
|
|
1364
|
+
const channel = deps.turnChannelContext.assistantMessageChannel;
|
|
1365
|
+
if (channel === "vellum") {
|
|
1366
|
+
// A vellum turn is not a provider message: no envelope.
|
|
1367
|
+
return metadata;
|
|
1419
1368
|
}
|
|
1420
|
-
|
|
1369
|
+
// Every channel row describes itself in the neutral envelope
|
|
1370
|
+
// `readProviderMetadata` serves, the shape a plugin channel writes too.
|
|
1371
|
+
// `messageId` stays absent here and is back-filled by the post-send
|
|
1372
|
+
// reconciliation once the transport returns the sent message's id, which
|
|
1373
|
+
// is what lets a later reaction or delete on this row resolve back to it.
|
|
1374
|
+
//
|
|
1375
|
+
// The whole envelope resolves from one turn-local snapshot: the actor the
|
|
1376
|
+
// provenance above names is the actor whose channel and thread the row
|
|
1377
|
+
// routes to. Reading the live slot here instead would let a concurrent
|
|
1378
|
+
// inbound repoint the reply mid-turn, and could stamp one actor's class
|
|
1379
|
+
// beside another actor's routing identity.
|
|
1380
|
+
const rowTrust = turnOrRestingTrust(deps.ctx);
|
|
1381
|
+
const chatId = rowTrust?.requesterChatId;
|
|
1382
|
+
if (!chatId) {
|
|
1383
|
+
return metadata;
|
|
1384
|
+
}
|
|
1385
|
+
const envelope: ProviderMessageMetadata = {
|
|
1386
|
+
source: channel,
|
|
1387
|
+
conversationExternalId: chatId,
|
|
1388
|
+
eventKind: "message",
|
|
1389
|
+
};
|
|
1390
|
+
if (channel === "slack") {
|
|
1391
|
+
// Slack's own facts ride the schema's passthrough and come back through
|
|
1392
|
+
// the envelope's Slack view (`slackViewOfProviderMetadata`). The thread
|
|
1393
|
+
// is this turn's own inbound thread id (the same field guardian-approval
|
|
1394
|
+
// cards read), stated because the reply is posted in it and the Slack
|
|
1395
|
+
// transcript is assembled by thread. Deliberately not the shared
|
|
1396
|
+
// conversation binding: on a legacy flat-to-thread aliased Slack
|
|
1397
|
+
// conversation a concurrent inbound can rewrite the binding's
|
|
1398
|
+
// externalThreadId mid-turn.
|
|
1399
|
+
const turnThreadTs = rowTrust?.sourceThreadId;
|
|
1400
|
+
const threadTs = isSlackTs(turnThreadTs) ? turnThreadTs : undefined;
|
|
1401
|
+
const timestampTimezone = resolveAssistantReplyTimestampTimezone(deps.ctx);
|
|
1402
|
+
const timestampTimezoneLabel = formatSlackTimezoneLabel(timestampTimezone, {
|
|
1403
|
+
nowMs: state.turnStartedAt,
|
|
1404
|
+
});
|
|
1405
|
+
metadata.providerMeta = JSON.stringify({
|
|
1406
|
+
...envelope,
|
|
1407
|
+
...(threadTs ? { threadId: threadTs } : {}),
|
|
1408
|
+
...(timestampTimezone ? { timestampTimezone } : {}),
|
|
1409
|
+
...(timestampTimezoneLabel ? { timestampTimezoneLabel } : {}),
|
|
1410
|
+
});
|
|
1411
|
+
return metadata;
|
|
1412
|
+
}
|
|
1413
|
+
// No `threadId` on the other channels: a value there asserts a thread
|
|
1414
|
+
// exists, and the reply's routing thread is delivery state, not a fact
|
|
1415
|
+
// about this row.
|
|
1416
|
+
metadata.providerMeta = JSON.stringify(envelope);
|
|
1421
1417
|
return metadata;
|
|
1422
1418
|
}
|
|
1423
1419
|
|
|
@@ -359,6 +359,8 @@ function connectionResolutionUserMessage(
|
|
|
359
359
|
return `No provider connection is configured${usedBy}. Ask me to set one up right here, or add an API key in ${fixPath}.`;
|
|
360
360
|
case "unroutable_managed_model":
|
|
361
361
|
return `The model "${error.model ?? "<unset>"}"${usedBy} isn't served by the Vellum managed route. Pick a model from the Vellum catalog, or choose a concrete provider in ${fixPath}.`;
|
|
362
|
+
case "adapter_unavailable":
|
|
363
|
+
return `${connection}${usedBy} could not serve model "${error.model ?? "<unset>"}". This model is only available on the Vellum GPU route and was not sent through another provider.`;
|
|
362
364
|
case "missing_credential":
|
|
363
365
|
// Provider-neutral: api_key connections store keys, oauth_subscription
|
|
364
366
|
// connections store login tokens — the fix differs but the location
|