@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/ARCHITECTURE.md
CHANGED
|
@@ -120,7 +120,7 @@ Scoped approval grants allow a guardian's approval decision on one channel (e.g.
|
|
|
120
120
|
|
|
121
121
|
### Guardian Decision Primitive
|
|
122
122
|
|
|
123
|
-
All guardian approval decisions
|
|
123
|
+
All guardian approval decisions, regardless of how they arrive, route through a single primitive in `src/approvals/guardian-decision-primitive.ts`, which centralizes decision logic for callback button handlers, the conversational approval engine, and channel text replies.
|
|
124
124
|
|
|
125
125
|
**Core API:**
|
|
126
126
|
|
|
@@ -146,7 +146,7 @@ All guardian approval decisions — regardless of how they arrive — route thro
|
|
|
146
146
|
**Text fallback path (always available):**
|
|
147
147
|
|
|
148
148
|
- Every prompt includes a `requestCode` (6-char alphanumeric). Guardians can reply with `<requestCode> approve` or `<requestCode> reject` on any channel.
|
|
149
|
-
- `access_request` prompts
|
|
149
|
+
- `access_request` prompts carry the request-code verify/trust/reject/block directive in the card's `plainTextFallback`, appended by a transport only when it sends text without buttons; the `"open invite flow"` phrase for starting the Trusted Contacts invite flow stays in the text on every surface because nothing offers a button for it.
|
|
150
150
|
- `pending_question` prompts (voice-originated) support `<requestCode> <your answer>` for free-text answers.
|
|
151
151
|
- The `routeGuardianReply` router processes text replies through a priority-ordered pipeline: callback parsing -> request code parsing -> NL classification. All paths converge on `applyGuardianDecision`.
|
|
152
152
|
|
package/Dockerfile
CHANGED
|
@@ -186,53 +186,11 @@ RUN printf '%s\n' \
|
|
|
186
186
|
>> /home/assistant/.bashrc && \
|
|
187
187
|
chown assistant:assistant /home/assistant/.bashrc
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
' exec /usr/bin/apt-get "$@"' \
|
|
195
|
-
'fi' \
|
|
196
|
-
'export DEBIAN_FRONTEND=noninteractive' \
|
|
197
|
-
'DATA_ROOT="${VELLUM_APT_DATA_ROOT:-/data/system}"' \
|
|
198
|
-
'/app/assistant/docker-init-apt-root.sh' \
|
|
199
|
-
'if [ -x "${DATA_ROOT}/bin/sh" ] && [ -x "${DATA_ROOT}/usr/bin/apt-get" ] && [ -f "${DATA_ROOT}/.rootfs-initialized" ] && ! grep -qs " ${DATA_ROOT} .*noexec" /proc/mounts; then' \
|
|
200
|
-
' exec chroot "${DATA_ROOT}" /usr/bin/apt-get "$@"' \
|
|
201
|
-
'fi' \
|
|
202
|
-
'exec /usr/bin/apt-get "$@"' \
|
|
203
|
-
> /usr/local/bin/apt-get && \
|
|
204
|
-
chmod +x /usr/local/bin/apt-get && \
|
|
205
|
-
printf '%s\n' \
|
|
206
|
-
'#!/usr/bin/env sh' \
|
|
207
|
-
'set -eu' \
|
|
208
|
-
'. /app/assistant/docker-kata-runtime-family.sh' \
|
|
209
|
-
'if ! vellum_is_kata_family_runtime; then' \
|
|
210
|
-
' exec /usr/bin/apt "$@"' \
|
|
211
|
-
'fi' \
|
|
212
|
-
'export DEBIAN_FRONTEND=noninteractive' \
|
|
213
|
-
'DATA_ROOT="${VELLUM_APT_DATA_ROOT:-/data/system}"' \
|
|
214
|
-
'/app/assistant/docker-init-apt-root.sh' \
|
|
215
|
-
'if [ -x "${DATA_ROOT}/bin/sh" ] && [ -x "${DATA_ROOT}/usr/bin/apt" ] && [ -f "${DATA_ROOT}/.rootfs-initialized" ] && ! grep -qs " ${DATA_ROOT} .*noexec" /proc/mounts; then' \
|
|
216
|
-
' exec chroot "${DATA_ROOT}" /usr/bin/apt "$@"' \
|
|
217
|
-
'fi' \
|
|
218
|
-
'exec /usr/bin/apt "$@"' \
|
|
219
|
-
> /usr/local/bin/apt && \
|
|
220
|
-
chmod +x /usr/local/bin/apt && \
|
|
221
|
-
printf '%s\n' \
|
|
222
|
-
'#!/usr/bin/env sh' \
|
|
223
|
-
'set -eu' \
|
|
224
|
-
'. /app/assistant/docker-kata-runtime-family.sh' \
|
|
225
|
-
'if ! vellum_is_kata_family_runtime; then' \
|
|
226
|
-
' exec /usr/bin/dpkg "$@"' \
|
|
227
|
-
'fi' \
|
|
228
|
-
'DATA_ROOT="${VELLUM_APT_DATA_ROOT:-/data/system}"' \
|
|
229
|
-
'/app/assistant/docker-init-apt-root.sh' \
|
|
230
|
-
'if [ -x "${DATA_ROOT}/bin/sh" ] && [ -x "${DATA_ROOT}/usr/bin/dpkg" ] && [ -f "${DATA_ROOT}/.rootfs-initialized" ] && ! grep -qs " ${DATA_ROOT} .*noexec" /proc/mounts; then' \
|
|
231
|
-
' exec chroot "${DATA_ROOT}" /usr/bin/dpkg "$@"' \
|
|
232
|
-
'fi' \
|
|
233
|
-
'exec /usr/bin/dpkg "$@"' \
|
|
234
|
-
> /usr/local/bin/dpkg && \
|
|
235
|
-
chmod +x /usr/local/bin/dpkg
|
|
189
|
+
# apt/apt-get/dpkg must persist like pip: on Kata-family runtimes the wrapper
|
|
190
|
+
# routes package operations into the persistent apt chroot.
|
|
191
|
+
RUN ln -s /app/assistant/docker-kata-apt-wrapper.sh /usr/local/bin/apt-get && \
|
|
192
|
+
ln -s /app/assistant/docker-kata-apt-wrapper.sh /usr/local/bin/apt && \
|
|
193
|
+
ln -s /app/assistant/docker-kata-apt-wrapper.sh /usr/local/bin/dpkg
|
|
236
194
|
|
|
237
195
|
# pip must persist like apt: on Kata-family runtimes the wrapper routes root
|
|
238
196
|
# installs into the persistent apt chroot.
|
|
@@ -274,8 +232,10 @@ RUN chmod +x \
|
|
|
274
232
|
/app/assistant/docker-entrypoint.sh \
|
|
275
233
|
/app/assistant/docker-init-apt-root.sh \
|
|
276
234
|
/app/assistant/docker-kata-apt-env.sh \
|
|
235
|
+
/app/assistant/docker-kata-apt-shims.sh \
|
|
236
|
+
/app/assistant/docker-kata-apt-wrapper.sh \
|
|
237
|
+
/app/assistant/docker-kata-chroot-exec.sh \
|
|
277
238
|
/app/assistant/docker-kata-pip.sh \
|
|
278
|
-
/app/assistant/docker-kata-pip-chroot.sh \
|
|
279
239
|
/app/assistant/docker-kata-runtime-family.sh \
|
|
280
240
|
/usr/local/bin/vellum-block-volume-common.sh \
|
|
281
241
|
/usr/local/bin/vellum-block-volume-init.sh \
|
package/docker-entrypoint.sh
CHANGED
|
@@ -11,7 +11,11 @@ KATA_APT_INIT_PID=""
|
|
|
11
11
|
if vellum_is_kata_family_runtime && [ -x /app/assistant/docker-init-apt-root.sh ]; then
|
|
12
12
|
export VELLUM_APT_DATA_ROOT="${VELLUM_APT_DATA_ROOT:-/data/system}"
|
|
13
13
|
# Warm the chroot used by Kata-family apt wrappers without blocking assistant readiness.
|
|
14
|
-
|
|
14
|
+
(
|
|
15
|
+
/app/assistant/docker-init-apt-root.sh
|
|
16
|
+
# Refresh persisted wrapper-script shims before commands use the overlay.
|
|
17
|
+
/app/assistant/docker-kata-apt-shims.sh || true
|
|
18
|
+
) &
|
|
15
19
|
KATA_APT_INIT_PID="$!"
|
|
16
20
|
fi
|
|
17
21
|
|
package/docker-kata-apt-env.sh
CHANGED
|
@@ -36,6 +36,9 @@ _vellum_kata_prepend_library_path() {
|
|
|
36
36
|
esac
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
# Shims for chroot wrapper scripts with hardcoded absolute paths must shadow
|
|
40
|
+
# the broken originals in the chroot bin dirs below (docker-kata-apt-shims.sh).
|
|
41
|
+
_vellum_kata_append_path "${VELLUM_APT_DATA_ROOT}/.host-shims"
|
|
39
42
|
_vellum_kata_append_path "${VELLUM_APT_DATA_ROOT}/bin"
|
|
40
43
|
_vellum_kata_append_path "${VELLUM_APT_DATA_ROOT}/usr/local/sbin"
|
|
41
44
|
_vellum_kata_append_path "${VELLUM_APT_DATA_ROOT}/usr/local/bin"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Regenerates host-side shims for chroot-installed commands whose bin entry
|
|
3
|
+
# is a pure trampoline script exec'ing a hardcoded absolute path (Debian
|
|
4
|
+
# 7zip's /usr/bin/7z execs /usr/lib/7zip/7z, for example). Those targets only
|
|
5
|
+
# exist inside the chroot, so running the wrapper through the host PATH
|
|
6
|
+
# overlay fails; each shim execs the chroot-prefixed target instead.
|
|
7
|
+
# docker-kata-apt-env.sh and buildSanitizedEnv put the shim dir on PATH ahead
|
|
8
|
+
# of the chroot bin dirs so shims shadow the broken wrappers.
|
|
9
|
+
set -eu
|
|
10
|
+
|
|
11
|
+
DATA_ROOT="${VELLUM_APT_DATA_ROOT:-/data/system}"
|
|
12
|
+
SHIM_DIR="${DATA_ROOT}/.host-shims"
|
|
13
|
+
MARKER="# vellum-apt-shim:"
|
|
14
|
+
# dpkg-managed dirs that can hold trampolines (Debian policy keeps packages
|
|
15
|
+
# out of /usr/local; /bin and /sbin are usrmerge symlinks into these).
|
|
16
|
+
SCAN_DIRS="usr/bin usr/sbin usr/games"
|
|
17
|
+
# Every overlay bin dir in PATH priority order (usrmerge collapses bin/sbin
|
|
18
|
+
# into their usr counterparts): precedence must also see the /usr/local dirs
|
|
19
|
+
# that chroot pip installs populate, so a dpkg trampoline never shims over a
|
|
20
|
+
# higher-priority local command.
|
|
21
|
+
PRIORITY_DIRS="usr/bin usr/local/sbin usr/local/bin usr/sbin usr/games"
|
|
22
|
+
|
|
23
|
+
[ -d "${DATA_ROOT}/usr/bin" ] || exit 0
|
|
24
|
+
mkdir -p "${SHIM_DIR}"
|
|
25
|
+
|
|
26
|
+
# Scanning the bin dirs forks a few processes per file; skip it when the
|
|
27
|
+
# overlay state is unchanged (read-only invocations like `apt list` or
|
|
28
|
+
# `dpkg -l`). The stamp covers command names, links, effective modes, and small
|
|
29
|
+
# executable contents so reinstalls and pip changes invalidate stale shims.
|
|
30
|
+
STAMP_FILE="${SHIM_DIR}/.overlay-stamp"
|
|
31
|
+
STAMP="$(
|
|
32
|
+
{
|
|
33
|
+
md5sum "${DATA_ROOT}/var/lib/dpkg/status" 2>/dev/null
|
|
34
|
+
for d in ${PRIORITY_DIRS}; do
|
|
35
|
+
printf '%s\0' "${d}"
|
|
36
|
+
find "${DATA_ROOT}/${d}" -mindepth 1 -maxdepth 1 -printf '%P %y %m %l\0' 2>/dev/null | sort -z
|
|
37
|
+
find -L "${DATA_ROOT}/${d}" -mindepth 1 -maxdepth 1 -printf '%P %y %m\0' 2>/dev/null | sort -z
|
|
38
|
+
done
|
|
39
|
+
for d in ${SCAN_DIRS}; do
|
|
40
|
+
find -L "${DATA_ROOT}/${d}" -mindepth 1 -maxdepth 1 -type f -size -513c -perm /111 -print0 2>/dev/null |
|
|
41
|
+
sort -z |
|
|
42
|
+
xargs -0 -r md5sum -z
|
|
43
|
+
done
|
|
44
|
+
} | md5sum | cut -d' ' -f1
|
|
45
|
+
)"
|
|
46
|
+
if [ "${STAMP}" = "$(cat "${STAMP_FILE}" 2>/dev/null || true)" ]; then
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Prints the target of a pure trampoline: a tiny script whose body is nothing
|
|
51
|
+
# but the shebang, comments, blank lines, and a single `exec /abs/path "$@"`
|
|
52
|
+
# line. Wrappers that do anything else (export env vars, cd, pass extra
|
|
53
|
+
# flags) print nothing: a direct-exec shim would bypass that setup.
|
|
54
|
+
wrapper_target() {
|
|
55
|
+
[ -f "$1" ] && [ -x "$1" ] || return 0
|
|
56
|
+
[ "$(wc -c <"$1")" -le 512 ] || return 0
|
|
57
|
+
case "$(head -c 2 "$1" 2>/dev/null)" in
|
|
58
|
+
'#!') ;;
|
|
59
|
+
*) return 0 ;;
|
|
60
|
+
esac
|
|
61
|
+
body="$(sed -e '1d' -e '/^[[:space:]]*#/d' -e '/^[[:space:]]*$/d' "$1")"
|
|
62
|
+
[ -n "${body}" ] || return 0
|
|
63
|
+
[ "$(printf '%s' "${body}" | wc -l)" -eq 0 ] || return 0
|
|
64
|
+
printf '%s\n' "${body}" | sed -n 's/^[[:space:]]*exec[[:space:]][[:space:]]*\(\/[^"[:space:]][^"[:space:]]*\)[[:space:]][[:space:]]*"\$@"[[:space:]]*$/\1/p'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Highest-priority overlay dir that holds an entry for this shim name.
|
|
68
|
+
shim_source() {
|
|
69
|
+
for d in ${PRIORITY_DIRS}; do
|
|
70
|
+
candidate="${DATA_ROOT}/${d}/$1"
|
|
71
|
+
if [ -f "${candidate}" ] && [ -x "${candidate}" ]; then
|
|
72
|
+
printf '%s\n' "${candidate}"
|
|
73
|
+
return 0
|
|
74
|
+
fi
|
|
75
|
+
done
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
generation_failed=0
|
|
79
|
+
for d in ${SCAN_DIRS}; do
|
|
80
|
+
for bin in "${DATA_ROOT}/${d}/"*; do
|
|
81
|
+
# Shims outrank every chroot bin dir on PATH, so only the
|
|
82
|
+
# highest-priority entry for a name may produce one: a trampoline in a
|
|
83
|
+
# lower dir must not shadow a real command above it.
|
|
84
|
+
[ "$(shim_source "${bin##*/}")" = "${bin}" ] || continue
|
|
85
|
+
target="$(wrapper_target "${bin}")"
|
|
86
|
+
[ -n "${target}" ] || continue
|
|
87
|
+
# Only shim when the hardcoded path is broken on the host but real in
|
|
88
|
+
# the chroot.
|
|
89
|
+
if [ -e "${target}" ] || [ ! -x "${DATA_ROOT}${target}" ]; then
|
|
90
|
+
continue
|
|
91
|
+
fi
|
|
92
|
+
shim="${SHIM_DIR}/${bin##*/}"
|
|
93
|
+
if ! tmp="$(mktemp "${SHIM_DIR}/.shim.XXXXXX")"; then
|
|
94
|
+
generation_failed=1
|
|
95
|
+
continue
|
|
96
|
+
fi
|
|
97
|
+
if printf '%s\n' \
|
|
98
|
+
'#!/bin/sh' \
|
|
99
|
+
"${MARKER} ${target}" \
|
|
100
|
+
"exec \"${DATA_ROOT}${target}\" \"\$@\"" \
|
|
101
|
+
>"${tmp}" &&
|
|
102
|
+
chmod 0755 "${tmp}" &&
|
|
103
|
+
mv -f "${tmp}" "${shim}"; then
|
|
104
|
+
:
|
|
105
|
+
else
|
|
106
|
+
rm -f "${tmp}"
|
|
107
|
+
generation_failed=1
|
|
108
|
+
fi
|
|
109
|
+
done
|
|
110
|
+
done
|
|
111
|
+
|
|
112
|
+
if [ "${generation_failed}" -ne 0 ]; then
|
|
113
|
+
exit 1
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Drop shims that remain stale after desired replacements are installed.
|
|
117
|
+
for shim in "${SHIM_DIR}"/*; do
|
|
118
|
+
[ -f "${shim}" ] || continue
|
|
119
|
+
grep -qs "^${MARKER}" "${shim}" || continue
|
|
120
|
+
target="$(sed -n "s|^${MARKER} ||p" "${shim}" | sed -n '1p')"
|
|
121
|
+
src="$(shim_source "$(basename "${shim}")")"
|
|
122
|
+
if [ -z "${target}" ] || [ -z "${src}" ] || [ ! -x "${DATA_ROOT}${target}" ] || [ "$(wrapper_target "${src}")" != "${target}" ]; then
|
|
123
|
+
rm -f "${shim}"
|
|
124
|
+
fi
|
|
125
|
+
done
|
|
126
|
+
|
|
127
|
+
printf '%s\n' "${STAMP}" >"${STAMP_FILE}"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# apt/apt-get/dpkg wrapper (symlinked from /usr/local/bin). On Kata-family
|
|
3
|
+
# runtimes, package operations are routed into the persistent apt chroot so
|
|
4
|
+
# they survive machine saves; the image rootfs is discarded on every save.
|
|
5
|
+
# The chroot runs through docker-kata-chroot-exec.sh in a private mount
|
|
6
|
+
# namespace with /dev, /dev/pts and /proc available, so apt can allocate a
|
|
7
|
+
# pty for term.log and maintainer scripts see a normal system.
|
|
8
|
+
set -eu
|
|
9
|
+
|
|
10
|
+
TOOL_NAME="$(basename "$0")"
|
|
11
|
+
case "${TOOL_NAME}" in
|
|
12
|
+
apt | apt-get | dpkg) ;;
|
|
13
|
+
*) TOOL_NAME="apt-get" ;;
|
|
14
|
+
esac
|
|
15
|
+
|
|
16
|
+
. /app/assistant/docker-kata-runtime-family.sh
|
|
17
|
+
|
|
18
|
+
if ! vellum_is_kata_family_runtime; then
|
|
19
|
+
exec "/usr/bin/${TOOL_NAME}" "$@"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [ "${TOOL_NAME}" != "dpkg" ]; then
|
|
23
|
+
export DEBIAN_FRONTEND=noninteractive
|
|
24
|
+
fi
|
|
25
|
+
DATA_ROOT="${VELLUM_APT_DATA_ROOT:-/data/system}"
|
|
26
|
+
|
|
27
|
+
/app/assistant/docker-init-apt-root.sh
|
|
28
|
+
if [ -x "${DATA_ROOT}/bin/sh" ] && [ -x "${DATA_ROOT}/usr/bin/${TOOL_NAME}" ] && [ -f "${DATA_ROOT}/.rootfs-initialized" ] && ! grep -qs " ${DATA_ROOT} .*noexec" /proc/mounts; then
|
|
29
|
+
rc=0
|
|
30
|
+
# Platform kata pods are privileged inside their per-assistant VM, so
|
|
31
|
+
# unshare -m works there; if this environment can't create a mount
|
|
32
|
+
# namespace, fall back to a bare chroot: installs still persist, but with
|
|
33
|
+
# no /dev/pts or /proc apt logs a pty warning and the caller's cwd is not
|
|
34
|
+
# restored.
|
|
35
|
+
if unshare -m true 2>/dev/null; then
|
|
36
|
+
unshare -m /app/assistant/docker-kata-chroot-exec.sh "${DATA_ROOT}" "${PWD}" "/usr/bin/${TOOL_NAME}" "$@" || rc=$?
|
|
37
|
+
else
|
|
38
|
+
chroot "${DATA_ROOT}" "/usr/bin/${TOOL_NAME}" "$@" || rc=$?
|
|
39
|
+
fi
|
|
40
|
+
# Packages can install wrapper scripts that hardcode absolute paths which
|
|
41
|
+
# only resolve inside the chroot; refresh host-side shims for those.
|
|
42
|
+
/app/assistant/docker-kata-apt-shims.sh || true
|
|
43
|
+
exit "${rc}"
|
|
44
|
+
fi
|
|
45
|
+
exec "/usr/bin/${TOOL_NAME}" "$@"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Runs a command inside the persistent apt chroot. Must be invoked under
|
|
3
|
+
# `unshare -m` (see docker-kata-pip.sh and docker-kata-apt-wrapper.sh): the
|
|
4
|
+
# bind mounts below live in a private mount namespace so they disappear with
|
|
5
|
+
# the process and are never visible to other processes; a lingering /data
|
|
6
|
+
# bind inside the chroot would create a path cycle (/data/system/data/system/...)
|
|
7
|
+
# for anything walking /data.
|
|
8
|
+
set -eu
|
|
9
|
+
|
|
10
|
+
DATA_ROOT="$1"
|
|
11
|
+
CALLER_CWD="$2"
|
|
12
|
+
shift 2
|
|
13
|
+
|
|
14
|
+
for dir in /workspace /data /tmp /var/tmp; do
|
|
15
|
+
[ -d "${dir}" ] || continue
|
|
16
|
+
mkdir -p "${DATA_ROOT}${dir}"
|
|
17
|
+
mount --bind "${dir}" "${DATA_ROOT}${dir}" 2>/dev/null || true
|
|
18
|
+
done
|
|
19
|
+
|
|
20
|
+
# /proc and /dev make the chroot behave like a normal system: apt needs a pty
|
|
21
|
+
# (posix_openpt via /dev/ptmx) to write /var/log/apt/term.log, and package
|
|
22
|
+
# maintainer scripts routinely read /proc. The recursive /dev bind carries the
|
|
23
|
+
# container's /dev/pts along; the devpts mount is a fallback for environments
|
|
24
|
+
# where it did not produce one.
|
|
25
|
+
mkdir -p "${DATA_ROOT}/proc" "${DATA_ROOT}/dev"
|
|
26
|
+
mount --bind /proc "${DATA_ROOT}/proc" 2>/dev/null || true
|
|
27
|
+
mount --rbind /dev "${DATA_ROOT}/dev" 2>/dev/null || true
|
|
28
|
+
if [ ! -e "${DATA_ROOT}/dev/pts/ptmx" ]; then
|
|
29
|
+
mkdir -p "${DATA_ROOT}/dev/pts" 2>/dev/null || true
|
|
30
|
+
mount -t devpts -o gid=5,mode=620,ptmxmode=666 devpts "${DATA_ROOT}/dev/pts" 2>/dev/null || true
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# chroot(1) always chdirs to /; restore the caller's cwd when it exists inside
|
|
34
|
+
# the chroot so relative paths keep working.
|
|
35
|
+
exec chroot "${DATA_ROOT}" /bin/sh -c 'cd "$1" 2>/dev/null || cd /; shift; exec "$@"' sh "${CALLER_CWD}" "$@"
|
package/docker-kata-pip.sh
CHANGED
|
@@ -38,10 +38,16 @@ if [ "$(id -u)" = "0" ]; then
|
|
|
38
38
|
# unshare -m works there; if this environment can't create a mount
|
|
39
39
|
# namespace, fall back to a bare chroot — index installs still persist,
|
|
40
40
|
# only caller-path installs lose visibility.
|
|
41
|
+
rc=0
|
|
41
42
|
if unshare -m true 2>/dev/null; then
|
|
42
|
-
|
|
43
|
+
unshare -m /app/assistant/docker-kata-chroot-exec.sh "${DATA_ROOT}" "${PWD}" "/usr/bin/${PIP_NAME}" "$@" || rc=$?
|
|
44
|
+
else
|
|
45
|
+
chroot "${DATA_ROOT}" /bin/sh -c 'cd "$1" 2>/dev/null || cd /; shift; exec "$@"' sh "${PWD}" "/usr/bin/${PIP_NAME}" "$@" || rc=$?
|
|
43
46
|
fi
|
|
44
|
-
|
|
47
|
+
# pip can add usr/local commands that outrank existing shims on the
|
|
48
|
+
# PATH overlay; refresh so a stale shim never shadows them.
|
|
49
|
+
/app/assistant/docker-kata-apt-shims.sh || true
|
|
50
|
+
exit "${rc}"
|
|
45
51
|
fi
|
|
46
52
|
fi
|
|
47
53
|
echo "Warning: persistent pip root unavailable; falling back to the image pip (installs will not survive a save)" >&2
|
|
@@ -170,10 +170,14 @@ Ingested pages carry provenance frontmatter with distinct consumers:
|
|
|
170
170
|
|
|
171
171
|
### Read paths
|
|
172
172
|
|
|
173
|
-
- **v3 (live)**: per-turn lane selection over concept pages
|
|
174
|
-
retrieval
|
|
175
|
-
learned edges, entity/hot/fresh/core sets
|
|
176
|
-
card by `v3/injector.ts`.
|
|
173
|
+
- **v3 (live)**: per-turn lane selection over concept pages (dense/sparse
|
|
174
|
+
retrieval via `substrate/sim.ts` over the concept-page collection,
|
|
175
|
+
learned edges, entity/hot/fresh/core sets) rendered as the `<memory>`
|
|
176
|
+
card by `v3/injector.ts`. Frozen cards stay on historical user messages
|
|
177
|
+
so the provider prefix stays cacheable. Each turn's `<memory_spotlight>`
|
|
178
|
+
stays on the user message that was sent with it. A new spotlight is
|
|
179
|
+
added only on the new tail, so older messages are not rewritten. The
|
|
180
|
+
static `<info>` block
|
|
177
181
|
(`substrate/static-context.ts`: essentials/threads/recent/buffer) also
|
|
178
182
|
injects whenever the substrate is active.
|
|
179
183
|
- **v2 (transitional)**: activation/router engine in `v2/`
|
|
@@ -21,17 +21,17 @@ anti-pattern was retired in #35642 and again in the ask_question redesign).
|
|
|
21
21
|
notification pipeline (notifications/emit-signal.ts →
|
|
22
22
|
decision engine → destination resolver → decision-engine.ts → destination-resolver.ts →
|
|
23
23
|
broadcaster builds the card context ONCE broadcaster.ts: resolveApprovalContext /
|
|
24
|
-
(generic actions[] + plainTextFallback)
|
|
24
|
+
(generic actions[] + plainTextFallback) resolveQuestionContext)
|
|
25
25
|
│
|
|
26
26
|
▼ per-channel rendering ONLY
|
|
27
27
|
channel adapters (notifications/adapters/{telegram,slack,macos,platform},
|
|
28
28
|
telegram: inline keyboard; slack: blocks; vellum: conversation card via approval-card-builder)
|
|
29
29
|
card deliveries recorded per channel (guardian-delivery-recorder.ts → guardian_request_deliveries)
|
|
30
30
|
│
|
|
31
|
-
▼ user responds: button tap /
|
|
31
|
+
▼ user responds: button tap / "CODE <reply>" / bare text
|
|
32
32
|
guardian reply router (runtime/guardian-reply-router.ts, invoked from
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
callbacks → request codes → bare answer → routes/inbound-stages/guardian-reply-intercept.ts,
|
|
34
|
+
explicit approve/reject → NL BEFORE background dispatch: replies to parked
|
|
35
35
|
│ prompts resolve inline, never deferred)
|
|
36
36
|
▼ one decision primitive
|
|
37
37
|
applyGuardianDecision (approvals/guardian-decision-primitive.ts:
|
|
@@ -45,14 +45,14 @@ anti-pattern was retired in #35642 and again in the ask_question redesign).
|
|
|
45
45
|
|
|
46
46
|
## Who owns what
|
|
47
47
|
|
|
48
|
-
| Concern | Owner
|
|
49
|
-
| --------------------------------------- |
|
|
50
|
-
| The "what": card text, actions, options | broadcaster context build (once per broadcast)
|
|
51
|
-
| The "how": channel-native rendering | `notifications/adapters/<channel>`
|
|
52
|
-
| Request state | gateway `guardian_requests` (+ deliveries)
|
|
53
|
-
| Decisions | `applyGuardianDecision` (CAS, atomic ACL outcome)
|
|
54
|
-
| Kind-specific follow-through | resolver registry (`kind` → resolver)
|
|
55
|
-
| Reply understanding | guardian reply router (codes, buttons,
|
|
48
|
+
| Concern | Owner | Never |
|
|
49
|
+
| --------------------------------------- | ------------------------------------------------- | ----------------------------------- |
|
|
50
|
+
| The "what": card text, actions, options | broadcaster context build (once per broadcast) | built per-adapter |
|
|
51
|
+
| The "how": channel-native rendering | `notifications/adapters/<channel>` | domain logic, payload parsing |
|
|
52
|
+
| Request state | gateway `guardian_requests` (+ deliveries) | daemon-side request tables |
|
|
53
|
+
| Decisions | `applyGuardianDecision` (CAS, atomic ACL outcome) | inline decision logic at call sites |
|
|
54
|
+
| Kind-specific follow-through | resolver registry (`kind` → resolver) | switch statements in the router |
|
|
55
|
+
| Reply understanding | guardian reply router (codes, buttons, modes) | per-feature inbound intercepts |
|
|
56
56
|
|
|
57
57
|
## The canonical home-feed projection
|
|
58
58
|
|
|
@@ -131,6 +131,10 @@ routing after a restart even though the card is absent from the model's history.
|
|
|
131
131
|
Two instruction modes exist per request kind (`notifications/guardian-question-mode.ts`):
|
|
132
132
|
**approval** ("CODE approve" / approve–reject buttons) and **answer**
|
|
133
133
|
("CODE <your answer>" / option buttons). `pending_question` is answer-mode.
|
|
134
|
+
Whether a channel's copy carries the "CODE <reply>" instruction at all is one
|
|
135
|
+
rule, `guardianCopyCarriesReplyMechanics`: text chats do, while the vellum
|
|
136
|
+
bell and banner, the platform push, and Slack approval cards act through the
|
|
137
|
+
card and have it stripped.
|
|
134
138
|
|
|
135
139
|
## Worked example: `ask_question` on a channel
|
|
136
140
|
|
|
@@ -186,8 +190,8 @@ chat the turn is running in. On Slack that chat can be a shared room, and the
|
|
|
186
190
|
card carries the tool, a command preview and live buttons.
|
|
187
191
|
`resolveGuardianPromptDelivery` addresses it to the guardian's bound DM
|
|
188
192
|
instead, by chat id rather than user id because that address is written to the
|
|
189
|
-
delivery row and read back to
|
|
190
|
-
|
|
193
|
+
delivery row and read back to scope plain-text replies and edit the decided
|
|
194
|
+
card. It returns the address and its route together, since
|
|
191
195
|
the turn's own callback carries a `threadTs` naming a thread that does not
|
|
192
196
|
exist in the DM. When no private address resolves it returns nothing and the
|
|
193
197
|
prompt is left to the in-app confirmation, because the room is the disclosure
|
|
@@ -24,13 +24,7 @@ Design doc defining how unknown users gain access to a Vellum assistant via chan
|
|
|
24
24
|
2. **The message is denied at the ingress ACL.** The gateway resolves the actor against its ACL DB and stamps a trust verdict onto the inbound metadata; the daemon's ACL stage (`runtime/routes/inbound-stages/acl-enforcement.ts`) consumes that verdict, finds no active member, replies _"Hmm looks like you don't have access to talk to me. I'll let <guardian> know you tried talking to me and get back to you."_ and returns `{ denied: true, reason: 'not_a_member' }`.
|
|
25
25
|
3. **Notification pipeline alerts the guardian.** The denial triggers `notifyGuardianOfAccessRequest()` (`runtime/access-request-helper.ts`), which creates an access request in the gateway (`guardian_requests`, `kind: 'access_request'`, `toolName: 'ingress_access_request'`, via the `channels/gateway-guardian-requests.ts` client) and calls `emitNotificationSignal()` with `sourceEventName: 'ingress.access_request'`. The notification routes through the decision engine to the guardian's surfaces (vellum app, Telegram, Slack, etc.). The guardian sees who is requesting access, including a request code for approve/reject and an `open invite flow` option to start the Trusted Contacts invite flow.
|
|
26
26
|
|
|
27
|
-
**Access-request copy contract:** Every guardian-facing access-request notification
|
|
28
|
-
1. **Requester context** — best-available identity (display name, username, external ID, source channel), sanitized to prevent control-character injection.
|
|
29
|
-
2. **Request-code decision directive** — e.g., `Reply "A1B2C3 approve" to grant access or "A1B2C3 reject" to deny.`
|
|
30
|
-
3. **Invite directive** — the exact phrase `Reply "open invite flow" to start Trusted Contacts invite flow.`
|
|
31
|
-
4. **Revoked-member warning** (when applicable) — `Note: this user was previously revoked.`
|
|
32
|
-
|
|
33
|
-
Model-generated phrasing is permitted for the surrounding copy, but a post-generation enforcement step in the decision engine validates that all required directive elements are present. If any are missing, the full deterministic contract text is appended. This ensures the guardian can always parse and act on the notification regardless of LLM output quality.
|
|
27
|
+
**Access-request copy contract:** Every guardian-facing access-request notification carries the requester context (`buildAccessRequestContextText`): best-available identity (display name, username, external ID, source channel) sanitized to prevent control-character injection, the message preview, and a revoked-member warning when applicable (`Note: this user was previously revoked.`). The context also carries the invite directive (`Reply "open invite flow" to start Trusted Contacts invite flow.`), because no surface offers a button for the invite flow and typing the phrase is the only way to start it. The typed-reply mechanics are separate (`buildAccessRequestReplyMechanics`): the request-code directive (`Reply "A1B2C3 verify" ... "A1B2C3 trust" ... "A1B2C3 reject" ... "A1B2C3 block"`, without `verify` when no handshake is offered). It rides only in the card's `plainTextFallback`; a transport appends it when it sends text without buttons, and the decision engine strips any copy of it the model wrote into the composed text, so surfaces with buttons show the context alone.
|
|
34
28
|
|
|
35
29
|
**Guardian identity resolution** is anchored on the assistant's vellum principal (`resolveAnchoredGuardian`), so access requests cannot bind to stale or cross-assistant contacts:
|
|
36
30
|
1. A source-channel guardian binding (from the gateway contact list) that matches the vellum anchor principal.
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { AVATAR_COLORS } from "./colors.js";
|
|
2
2
|
import type { CharacterComponents } from "./types.js";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The white of an eye. Exported because a consumer that reduces an eye pair to
|
|
6
|
+
* a silhouette (an Android themed-icon mask, say) has to tell the sclera paths
|
|
7
|
+
* from the pupils, and matching on this is the only way to do that from the
|
|
8
|
+
* path list alone.
|
|
9
|
+
*/
|
|
10
|
+
export const SCLERA = "#F2F2F2";
|
|
5
11
|
const PUPIL = "#1A1A1A";
|
|
6
12
|
|
|
7
13
|
/**
|
package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json
CHANGED
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
"./redacted-credential": "./src/redacted-credential.ts",
|
|
23
23
|
"./secret-detection": "./src/secret-detection.ts",
|
|
24
24
|
"./stripe-currency": "./src/stripe-currency.ts",
|
|
25
|
-
"./error": "./src/error.ts"
|
|
25
|
+
"./error": "./src/error.ts",
|
|
26
|
+
"./reactions": "./src/reactions.ts",
|
|
27
|
+
"./guardian-requests": "./src/guardian-requests.ts",
|
|
28
|
+
"./platform-credential": "./src/platform-credential.ts"
|
|
26
29
|
},
|
|
27
30
|
"scripts": {
|
|
28
31
|
"typecheck": "bunx tsc --noEmit",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical vocabulary for a guardian request's lifecycle status, shared by
|
|
3
|
+
* the gateway that owns the request rows, the daemon that decides and
|
|
4
|
+
* projects them, and the web that renders the projection.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
export const GUARDIAN_REQUEST_STATUS_VALUES = [
|
|
9
|
+
"pending",
|
|
10
|
+
"approved",
|
|
11
|
+
"denied",
|
|
12
|
+
"expired",
|
|
13
|
+
"cancelled",
|
|
14
|
+
] as const;
|
|
15
|
+
export const GuardianRequestStatusSchema = z.enum(
|
|
16
|
+
GUARDIAN_REQUEST_STATUS_VALUES,
|
|
17
|
+
);
|
|
18
|
+
export type GuardianRequestStatus = z.infer<typeof GuardianRequestStatusSchema>;
|
package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export * from "./rpc.js";
|
|
|
28
28
|
export * from "./trust-rules.js";
|
|
29
29
|
export * from "./ingress.js";
|
|
30
30
|
export * from "./no-response.js";
|
|
31
|
+
export * from "./platform-credential.js";
|
|
31
32
|
export * from "./remote-web-pairing.js";
|
|
32
33
|
export * from "./twilio-ingress.js";
|
|
33
34
|
export * from "./url-normalization.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire contract for asking an assistant whether its stored platform-managed
|
|
3
|
+
* credential still authenticates.
|
|
4
|
+
*
|
|
5
|
+
* The daemon route is the authoritative serving side:
|
|
6
|
+
* - `POST /v1/platform/verify-credential`
|
|
7
|
+
* (`assistant/src/runtime/routes/platform-routes.ts`)
|
|
8
|
+
*
|
|
9
|
+
* The daemon's platform client produces the verdict, the route reports it,
|
|
10
|
+
* and the `vellum` CLI reads it before deciding whether a stored key can be
|
|
11
|
+
* re-injected (`cli/src/lib/assistant-api-key-resolution.ts`), so all three
|
|
12
|
+
* share one definition and cannot silently drift. The web app reads the same
|
|
13
|
+
* shape through its generated daemon client.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* What the platform said about the stored credential, right now.
|
|
19
|
+
*
|
|
20
|
+
* - `valid`: the platform accepted it.
|
|
21
|
+
* - `rejected`: the platform refused it (unauthorized or forbidden); the
|
|
22
|
+
* credential needs replacing.
|
|
23
|
+
* - `unknown`: the check itself could not run (no credential stored, the
|
|
24
|
+
* platform unreachable, a server error). Not evidence either way.
|
|
25
|
+
*/
|
|
26
|
+
export const PlatformCredentialVerificationStatusSchema = z.enum([
|
|
27
|
+
"valid",
|
|
28
|
+
"rejected",
|
|
29
|
+
"unknown",
|
|
30
|
+
]);
|
|
31
|
+
export type PlatformCredentialVerificationStatus = z.infer<
|
|
32
|
+
typeof PlatformCredentialVerificationStatusSchema
|
|
33
|
+
>;
|
|
34
|
+
|
|
35
|
+
/** `POST /v1/platform/verify-credential` response body. */
|
|
36
|
+
export const PlatformVerifyCredentialResponseSchema = z.object({
|
|
37
|
+
status: PlatformCredentialVerificationStatusSchema,
|
|
38
|
+
});
|
|
39
|
+
export type PlatformVerifyCredentialResponse = z.infer<
|
|
40
|
+
typeof PlatformVerifyCredentialResponseSchema
|
|
41
|
+
>;
|
package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/reactions.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical vocabulary for a reaction's emoji, shared by every service that
|
|
3
|
+
* handles one: the gateway that normalizes it, the daemon that stores and
|
|
4
|
+
* projects it, and the web that renders it.
|
|
5
|
+
*
|
|
6
|
+
* The kind is said by the channel rather than inferred from how the emoji
|
|
7
|
+
* is spelled. Modelled on Zulip's `reaction_type`, the one surveyed system
|
|
8
|
+
* that separates the namespace from the name.
|
|
9
|
+
*
|
|
10
|
+
* `shortcode` is a name in a channel's own namespace whose kind the channel
|
|
11
|
+
* does not disclose: Slack sends `+1` for the standard emoji and `blob_wave`
|
|
12
|
+
* for a workspace upload with nothing to tell them apart, and only the
|
|
13
|
+
* workspace token can resolve the second. It is a distinct kind from
|
|
14
|
+
* `unicode`, not a stand-in for an unknown one.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
export const REACTION_EMOJI_KINDS = ["unicode", "shortcode", "custom"] as const;
|
|
19
|
+
export type ReactionEmojiKind = (typeof REACTION_EMOJI_KINDS)[number];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The typed emoji fields every schema that carries a reaction spreads in,
|
|
23
|
+
* so the wire contract, the stored envelopes, and the response projection
|
|
24
|
+
* describe one shape. Optional throughout: a persisted row or a replayed
|
|
25
|
+
* payload may carry only the spelling.
|
|
26
|
+
*/
|
|
27
|
+
export const ReactionEmojiFieldsSchema = z.object({
|
|
28
|
+
/** Which namespace the emoji was drawn from. */
|
|
29
|
+
emojiKind: z.enum(REACTION_EMOJI_KINDS).optional(),
|
|
30
|
+
/**
|
|
31
|
+
* The emoji's name in that namespace: the character itself for `unicode`,
|
|
32
|
+
* the bare name for `shortcode` and `custom`. Never a mention form.
|
|
33
|
+
*/
|
|
34
|
+
emojiName: z.string().optional(),
|
|
35
|
+
/** The channel's id for a `custom` emoji, absent for every other kind. */
|
|
36
|
+
emojiId: z.string().optional(),
|
|
37
|
+
/** Whether a `custom` emoji animates. Absent for every other kind. */
|
|
38
|
+
emojiAnimated: z.boolean().optional(),
|
|
39
|
+
});
|
|
40
|
+
export type ReactionEmojiFields = z.infer<typeof ReactionEmojiFieldsSchema>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The typed emoji fields a source actually carries, with undefined ones
|
|
44
|
+
* omitted: an absent key and a present-but-undefined one serialize alike, but the
|
|
45
|
+
* stored envelope and the response should carry only what was declared. Every writer of a reaction shape (the wire
|
|
46
|
+
* payload, both stored envelopes, the response projection) copies the
|
|
47
|
+
* fields through this rather than restating the four-way pick.
|
|
48
|
+
*/
|
|
49
|
+
export function pickReactionEmojiFields(
|
|
50
|
+
source: ReactionEmojiFields,
|
|
51
|
+
): ReactionEmojiFields {
|
|
52
|
+
return {
|
|
53
|
+
...(source.emojiKind !== undefined ? { emojiKind: source.emojiKind } : {}),
|
|
54
|
+
...(source.emojiName !== undefined ? { emojiName: source.emojiName } : {}),
|
|
55
|
+
...(source.emojiId !== undefined ? { emojiId: source.emojiId } : {}),
|
|
56
|
+
...(source.emojiAnimated !== undefined
|
|
57
|
+
? { emojiAnimated: source.emojiAnimated }
|
|
58
|
+
: {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json
CHANGED
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
"./redacted-credential": "./src/redacted-credential.ts",
|
|
23
23
|
"./secret-detection": "./src/secret-detection.ts",
|
|
24
24
|
"./stripe-currency": "./src/stripe-currency.ts",
|
|
25
|
-
"./error": "./src/error.ts"
|
|
25
|
+
"./error": "./src/error.ts",
|
|
26
|
+
"./reactions": "./src/reactions.ts",
|
|
27
|
+
"./guardian-requests": "./src/guardian-requests.ts",
|
|
28
|
+
"./platform-credential": "./src/platform-credential.ts"
|
|
26
29
|
},
|
|
27
30
|
"scripts": {
|
|
28
31
|
"typecheck": "bunx tsc --noEmit",
|