@vellumai/assistant 0.11.7 → 0.11.8-staging.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +10 -10
- package/Dockerfile +1 -0
- package/docs/architecture/security.md +1 -1
- package/docs/error-handling.md +1 -2
- package/docs/guardian-request-flow.md +51 -5
- package/docs/trusted-contact-access.md +13 -12
- package/knip.json +1 -0
- package/node_modules/@vellumai/avatar-catalog/package.json +19 -0
- package/{src/avatar/character-components.ts → node_modules/@vellumai/avatar-catalog/src/catalog.ts} +12 -44
- package/node_modules/@vellumai/avatar-catalog/src/colors.ts +19 -0
- package/node_modules/@vellumai/avatar-catalog/src/index.ts +25 -0
- package/node_modules/@vellumai/avatar-catalog/src/types.ts +47 -0
- package/node_modules/@vellumai/avatar-catalog/tsconfig.json +20 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +43 -12
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/test-path-guard.test.ts +89 -0
- package/node_modules/@vellumai/environments/src/test-path-guard.ts +113 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-request-contract.test.ts +8 -18
- package/node_modules/@vellumai/gateway-client/src/__tests__/inbound-event-kind.test.ts +46 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +46 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-request-contract.ts +62 -21
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-event-kind.ts +53 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +11 -5
- package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +1 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +7 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/no-response.ts +84 -0
- package/openapi.yaml +481 -9
- package/package.json +3 -1
- package/scripts/sync-llm-catalog.ts +3 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +16 -0
- package/src/__tests__/agent-loop-compacted-history-transform.test.ts +304 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +3 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +3 -0
- package/src/__tests__/anthropic-provider.test.ts +56 -0
- package/src/__tests__/approval-cascade.test.ts +0 -13
- package/src/__tests__/attachments-store.test.ts +117 -6
- package/src/__tests__/attachments.test.ts +122 -0
- package/src/__tests__/bundled-asset.test.ts +22 -1
- package/src/__tests__/channel-approval.test.ts +14 -7
- package/src/__tests__/channel-approvals.test.ts +3 -3
- package/src/__tests__/channel-delivery-store.test.ts +58 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +1 -0
- package/src/__tests__/channel-invite-transport.test.ts +1 -0
- package/src/__tests__/channel-readiness-slack-remote.test.ts +3 -3
- package/src/__tests__/compaction-events.test.ts +0 -12
- package/src/__tests__/compactor-call-site-logging.test.ts +7 -1
- package/src/__tests__/compactor-retained-image-validation.test.ts +15 -0
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/contacts-write.test.ts +141 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -13
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -12
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -13
- package/src/__tests__/conversation-agent-loop.test.ts +95 -13
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -13
- package/src/__tests__/conversation-load-history-repair.test.ts +418 -0
- package/src/__tests__/conversation-media-retry.test.ts +33 -2
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -12
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -13
- package/src/__tests__/conversation-queue.test.ts +4 -13
- package/src/__tests__/conversation-retry-route.test.ts +5 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-sight-frames.test.ts +615 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -13
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -13
- package/src/__tests__/conversation-speed-override.test.ts +0 -12
- package/src/__tests__/conversation-store-ephemeral.test.ts +0 -12
- package/src/__tests__/conversation-summarize-route.test.ts +5 -0
- package/src/__tests__/conversation-summarize-up-to.test.ts +102 -0
- package/src/__tests__/conversation-wait-for-idle.test.ts +0 -12
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -17
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -17
- package/src/__tests__/credential-health-service.test.ts +45 -0
- package/src/__tests__/credential-record-retire.test.ts +256 -0
- package/src/__tests__/credential-record-write-through.test.ts +8 -4
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +0 -4
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +0 -4
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +0 -8
- package/src/__tests__/delete-propagation.test.ts +121 -3
- package/src/__tests__/evict-conversations-for-reload.test.ts +46 -6
- package/src/__tests__/file-ops-service.test.ts +8 -3
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/guardian-action-service-scope.test.ts +118 -0
- package/src/__tests__/guardian-card-withdrawal.test.ts +299 -5
- package/src/__tests__/guardian-expiry-notifier.test.ts +117 -13
- package/src/__tests__/guardian-gateway-sim.test.ts +119 -0
- package/src/__tests__/guardian-gateway-sim.ts +56 -20
- package/src/__tests__/guardian-reply-router-answer-mode.test.ts +6 -0
- package/src/__tests__/guardian-request-status-sync.test.ts +162 -0
- package/src/__tests__/guardian-wait-controller.test.ts +6 -0
- package/src/__tests__/headless-browser-mode.test.ts +27 -0
- package/src/__tests__/helpers/mock-conversation.ts +2 -0
- package/src/__tests__/list-messages-system-card.test.ts +70 -0
- package/src/__tests__/llm-catalog-parity.test.ts +2 -0
- package/src/__tests__/managed-profile-guard.test.ts +2 -2
- package/src/__tests__/media-resolve-image-validation.test.ts +106 -0
- package/src/__tests__/media-stream-server-integration.test.ts +6 -0
- package/src/__tests__/non-member-access-request.test.ts +12 -0
- package/src/__tests__/notification-discord-adapter.test.ts +76 -4
- package/src/__tests__/oauth-commands-routes.test.ts +21 -0
- package/src/__tests__/openai-provider.test.ts +63 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +114 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +63 -1
- package/src/__tests__/persist-unsendable-image.test.ts +19 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +2 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +1 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -1
- package/src/__tests__/process-message-display-content.test.ts +1 -1
- package/src/__tests__/processing-flag-persist-failure.test.ts +0 -5
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +0 -17
- package/src/__tests__/qdrant-manager.test.ts +6 -0
- package/src/__tests__/reaction-persistence.test.ts +58 -62
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +67 -0
- package/src/__tests__/server-history-render.test.ts +22 -0
- package/src/__tests__/sight-frame-retention.test.ts +362 -0
- package/src/__tests__/slack-reaction-approvals.test.ts +44 -24
- package/src/__tests__/slack-reaction-guardian-approval.test.ts +29 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-notify-stale-rebuild.test.ts +85 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +4 -4
- package/src/__tests__/terminal-tools.test.ts +9 -9
- package/src/__tests__/tool-result-spool.test.ts +5 -8
- package/src/__tests__/usage-attribution.test.ts +26 -0
- package/src/acp/__tests__/acp-auth-marker-store.test.ts +191 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +262 -0
- package/src/acp/__tests__/acp-connect-card-state.test.ts +17 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +10 -3
- package/src/acp/acp-auth-marker-store.ts +196 -0
- package/src/acp/acp-claude-oauth.ts +154 -4
- package/src/acp/acp-connect-card-state.ts +49 -8
- package/src/acp/agent-process.ts +1 -0
- package/src/acp/auto-install.ts +1 -0
- package/src/acp/client-handler.ts +1 -0
- package/src/acp/prepare-agent-env.ts +79 -1
- package/src/acp/resolve-agent.ts +12 -0
- package/src/acp/session-manager.test.ts +183 -0
- package/src/acp/session-manager.ts +110 -6
- package/src/acp/types.ts +17 -0
- package/src/agent/attachments.ts +7 -2
- package/src/agent/loop.ts +59 -1
- package/src/api/events/contact-form-closed.ts +32 -0
- package/src/api/events/contact-record-request.ts +63 -0
- package/src/api/events/contact-request.ts +10 -3
- package/src/api/index.ts +20 -0
- package/src/api/responses/conversation-message.ts +20 -0
- package/src/api/responses/home.ts +101 -0
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-card-withdrawal.ts +127 -30
- package/src/approvals/guardian-decision-primitive.ts +9 -0
- package/src/approvals/guardian-expiry-notifier.ts +22 -16
- package/src/approvals/guardian-request-status-sync.ts +98 -0
- package/src/avatar/svg-compositor.ts +1 -1
- package/src/avatar/traits-png-sync.ts +1 -1
- package/src/bundler/app-compiler.ts +1 -0
- package/src/bundler/compiler-tools.ts +1 -0
- package/src/bundler/package-resolver.ts +1 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +183 -3
- package/src/calls/guardian-action-sweep.ts +6 -1
- package/src/calls/voice-session-bridge.ts +46 -1
- package/src/channels/__tests__/gateway-guardian-requests.test.ts +13 -18
- package/src/channels/gateway-guardian-requests.ts +37 -18
- package/src/cli/__tests__/contacts-record-prompt-cli.test.ts +329 -0
- package/src/cli/commands/__tests__/browser.test.ts +65 -1
- package/src/cli/commands/browser.ts +30 -10
- package/src/cli/commands/contacts.help.ts +173 -11
- package/src/cli/commands/contacts.ts +343 -4
- package/src/cli/commands/db/refresh.ts +1 -0
- package/src/cli/commands/oauth/index.help.ts +6 -3
- package/src/cli/commands/oauth/request.test.ts +37 -0
- package/src/cli/commands/oauth/request.ts +7 -6
- package/src/cli/lib/bundled-marketplace.json +1 -1
- package/src/cli/lib/install-from-github.ts +20 -19
- package/src/cli/lib/install-plugin-dependencies.ts +3 -5
- package/src/cli/lib/merge-plugin-tree.ts +1 -0
- package/src/cli/lib/publish-plugin.ts +4 -1
- package/src/config/__tests__/balanced-model-experiment.test.ts +63 -31
- package/src/config/__tests__/default-profile-catalog.test.ts +2 -2
- package/src/config/balanced-model-experiment.ts +15 -9
- package/src/config/bundled-skills/contacts/SKILL.md +61 -18
- package/src/config/default-profile-catalog.ts +12 -11
- package/src/config/feature-flag-registry.json +22 -13
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/live-voice.test.ts +13 -0
- package/src/config/schemas/live-voice.ts +7 -0
- package/src/config/schemas/sight.ts +45 -0
- package/src/config/skills.ts +2 -1
- package/src/contacts/__tests__/mirror-reconciler.test.ts +352 -0
- package/src/contacts/contact-store.ts +47 -4
- package/src/contacts/contacts-write.ts +5 -2
- package/src/contacts/mirror-reconciler.ts +210 -0
- package/src/contacts/mirror-tombstones.ts +42 -0
- package/src/contacts/notify-contacts-changed.ts +11 -2
- package/src/context/compactor.ts +47 -19
- package/src/context/tool-result-spool.ts +21 -14
- package/src/conversations/message-consolidation.ts +2 -2
- package/src/credential-health/credential-health-service.ts +37 -6
- package/src/daemon/conversation-agent-loop-handlers.ts +19 -2
- package/src/daemon/conversation-agent-loop.ts +100 -1
- package/src/daemon/conversation-media-retry.ts +11 -6
- package/src/daemon/conversation-messaging.ts +293 -30
- package/src/daemon/conversation-runtime-assembly.ts +67 -0
- package/src/daemon/conversation-sight-frames.ts +187 -0
- package/src/daemon/conversation-store.ts +18 -28
- package/src/daemon/conversation-surfaces.ts +12 -2
- package/src/daemon/conversation-tool-setup.ts +39 -10
- package/src/daemon/conversation.ts +159 -16
- package/src/daemon/daemon-control.ts +6 -17
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +10 -11
- package/src/daemon/install-assistant-command.ts +1 -0
- package/src/daemon/lifecycle.ts +10 -13
- package/src/daemon/message-types/sync.ts +4 -0
- package/src/daemon/process-message.ts +2 -17
- package/src/daemon/reaction-history-render.test.ts +162 -0
- package/src/daemon/reaction-history-render.ts +115 -0
- package/src/daemon/reaction-record.test.ts +162 -0
- package/src/daemon/reaction-record.ts +83 -0
- package/src/daemon/shutdown-handlers.ts +0 -2
- package/src/daemon/summarize-boundary.ts +12 -0
- package/src/daemon/video-thumbnail.ts +1 -1
- package/src/home/feed-types.ts +4 -0
- package/src/home/feed-writer.ts +25 -0
- package/src/live-voice/__tests__/live-voice-attach-frame.test.ts +679 -0
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +8 -5
- package/src/live-voice/__tests__/live-voice-photo.test.ts +637 -1
- package/src/live-voice/__tests__/live-voice-sight-frame-inline.test.ts +787 -0
- package/src/live-voice/__tests__/live-voice-sight-frame.test.ts +347 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +109 -0
- package/src/live-voice/live-voice-photo.ts +420 -65
- package/src/live-voice/live-voice-session.ts +311 -27
- package/src/live-voice/protocol.ts +147 -11
- package/src/messaging/provider-message-metadata.ts +7 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +113 -1
- package/src/messaging/providers/channel-transport.ts +37 -0
- package/src/messaging/providers/discord/api.ts +4 -4
- package/src/messaging/providers/discord/send.test.ts +177 -2
- package/src/messaging/providers/discord/send.ts +197 -10
- package/src/messaging/providers/discord/transport.ts +10 -0
- package/src/messaging/providers/index.ts +42 -0
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +129 -3
- package/src/messaging/providers/slack/__tests__/auth.test.ts +35 -0
- package/src/messaging/providers/slack/__tests__/message-content.test.ts +152 -0
- package/src/messaging/providers/slack/adapter.ts +112 -27
- package/src/messaging/providers/slack/api.test.ts +10 -0
- package/src/messaging/providers/slack/auth.ts +32 -6
- package/src/messaging/providers/slack/client.ts +0 -15
- package/src/messaging/providers/slack/message-content.ts +137 -0
- package/src/messaging/providers/slack/send.test.ts +43 -2
- package/src/messaging/providers/slack/send.ts +18 -5
- package/src/messaging/providers/slack/transport.ts +10 -0
- package/src/messaging/providers/slack/types.ts +21 -2
- package/src/messaging/providers/telegram-bot/adapter.ts +5 -1
- package/src/messaging/providers/telegram-bot/send.test.ts +49 -2
- package/src/messaging/providers/telegram-bot/send.ts +41 -1
- package/src/messaging/providers/telegram-bot/transport.ts +10 -0
- package/src/messaging/providers/telegram-bot/withdraw.ts +13 -5
- package/src/messaging/reaction-envelopes.ts +79 -0
- package/src/monitoring/control.ts +2 -2
- package/src/monitoring/page-cache.ts +5 -1
- package/src/notifications/AGENTS.md +1 -1
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +360 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +8 -0
- package/src/notifications/adapters/discord.ts +62 -9
- package/src/notifications/conversation-pairing.ts +31 -4
- package/src/notifications/emit-signal.ts +8 -5
- package/src/notifications/guardian-delivery-recorder.ts +47 -6
- package/src/notifications/guardian-feed-projection.ts +492 -0
- package/src/notifications/home-feed-side-effect.ts +59 -11
- package/src/oauth/byo-connection.test.ts +22 -0
- package/src/oauth/byo-connection.ts +11 -5
- package/src/oauth/connection.ts +3 -2
- package/src/oauth/platform-connection.test.ts +35 -0
- package/src/oauth/platform-connection.ts +18 -13
- package/src/oauth/scope-utils.ts +48 -0
- package/src/oauth/seed-providers.ts +12 -0
- package/src/outbound-proxy/certs.ts +5 -4
- package/src/permissions/question-guardian-request.test.ts +7 -1
- package/src/persistence/attachments-store.ts +134 -31
- package/src/persistence/conversation-crud.ts +137 -4
- package/src/persistence/conversation-types.ts +109 -0
- package/src/persistence/db-connection.ts +3 -21
- package/src/persistence/delivery-crud.ts +42 -12
- package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +15 -2
- package/src/persistence/embeddings/embedding-local.ts +7 -112
- package/src/persistence/embeddings/qdrant-manager.ts +4 -1
- package/src/persistence/llm-request-log-store.ts +1 -1
- package/src/persistence/migrations/370-acp-session-history-auth-error-code.ts +36 -0
- package/src/persistence/migrations/371-acp-session-history-auth-error-credential.ts +40 -0
- package/src/persistence/migrations/372-create-acp-refused-credentials.ts +34 -0
- package/src/persistence/migrations/373-acp-auth-marker-index.ts +27 -0
- package/src/persistence/migrations/__tests__/370-acp-session-history-auth-error-code.test.ts +65 -0
- package/src/persistence/migrations/__tests__/371-acp-session-history-auth-error-credential.test.ts +68 -0
- package/src/persistence/migrations/__tests__/372-create-acp-refused-credentials.test.ts +64 -0
- package/src/persistence/migrations/__tests__/373-acp-auth-marker-index.test.ts +82 -0
- package/src/persistence/schema/acp.ts +25 -0
- package/src/persistence/steps.ts +8 -0
- package/src/platform/platform-patch-queue.test.ts +43 -41
- package/src/plugin-api/index.ts +12 -0
- package/src/plugins/defaults/compaction/window-manager.ts +2 -0
- package/src/plugins/defaults/image-recovery/recover.ts +12 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +166 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +3 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +3 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +53 -9
- package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +4 -7
- package/src/plugins/defaults/memory/src/memory-worker-routes.ts +1 -1
- package/src/plugins/defaults/memory/v1/job-handlers/index-maintenance.ts +15 -1
- package/src/plugins/defaults/memory/worker-control.ts +1 -2
- package/src/providers/content-block-schema.ts +1 -0
- package/src/providers/fireworks/client.ts +9 -1
- package/src/providers/gemini/client.ts +22 -15
- package/src/providers/media-resolve.ts +24 -10
- package/src/providers/model-catalog.ts +91 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -5
- package/src/providers/retry.ts +16 -11
- package/src/providers/types.ts +56 -0
- package/src/routes/__tests__/control.test.ts +4 -3
- package/src/routes/control.ts +1 -1
- package/src/routes/route-host-client.ts +1 -1
- package/src/runtime/AGENTS.md +30 -12
- package/src/runtime/__tests__/agent-wake.test.ts +71 -6
- package/src/runtime/__tests__/guardian-form-registry.test.ts +242 -0
- package/src/runtime/access-request-helper.ts +15 -7
- package/src/runtime/agent-wake.ts +16 -4
- package/src/runtime/channel-approval-types.ts +8 -5
- package/src/runtime/channel-invite-transport.ts +2 -0
- package/src/runtime/channel-invite-transports/discord.ts +27 -0
- package/src/runtime/channel-readiness-service.ts +4 -4
- package/src/runtime/channel-reply-delivery.ts +72 -28
- package/src/runtime/guardian-action-service.ts +9 -5
- package/src/runtime/guardian-form-registry.ts +256 -0
- package/src/runtime/guardian-reply-router.ts +33 -14
- package/src/runtime/invite-instruction-generator.ts +14 -2
- package/src/runtime/migrations/vbundle-metadata-merge.ts +4 -7
- package/src/runtime/no-response.ts +13 -74
- package/src/runtime/routes/__tests__/acp-routes.test.ts +527 -0
- package/src/runtime/routes/__tests__/app-management-shared-apps.test.ts +74 -0
- package/src/runtime/routes/__tests__/contact-form-kinds.test.ts +22 -0
- package/src/runtime/routes/__tests__/contact-record-prompt-route.test.ts +303 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +6 -0
- package/src/runtime/routes/__tests__/route-host-worker-routes.test.ts +3 -4
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +5 -6
- package/src/runtime/routes/acp-routes-list.test.ts +58 -0
- package/src/runtime/routes/acp-routes.ts +253 -47
- package/src/runtime/routes/app-management-routes.ts +80 -47
- package/src/runtime/routes/approval-routes.ts +9 -0
- package/src/runtime/routes/archive-utils.ts +6 -1
- package/src/runtime/routes/avatar-routes.ts +1 -1
- package/src/runtime/routes/channel-route-shared.ts +18 -20
- package/src/runtime/routes/contact-prompt-routes.ts +271 -105
- package/src/runtime/routes/conversation-routes.ts +41 -32
- package/src/runtime/routes/credential-prompt-routes.ts +10 -1
- package/src/runtime/routes/debug-bash-routes.ts +1 -0
- package/src/runtime/routes/guardian-expiry-sweep.ts +89 -31
- package/src/runtime/routes/guardian-form-routes.ts +115 -0
- package/src/runtime/routes/inbound-message-handler.ts +82 -29
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +6 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +6 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +6 -1
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +50 -17
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +97 -94
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/channel.ts +39 -18
- package/src/runtime/routes/migration-routes.ts +1 -3
- package/src/runtime/routes/monitoring-routes.ts +0 -3
- package/src/runtime/routes/oauth-commands-routes.ts +18 -5
- package/src/runtime/routes/route-host-worker-routes.ts +1 -1
- package/src/runtime/routes/schedule-worker-routes.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +4 -0
- package/src/schedule/run-script.ts +1 -0
- package/src/schedule/scheduler.ts +1 -1
- package/src/schedule/worker-control.ts +2 -2
- package/src/security/oauth2.ts +2 -11
- package/src/security/secure-keys.ts +55 -8
- package/src/skills/catalog-install.ts +7 -4
- package/src/skills/clawhub.ts +1 -0
- package/src/skills/inline-command-runner.ts +1 -0
- package/src/stt/__tests__/room-noise-floor.test.ts +108 -0
- package/src/stt/room-noise-floor.ts +140 -0
- package/src/subagent/notify.ts +49 -3
- package/src/tools/__tests__/tool-input-schemas.test.ts +2 -0
- package/src/tools/acp/spawn.ts +2 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +9 -0
- package/src/tools/browser/browser-execution.ts +10 -43
- package/src/tools/browser/browser-manager.ts +1 -0
- package/src/tools/browser/browser-status-constants.ts +6 -5
- package/src/tools/browser/runtime-check.ts +2 -0
- package/src/tools/channel/react-to-message.test.ts +193 -0
- package/src/tools/channel/react-to-message.ts +159 -0
- package/src/tools/credentials/metadata-store.ts +115 -19
- package/src/tools/credentials/store.ts +0 -2
- package/src/tools/filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +24 -0
- package/src/tools/host-terminal/host-shell.ts +11 -38
- package/src/tools/shared/filesystem/file-ops-service.ts +10 -7
- package/src/tools/shared/shell-output.test.ts +104 -0
- package/src/tools/shared/shell-output.ts +112 -39
- package/src/tools/skills/sandbox-runner.ts +1 -0
- package/src/tools/system/request-permission.test.ts +34 -0
- package/src/tools/system/request-permission.ts +29 -5
- package/src/tools/terminal/shell.test.ts +63 -0
- package/src/tools/terminal/shell.ts +12 -44
- package/src/tools/tool-input-schemas.ts +2 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/usage/attribution.ts +12 -4
- package/src/util/__tests__/worker-ownership.test.ts +77 -0
- package/src/util/__tests__/worker-process-command.test.ts +246 -1
- package/src/util/bun-runtime.ts +44 -13
- package/src/util/bundled-asset.ts +9 -1
- package/src/util/disk-usage.ts +1 -0
- package/src/util/file-use.ts +4 -1
- package/src/util/guardian-form-timeouts.ts +50 -0
- package/src/util/image-conversion.ts +1 -0
- package/src/util/oauth-request-body.test.ts +59 -0
- package/src/util/oauth-request-body.ts +56 -5
- package/src/util/platform.ts +69 -73
- package/src/util/process-table.ts +16 -5
- package/src/util/read-stdin.ts +5 -0
- package/src/util/spawn.ts +1 -0
- package/src/util/sqlite3-runtime.ts +25 -5
- package/src/util/worker-ownership.ts +193 -0
- package/src/util/worker-process.ts +375 -37
- package/src/workspace/git-service.ts +4 -12
- package/src/workspace/migrations/010-app-dir-rename.ts +1 -0
|
@@ -11,10 +11,15 @@ import { eq, inArray } from "drizzle-orm";
|
|
|
11
11
|
import type { AcpSessionUpdateEvent } from "../api/events/acp-session-update.js";
|
|
12
12
|
import type { AssistantEvent } from "../api/index.js";
|
|
13
13
|
import { findConversation } from "../daemon/conversation-registry.js";
|
|
14
|
+
import { SYNC_TAGS } from "../daemon/message-types/sync.js";
|
|
14
15
|
import { getDb } from "../persistence/db-connection.js";
|
|
15
16
|
import { acpSessionHistory } from "../persistence/schema/index.js";
|
|
16
17
|
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
17
18
|
import { getLogger } from "../util/logger.js";
|
|
19
|
+
import {
|
|
20
|
+
acpAuthMarkerStillCurrent,
|
|
21
|
+
noteClaudeTokenRefused,
|
|
22
|
+
} from "./acp-auth-marker-store.js";
|
|
18
23
|
import { markAcpConnectCardRaised } from "./acp-connect-card-state.js";
|
|
19
24
|
import { AcpAgentProcess } from "./agent-process.js";
|
|
20
25
|
import {
|
|
@@ -113,6 +118,12 @@ interface SessionEntry {
|
|
|
113
118
|
* gate resume hints to the only adapter (claude-agent-acp) whose CLI
|
|
114
119
|
* accepts `--resume`. */
|
|
115
120
|
command: string;
|
|
121
|
+
/** Identity of the Claude token this session ran with, whichever source it
|
|
122
|
+
* came from. Recorded on the history row alongside the failure code, so the
|
|
123
|
+
* marker can be compared against the credential a later spawn resolves
|
|
124
|
+
* rather than needing a sweep to retire it. Absent for agents that use no
|
|
125
|
+
* Claude credential. */
|
|
126
|
+
credentialDigest?: string;
|
|
116
127
|
}
|
|
117
128
|
|
|
118
129
|
/**
|
|
@@ -275,6 +286,14 @@ export class AcpSessionManager {
|
|
|
275
286
|
);
|
|
276
287
|
} catch (err) {
|
|
277
288
|
log.error({ acpSessionId, agentId, err }, "ACP spawn failed");
|
|
289
|
+
// Here rather than at the tool boundary, because the HTTP spawn route
|
|
290
|
+
// reaches this method directly and would otherwise never record it. A
|
|
291
|
+
// configured CLAUDE_CODE_OAUTH_TOKEN that Claude refused keeps winning
|
|
292
|
+
// over whatever Connect stores until this is written down, so every
|
|
293
|
+
// later spawn repeats the same failure.
|
|
294
|
+
if (isAcpAuthRequired(err)) {
|
|
295
|
+
noteClaudeTokenRefused(agentConfig.credentialDigest, Date.now());
|
|
296
|
+
}
|
|
278
297
|
// No prompt has fired yet, so no permissions can be pending.
|
|
279
298
|
this.teardownSession(acpSessionId, entry);
|
|
280
299
|
throw err;
|
|
@@ -374,6 +393,7 @@ export class AcpSessionManager {
|
|
|
374
393
|
parentToolUseId: opts.parentToolUseId,
|
|
375
394
|
task: opts.task,
|
|
376
395
|
command: basename(opts.agentConfig.command),
|
|
396
|
+
credentialDigest: opts.agentConfig.credentialDigest,
|
|
377
397
|
};
|
|
378
398
|
|
|
379
399
|
this.sessions.set(acpSessionId, entry);
|
|
@@ -972,6 +992,8 @@ export class AcpSessionManager {
|
|
|
972
992
|
const usageColumns = {
|
|
973
993
|
task: entry.state.task ?? null,
|
|
974
994
|
parentToolUseId: entry.state.parentToolUseId ?? null,
|
|
995
|
+
authErrorCode: entry.state.authErrorCode ?? null,
|
|
996
|
+
authErrorCredential: entry.state.authErrorCredential ?? null,
|
|
975
997
|
usedTokens: usage?.usedTokens ?? null,
|
|
976
998
|
contextSize: usage?.contextSize ?? null,
|
|
977
999
|
costAmount: usage?.costAmount ?? null,
|
|
@@ -1114,7 +1136,7 @@ export class AcpSessionManager {
|
|
|
1114
1136
|
}
|
|
1115
1137
|
}
|
|
1116
1138
|
})
|
|
1117
|
-
.catch((err: Error) => {
|
|
1139
|
+
.catch(async (err: Error) => {
|
|
1118
1140
|
const current = this.sessions.get(acpSessionId);
|
|
1119
1141
|
// Same guards: entry must exist, prompt must be current, and status
|
|
1120
1142
|
// must not have been set to "cancelled".
|
|
@@ -1158,7 +1180,59 @@ export class AcpSessionManager {
|
|
|
1158
1180
|
errorCode !== undefined && current.state.status !== "cancelled"
|
|
1159
1181
|
? current.parentToolUseId
|
|
1160
1182
|
: undefined;
|
|
1161
|
-
|
|
1183
|
+
// Claude refused this credential, so no later spawn should resolve
|
|
1184
|
+
// it again. Recorded before the card is raised and independently of
|
|
1185
|
+
// the marker, because the user may delete this run and clearing
|
|
1186
|
+
// history must not put the revoked value back in play.
|
|
1187
|
+
if (errorCode !== undefined) {
|
|
1188
|
+
noteClaudeTokenRefused(current.credentialDigest, Date.now());
|
|
1189
|
+
}
|
|
1190
|
+
// The live event reaches clients that are listening now, and the
|
|
1191
|
+
// card it raises deliberately skips connected-state self-healing, so
|
|
1192
|
+
// nothing retires it until the next snapshot. If the credential has
|
|
1193
|
+
// already been replaced, that snapshot is the only thing that would
|
|
1194
|
+
// have withheld it, and it may be a navigation away. Ask the same
|
|
1195
|
+
// question the read path asks, against the same source of truth.
|
|
1196
|
+
//
|
|
1197
|
+
// An optimisation, not the authority: a write landing right after
|
|
1198
|
+
// this read raises a card the next snapshot withholds. The marker is
|
|
1199
|
+
// written either way, so the read path still has the final say.
|
|
1200
|
+
const stillCurrent =
|
|
1201
|
+
errorCode !== undefined &&
|
|
1202
|
+
recoveryAnchor !== undefined &&
|
|
1203
|
+
acpAuthMarkerStillCurrent(
|
|
1204
|
+
current.credentialDigest,
|
|
1205
|
+
// Imported at call time so this module's load graph stays as it
|
|
1206
|
+
// was: several suites stub `prepare-agent-env` with a partial
|
|
1207
|
+
// factory, and a new static import from here would break their
|
|
1208
|
+
// module resolution rather than their assertions.
|
|
1209
|
+
await import("./prepare-agent-env.js")
|
|
1210
|
+
.then((m) =>
|
|
1211
|
+
m.resolvedClaudeCredentialDigest(current.state.agentId),
|
|
1212
|
+
)
|
|
1213
|
+
// Unknown reads as current, matching the comparison itself:
|
|
1214
|
+
// failing toward a card the next snapshot can withdraw beats
|
|
1215
|
+
// withholding the user's only route back to auth.
|
|
1216
|
+
.catch(() => current.credentialDigest),
|
|
1217
|
+
);
|
|
1218
|
+
// The credential read above suspends this handler, and a cancel
|
|
1219
|
+
// landing in that window runs to completion first: `cancel()`
|
|
1220
|
+
// persists the cancelled row, drops the event buffer and tears the
|
|
1221
|
+
// session down. Carrying on would emit a card for a run the user
|
|
1222
|
+
// stopped and, worse, persist a second time over a buffer that is
|
|
1223
|
+
// already gone, replacing the stored event log with an empty one.
|
|
1224
|
+
// The map is the evidence: `teardownSession` removes the entry, so an
|
|
1225
|
+
// entry that is no longer the registered one means someone else has
|
|
1226
|
+
// already finished this session and there is nothing left here to
|
|
1227
|
+
// do.
|
|
1228
|
+
if (this.sessions.get(acpSessionId) !== current) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
if (
|
|
1232
|
+
errorCode !== undefined &&
|
|
1233
|
+
recoveryAnchor !== undefined &&
|
|
1234
|
+
stillCurrent
|
|
1235
|
+
) {
|
|
1162
1236
|
current.sendToVellum({
|
|
1163
1237
|
type: "acp_auth_required",
|
|
1164
1238
|
acpSessionId,
|
|
@@ -1169,7 +1243,36 @@ export class AcpSessionManager {
|
|
|
1169
1243
|
// Same registry as the missing-token spawn path, so the
|
|
1170
1244
|
// credential-prompt route redirects a redundant secure prompt at
|
|
1171
1245
|
// the card instead of stacking a second one.
|
|
1172
|
-
markAcpConnectCardRaised(
|
|
1246
|
+
markAcpConnectCardRaised(
|
|
1247
|
+
current.parentConversationId,
|
|
1248
|
+
current.state.agentId,
|
|
1249
|
+
);
|
|
1250
|
+
// The event above only reaches clients listening right now. The
|
|
1251
|
+
// code goes on the state so `persistTerminal` (called just below)
|
|
1252
|
+
// carries it to the history row, which is what a client that
|
|
1253
|
+
// reopens the conversation re-raises the card from.
|
|
1254
|
+
current.state.authErrorCode = errorCode;
|
|
1255
|
+
// Tell clients to re-read the snapshot, which is the only thing
|
|
1256
|
+
// that can withdraw this card. The check above reads the vault
|
|
1257
|
+
// and then this branch runs, so a replacement completing in
|
|
1258
|
+
// between publishes its own invalidation before the event goes
|
|
1259
|
+
// out, and a client that acted on it would never look again. This
|
|
1260
|
+
// nudge lands after, so whatever the snapshot says wins.
|
|
1261
|
+
void import("../runtime/sync/sync-publisher.js")
|
|
1262
|
+
.then(({ publishSyncInvalidation }) =>
|
|
1263
|
+
publishSyncInvalidation([SYNC_TAGS.acpAuthRecovery]),
|
|
1264
|
+
)
|
|
1265
|
+
.catch((err: unknown) => {
|
|
1266
|
+
log.warn(
|
|
1267
|
+
{ err },
|
|
1268
|
+
"failed to publish ACP auth recovery invalidation",
|
|
1269
|
+
);
|
|
1270
|
+
});
|
|
1271
|
+
// Named beside the code so the marker can answer for itself later.
|
|
1272
|
+
// Without it the row says auth broke but not on what, and a reader
|
|
1273
|
+
// has no way to tell a failure the user has since repaired from one
|
|
1274
|
+
// still waiting on them.
|
|
1275
|
+
current.state.authErrorCredential = current.credentialDigest;
|
|
1173
1276
|
}
|
|
1174
1277
|
|
|
1175
1278
|
// Persist the terminal row before teardown clears the buffer.
|
|
@@ -1187,9 +1290,10 @@ export class AcpSessionManager {
|
|
|
1187
1290
|
this.notifyParent(
|
|
1188
1291
|
current,
|
|
1189
1292
|
`[ACP agent "${current.state.agentId}" failed]\n\n${failureMessage}` +
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1293
|
+
// Same predicate as the recovery surface above. Telling the
|
|
1294
|
+
// model a Connect card is waiting when none was raised sends
|
|
1295
|
+
// it to an affordance that does not exist.
|
|
1296
|
+
(stillCurrent ? `\n\n${ACP_AUTH_RECOVERY_GUIDANCE}` : ""),
|
|
1193
1297
|
);
|
|
1194
1298
|
}
|
|
1195
1299
|
}
|
package/src/acp/types.ts
CHANGED
|
@@ -12,6 +12,13 @@ export interface AcpAgentConfig {
|
|
|
12
12
|
args: string[];
|
|
13
13
|
description?: string;
|
|
14
14
|
env?: Record<string, string>;
|
|
15
|
+
/**
|
|
16
|
+
* Identity of the Claude token `prepareAgentEnv` resolved into `env`,
|
|
17
|
+
* whichever source it came from. Recorded on the history row when Claude
|
|
18
|
+
* refuses it, so the marker can later be compared against the credential a
|
|
19
|
+
* spawn would resolve now. Absent for agents that use no Claude credential.
|
|
20
|
+
*/
|
|
21
|
+
credentialDigest?: string;
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
/**
|
|
@@ -32,6 +39,16 @@ export interface AcpSessionState {
|
|
|
32
39
|
task?: string;
|
|
33
40
|
/** Tool-use id of the `acp_spawn` call that spawned this session, if any. */
|
|
34
41
|
parentToolUseId?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Credential failure that ended the run, when one did. Persisted on the
|
|
44
|
+
* history row so a client that reopens the conversation can re-raise the
|
|
45
|
+
* inline Connect card, and cleared there when a replacement token lands.
|
|
46
|
+
*/
|
|
47
|
+
authErrorCode?: string;
|
|
48
|
+
/** Digest of the Claude token that failure was refused on, carried to the
|
|
49
|
+
* history row so the marker can be compared against the credential a later
|
|
50
|
+
* spawn resolves. */
|
|
51
|
+
authErrorCredential?: string;
|
|
35
52
|
/** Latest context-window usage gauge, from the most recent `usage_update`. */
|
|
36
53
|
latestUsage?: AcpUsageSnapshot;
|
|
37
54
|
}
|
package/src/agent/attachments.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
attachmentIdFragment,
|
|
3
|
+
type ContentBlock,
|
|
4
|
+
type Message,
|
|
5
|
+
} from "../providers/types.js";
|
|
2
6
|
import { optimizeImageForTransport } from "./image-optimize.js";
|
|
3
7
|
|
|
4
8
|
export interface MessageAttachmentInput {
|
|
@@ -91,6 +95,7 @@ export function attachmentsToContentBlocks(
|
|
|
91
95
|
media_type: mediaType,
|
|
92
96
|
data,
|
|
93
97
|
},
|
|
98
|
+
...attachmentIdFragment(attachment.id),
|
|
94
99
|
} as ContentBlock;
|
|
95
100
|
}
|
|
96
101
|
|
|
@@ -103,7 +108,7 @@ export function attachmentsToContentBlocks(
|
|
|
103
108
|
filename: attachment.filename,
|
|
104
109
|
},
|
|
105
110
|
extracted_text: attachment.extractedText,
|
|
106
|
-
...(attachment.id
|
|
111
|
+
...attachmentIdFragment(attachment.id),
|
|
107
112
|
} as ContentBlock;
|
|
108
113
|
}),
|
|
109
114
|
);
|
package/src/agent/loop.ts
CHANGED
|
@@ -797,6 +797,22 @@ export interface AgentLoopConstructorOptions {
|
|
|
797
797
|
* result-time pass and the post-turn truncation covers the turn instead.
|
|
798
798
|
*/
|
|
799
799
|
resolveConversationDir?: () => string | null;
|
|
800
|
+
/**
|
|
801
|
+
* Trim a freshly compacted history before it becomes the loop's working set.
|
|
802
|
+
*
|
|
803
|
+
* Compaction rebuilds history from the stored rows, so it can reintroduce
|
|
804
|
+
* content the caller had already trimmed out of the array it handed to
|
|
805
|
+
* `run()` (camera-frame retention is the case this exists for: the compaction
|
|
806
|
+
* model may retain any number of older frames, and the rebuilt history is
|
|
807
|
+
* sent on the very next request). The caller closes over whatever
|
|
808
|
+
* conversation state its trimming needs, which is what keeps this module free
|
|
809
|
+
* of any dependency on the daemon's conversation layer.
|
|
810
|
+
*
|
|
811
|
+
* Must be total: the loop treats a throw as "no trim" rather than failing the
|
|
812
|
+
* turn. Callers that hold no conversation (workflow leaf runs) omit it and
|
|
813
|
+
* the compacted history is installed as built.
|
|
814
|
+
*/
|
|
815
|
+
transformCompactedHistory?: (messages: Message[]) => Message[];
|
|
800
816
|
}
|
|
801
817
|
|
|
802
818
|
export class AgentLoop {
|
|
@@ -818,6 +834,11 @@ export class AgentLoop {
|
|
|
818
834
|
/** See {@link AgentLoopConstructorOptions.resolveConversationDir}. */
|
|
819
835
|
private readonly resolveConversationDir: (() => string | null) | null;
|
|
820
836
|
|
|
837
|
+
/** See {@link AgentLoopConstructorOptions.transformCompactedHistory}. */
|
|
838
|
+
private readonly transformCompactedHistory:
|
|
839
|
+
| ((messages: Message[]) => Message[])
|
|
840
|
+
| null;
|
|
841
|
+
|
|
821
842
|
/**
|
|
822
843
|
* Loop-held compaction circuit breaker. The loop has a 1:1 lifetime with its
|
|
823
844
|
* conversation, so it is the source of truth for the cross-turn failure
|
|
@@ -837,6 +858,7 @@ export class AgentLoop {
|
|
|
837
858
|
resolveTools,
|
|
838
859
|
conversationId,
|
|
839
860
|
resolveConversationDir,
|
|
861
|
+
transformCompactedHistory,
|
|
840
862
|
} = options;
|
|
841
863
|
this.provider = provider;
|
|
842
864
|
this.systemPrompt = systemPrompt;
|
|
@@ -846,6 +868,7 @@ export class AgentLoop {
|
|
|
846
868
|
this.toolExecutor = toolExecutor ?? null;
|
|
847
869
|
this.conversationId = conversationId;
|
|
848
870
|
this.resolveConversationDir = resolveConversationDir ?? null;
|
|
871
|
+
this.transformCompactedHistory = transformCompactedHistory ?? null;
|
|
849
872
|
this.compactionCircuit = new CompactionCircuit(this.conversationId);
|
|
850
873
|
}
|
|
851
874
|
|
|
@@ -925,6 +948,34 @@ export class AgentLoop {
|
|
|
925
948
|
}
|
|
926
949
|
}
|
|
927
950
|
|
|
951
|
+
/**
|
|
952
|
+
* Run the caller's post-compaction trim over a freshly compacted history,
|
|
953
|
+
* falling back to the untrimmed array when no transform is configured or the
|
|
954
|
+
* transform throws.
|
|
955
|
+
*
|
|
956
|
+
* The trim narrows what the next request carries, so failing it is strictly
|
|
957
|
+
* worse than skipping it: the turn still has a valid history either way, and
|
|
958
|
+
* the caller's own passes cover the next turn. Kept best-effort for the same
|
|
959
|
+
* reason the caller's pre-run pass is.
|
|
960
|
+
*/
|
|
961
|
+
private applyCompactedHistoryTransform(
|
|
962
|
+
compacted: Message[],
|
|
963
|
+
rlog: ReturnType<typeof getLogger>,
|
|
964
|
+
): Message[] {
|
|
965
|
+
if (!this.transformCompactedHistory) {
|
|
966
|
+
return compacted;
|
|
967
|
+
}
|
|
968
|
+
try {
|
|
969
|
+
return this.transformCompactedHistory(compacted);
|
|
970
|
+
} catch (err) {
|
|
971
|
+
rlog.warn(
|
|
972
|
+
{ err },
|
|
973
|
+
"Post-compaction history transform failed (non-fatal); keeping the compacted history as built",
|
|
974
|
+
);
|
|
975
|
+
return compacted;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
928
979
|
/**
|
|
929
980
|
* Compact the running history in place when the budget gate trips.
|
|
930
981
|
*
|
|
@@ -1363,7 +1414,14 @@ export class AgentLoop {
|
|
|
1363
1414
|
overflowSignal ?? undefined,
|
|
1364
1415
|
);
|
|
1365
1416
|
if (attempt.history) {
|
|
1366
|
-
|
|
1417
|
+
// Trim before anything else reads the rebuilt array: the
|
|
1418
|
+
// provider call further down this same iteration sends it, so
|
|
1419
|
+
// content compaction reintroduced has to be brought back
|
|
1420
|
+
// within the caller's bounds here or it ships un-trimmed.
|
|
1421
|
+
history = this.applyCompactedHistoryTransform(
|
|
1422
|
+
attempt.history,
|
|
1423
|
+
rlog,
|
|
1424
|
+
);
|
|
1367
1425
|
// The compacted, re-injected array is the new base; output
|
|
1368
1426
|
// produced after this point is what the wrapper persists.
|
|
1369
1427
|
newMessagesStart = history.length;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `contact_form_closed` SSE event.
|
|
3
|
+
*
|
|
4
|
+
* Tells every connected client that a contact form is over, so the card comes
|
|
5
|
+
* down instead of sitting there offering to submit an answer the gateway will
|
|
6
|
+
* now refuse.
|
|
7
|
+
*
|
|
8
|
+
* Both contact forms are workspace-global, broadcast without a conversation,
|
|
9
|
+
* which is why this is its own event rather than `interaction_resolved`: that
|
|
10
|
+
* one is conversation-scoped on the wire and is deliberately not broadcast for
|
|
11
|
+
* a conversation-less interaction.
|
|
12
|
+
*
|
|
13
|
+
* Canonical wire-contract source. Daemon code imports the type directly from
|
|
14
|
+
* this file; external consumers import via `@vellumai/assistant-api`.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
|
|
19
|
+
export const ContactFormClosedEventSchema = z.object({
|
|
20
|
+
type: z.literal("contact_form_closed"),
|
|
21
|
+
requestId: z.string(),
|
|
22
|
+
/**
|
|
23
|
+
* Why it closed. Every client saw the form, so every client needs to know:
|
|
24
|
+
* "answered" and "cancelled" retire the card on the clients that did not
|
|
25
|
+
* answer it, and "timed_out" retires it on all of them.
|
|
26
|
+
*/
|
|
27
|
+
reason: z.enum(["answered", "cancelled", "timed_out"]),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export type ContactFormClosedEvent = z.infer<
|
|
31
|
+
typeof ContactFormClosedEventSchema
|
|
32
|
+
>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `contact_record_request` SSE event.
|
|
3
|
+
*
|
|
4
|
+
* Server to client form asking the guardian to confirm a contact record write
|
|
5
|
+
* the assistant proposed: a create, an update, or a delete. Emitted by the
|
|
6
|
+
* `contacts/record-prompt` IPC route while a `pendingContactPrompts` entry
|
|
7
|
+
* awaits a reply.
|
|
8
|
+
*
|
|
9
|
+
* The proposed values are a starting point, not a decision. The guardian may
|
|
10
|
+
* edit the name and notes before submitting, and the client posts the result
|
|
11
|
+
* straight to the gateway, which owns the write.
|
|
12
|
+
*
|
|
13
|
+
* The form closes when the guardian answers it, or with a
|
|
14
|
+
* `contact_form_closed` event if it times out first. `interaction_resolved`
|
|
15
|
+
* does not cover it: that event is conversation-scoped on the wire and is
|
|
16
|
+
* deliberately not broadcast for a conversation-less interaction like this
|
|
17
|
+
* one.
|
|
18
|
+
*
|
|
19
|
+
* Canonical wire-contract source. Daemon code imports the type directly from
|
|
20
|
+
* this file; external consumers import via `@vellumai/assistant-api`.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
|
|
25
|
+
export const ContactRecordRequestEventSchema = z.object({
|
|
26
|
+
type: z.literal("contact_record_request"),
|
|
27
|
+
requestId: z.string(),
|
|
28
|
+
operation: z.enum(["create", "update", "delete"]),
|
|
29
|
+
/** Target of an update or delete. Absent on create. */
|
|
30
|
+
contactId: z.string().optional(),
|
|
31
|
+
/** Current name of the target, so the form can show what is changing. */
|
|
32
|
+
currentDisplayName: z.string().optional(),
|
|
33
|
+
/**
|
|
34
|
+
* The target's current notes. The form submits a field only when it differs
|
|
35
|
+
* from what is stored, so it needs what is stored to compare against.
|
|
36
|
+
*/
|
|
37
|
+
currentNotes: z.string().optional(),
|
|
38
|
+
/**
|
|
39
|
+
* The target's channels, so a delete confirmation can say which of two
|
|
40
|
+
* same-named contacts it is about, and what access is about to be lost.
|
|
41
|
+
*/
|
|
42
|
+
channels: z
|
|
43
|
+
.array(z.object({ type: z.string(), address: z.string() }))
|
|
44
|
+
.optional(),
|
|
45
|
+
|
|
46
|
+
/** Proposed name, prefilled into the form. */
|
|
47
|
+
displayName: z.string().optional(),
|
|
48
|
+
/** Proposed notes, prefilled into the form. */
|
|
49
|
+
notes: z.string().optional(),
|
|
50
|
+
/**
|
|
51
|
+
* Whether the caller asked for these notes explicitly. A proposal that
|
|
52
|
+
* happens to match what is stored is still a change the guardian confirmed,
|
|
53
|
+
* and an unreadable mirror reports stored notes as empty, so the form cannot
|
|
54
|
+
* tell "clear them" from "nothing to do" by comparison alone.
|
|
55
|
+
*/
|
|
56
|
+
notesProposed: z.boolean().optional(),
|
|
57
|
+
label: z.string().optional(),
|
|
58
|
+
description: z.string().optional(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export type ContactRecordRequestEvent = z.infer<
|
|
62
|
+
typeof ContactRecordRequestEventSchema
|
|
63
|
+
>;
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* address (phone, email, etc.). Emitted by the `contacts/prompt` IPC
|
|
6
6
|
* route while a `pendingContactPrompts` entry awaits a reply.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The form closes when the user answers it, or with a
|
|
9
|
+
* `contact_form_closed` event if it times out first. `interaction_resolved`
|
|
10
|
+
* does not cover it: that event is conversation-scoped on the wire and is
|
|
11
|
+
* deliberately not broadcast for a conversation-less interaction like this
|
|
12
|
+
* one.
|
|
11
13
|
*
|
|
12
14
|
* `channel` and `role` are advisory hints, not enforced enums — the
|
|
13
15
|
* client may render any input it likes and post back a structured
|
|
@@ -29,6 +31,11 @@ export const ContactRequestEventSchema = z.object({
|
|
|
29
31
|
label: z.string().optional(),
|
|
30
32
|
description: z.string().optional(),
|
|
31
33
|
role: z.string().optional(),
|
|
34
|
+
/**
|
|
35
|
+
* Initial state of the form's "mark verified" checkbox, from the CLI's
|
|
36
|
+
* `--verify`. What the guardian submits is what gets attested.
|
|
37
|
+
*/
|
|
38
|
+
verify: z.boolean().optional(),
|
|
32
39
|
});
|
|
33
40
|
|
|
34
41
|
export type ContactRequestEvent = z.infer<typeof ContactRequestEventSchema>;
|
package/src/api/index.ts
CHANGED
|
@@ -23,6 +23,8 @@ import { CompactionCircuitOpenEventSchema } from "./events/compaction-circuit-op
|
|
|
23
23
|
import { ConfigChangedEventSchema } from "./events/config-changed.js";
|
|
24
24
|
import { ConfirmationRequestEventSchema } from "./events/confirmation-request.js";
|
|
25
25
|
import { ConfirmationStateChangedEventSchema } from "./events/confirmation-state-changed.js";
|
|
26
|
+
import { ContactFormClosedEventSchema } from "./events/contact-form-closed.js";
|
|
27
|
+
import { ContactRecordRequestEventSchema } from "./events/contact-record-request.js";
|
|
26
28
|
import { ContactRequestEventSchema } from "./events/contact-request.js";
|
|
27
29
|
import { ContactsChangedEventSchema } from "./events/contacts-changed.js";
|
|
28
30
|
import { ContextCompactedEventSchema } from "./events/context-compacted.js";
|
|
@@ -285,6 +287,14 @@ export {
|
|
|
285
287
|
type ConfirmationStateChangedEvent,
|
|
286
288
|
ConfirmationStateChangedEventSchema,
|
|
287
289
|
} from "./events/confirmation-state-changed.js";
|
|
290
|
+
export {
|
|
291
|
+
type ContactFormClosedEvent,
|
|
292
|
+
ContactFormClosedEventSchema,
|
|
293
|
+
} from "./events/contact-form-closed.js";
|
|
294
|
+
export {
|
|
295
|
+
type ContactRecordRequestEvent,
|
|
296
|
+
ContactRecordRequestEventSchema,
|
|
297
|
+
} from "./events/contact-record-request.js";
|
|
288
298
|
export {
|
|
289
299
|
type ContactRequestEvent,
|
|
290
300
|
ContactRequestEventSchema,
|
|
@@ -767,6 +777,12 @@ export {
|
|
|
767
777
|
type FeedItemDetailPanelKind,
|
|
768
778
|
FeedItemDetailPanelKindSchema,
|
|
769
779
|
FeedItemDetailPanelSchema,
|
|
780
|
+
type FeedItemGuardianIntent,
|
|
781
|
+
FeedItemGuardianIntentSchema,
|
|
782
|
+
type FeedItemGuardianRequest,
|
|
783
|
+
FeedItemGuardianRequestSchema,
|
|
784
|
+
type FeedItemGuardianStatus,
|
|
785
|
+
FeedItemGuardianStatusSchema,
|
|
770
786
|
FeedItemSchema,
|
|
771
787
|
type FeedItemSourceType,
|
|
772
788
|
FeedItemSourceTypeSchema,
|
|
@@ -776,8 +792,10 @@ export {
|
|
|
776
792
|
FeedItemTypeSchema,
|
|
777
793
|
type FeedItemUrgency,
|
|
778
794
|
FeedItemUrgencySchema,
|
|
795
|
+
GUARDIAN_TERMINAL_REASON_SUPERSEDED,
|
|
779
796
|
type HomeFeedResponse,
|
|
780
797
|
HomeFeedResponseSchema,
|
|
798
|
+
isPendingGuardianFeedItem,
|
|
781
799
|
type RelationshipState,
|
|
782
800
|
RelationshipStateSchema,
|
|
783
801
|
type RelationshipTier,
|
|
@@ -952,6 +970,8 @@ export const AssistantEventSchema = z.discriminatedUnion("type", [
|
|
|
952
970
|
ConfigChangedEventSchema,
|
|
953
971
|
ConfirmationRequestEventSchema,
|
|
954
972
|
ConfirmationStateChangedEventSchema,
|
|
973
|
+
ContactFormClosedEventSchema,
|
|
974
|
+
ContactRecordRequestEventSchema,
|
|
955
975
|
ContactRequestEventSchema,
|
|
956
976
|
ContactsChangedEventSchema,
|
|
957
977
|
ContextCompactedEventSchema,
|
|
@@ -607,6 +607,26 @@ export const ConversationMessageSchema = z.object({
|
|
|
607
607
|
* system notices — no avatar, no persona bubble — and never group them
|
|
608
608
|
* with adjacent assistant turns. */
|
|
609
609
|
systemCard: z.boolean().optional(),
|
|
610
|
+
/** Set on a turn whose whole reply was the `<no_response/>` sentinel
|
|
611
|
+
* (`metadata.messageKind === "no_response"`): the assistant deliberately
|
|
612
|
+
* chose silence. Clients render a quiet standalone notice instead of the
|
|
613
|
+
* sentinel text, and treat the row as the turn's reply so nothing keeps
|
|
614
|
+
* waiting for one. */
|
|
615
|
+
noResponse: z.boolean().optional(),
|
|
616
|
+
/** Present on a reaction row, either direction: an inbound reaction the
|
|
617
|
+
* daemon persisted, or the assistant's own (`selfAuthored`). Clients
|
|
618
|
+
* render a reaction line from this instead of the row's stored sentinel
|
|
619
|
+
* text. Slack rows additionally carry their own `slackMessage` envelope,
|
|
620
|
+
* which Slack-aware renderers may prefer. */
|
|
621
|
+
reaction: z
|
|
622
|
+
.object({
|
|
623
|
+
emoji: z.string(),
|
|
624
|
+
op: z.enum(["added", "removed"]),
|
|
625
|
+
targetMessageId: z.string(),
|
|
626
|
+
actorDisplayName: z.string().optional(),
|
|
627
|
+
selfAuthored: z.boolean().optional(),
|
|
628
|
+
})
|
|
629
|
+
.optional(),
|
|
610
630
|
/** Present when this assistant row is a daemon-persisted provider-failure
|
|
611
631
|
* notice (`metadata.messageKind === "provider_error"`); clients may render
|
|
612
632
|
* a themed card instead of a persona bubble. `code` is the stable
|
|
@@ -107,6 +107,83 @@ export const FeedItemDetailPanelSchema = z.object({
|
|
|
107
107
|
});
|
|
108
108
|
export type FeedItemDetailPanel = z.infer<typeof FeedItemDetailPanelSchema>;
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Canonical guardian-request status projected onto a feed item.
|
|
112
|
+
*
|
|
113
|
+
* Values mirror `GuardianRequestStatusSchema` in
|
|
114
|
+
* `@vellumai/gateway-client` (this file is copied verbatim into client
|
|
115
|
+
* packages, so it cannot import the contract; the daemon's projection
|
|
116
|
+
* writer asserts the two stay aligned at compile time).
|
|
117
|
+
*/
|
|
118
|
+
export const FeedItemGuardianStatusSchema = z.enum([
|
|
119
|
+
"pending",
|
|
120
|
+
"approved",
|
|
121
|
+
"denied",
|
|
122
|
+
"expired",
|
|
123
|
+
"cancelled",
|
|
124
|
+
]);
|
|
125
|
+
export type FeedItemGuardianStatus = z.infer<
|
|
126
|
+
typeof FeedItemGuardianStatusSchema
|
|
127
|
+
>;
|
|
128
|
+
|
|
129
|
+
/** Whether the guardian is being asked to approve or to answer. */
|
|
130
|
+
export const FeedItemGuardianIntentSchema = z.enum(["approval", "question"]);
|
|
131
|
+
export type FeedItemGuardianIntent = z.infer<
|
|
132
|
+
typeof FeedItemGuardianIntentSchema
|
|
133
|
+
>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Read projection of one canonical guardian request onto its feed item.
|
|
137
|
+
*
|
|
138
|
+
* The feed item carrying this is the request's single "Needs attention"
|
|
139
|
+
* home: exactly one item per `requestId`, kept current by the daemon's
|
|
140
|
+
* status fan-out. Clients derive every affordance from `status` +
|
|
141
|
+
* `intent`: a `pending` approval offers Approve/Reject (via
|
|
142
|
+
* `POST /v1/guardian-actions/decision`), a `pending` question routes to
|
|
143
|
+
* the source conversation, and a terminal status renders as a receipt.
|
|
144
|
+
* Nothing here is an independent delivery record; it restates gateway
|
|
145
|
+
* `guardian_requests` state and is never a source of truth on its own.
|
|
146
|
+
*/
|
|
147
|
+
export const FeedItemGuardianRequestSchema = z.object({
|
|
148
|
+
requestId: z.string(),
|
|
149
|
+
/**
|
|
150
|
+
* Guardian request kind (`tool_approval`, `tool_grant_request`,
|
|
151
|
+
* `pending_question`, `access_request`). A string rather than an enum so
|
|
152
|
+
* rows written by a newer daemon still parse.
|
|
153
|
+
*/
|
|
154
|
+
kind: z.string(),
|
|
155
|
+
intent: FeedItemGuardianIntentSchema,
|
|
156
|
+
status: FeedItemGuardianStatusSchema,
|
|
157
|
+
/** Display name or identifier of the requester (e.g. "Alice"). */
|
|
158
|
+
requesterLabel: z.string().optional(),
|
|
159
|
+
/** Tool the request is about, when it is a tool approval/grant. */
|
|
160
|
+
toolName: z.string().optional(),
|
|
161
|
+
/** Channel the request originated from (e.g. "slack"). */
|
|
162
|
+
sourceChannel: z.string().optional(),
|
|
163
|
+
/** Display label for the originating chat (e.g. "#user-feedback"). */
|
|
164
|
+
sourceContextLabel: z.string().optional(),
|
|
165
|
+
/** Permalink to the originating channel message, when derivable. */
|
|
166
|
+
sourceUrl: z.string().optional(),
|
|
167
|
+
/** Web URL of the Slack guardian-DM approval card ("Open in Slack"). */
|
|
168
|
+
slackCardUrl: z.string().optional(),
|
|
169
|
+
/** slack:// deep link for the same card, preferred on devices with the app. */
|
|
170
|
+
slackCardAppUrl: z.string().optional(),
|
|
171
|
+
/** Action that resolved the request, for terminal statuses. */
|
|
172
|
+
decidedAction: z.string().optional(),
|
|
173
|
+
/** Display label of the decider, when the decision came from a person. */
|
|
174
|
+
decidedByLabel: z.string().optional(),
|
|
175
|
+
/** ISO-8601 time the request reached its terminal status. */
|
|
176
|
+
decidedAt: z.string().optional(),
|
|
177
|
+
/**
|
|
178
|
+
* Why a non-decision terminal status was reached (e.g. `superseded`
|
|
179
|
+
* when a newer message auto-denied the request). Display-only.
|
|
180
|
+
*/
|
|
181
|
+
terminalReason: z.string().optional(),
|
|
182
|
+
});
|
|
183
|
+
export type FeedItemGuardianRequest = z.infer<
|
|
184
|
+
typeof FeedItemGuardianRequestSchema
|
|
185
|
+
>;
|
|
186
|
+
|
|
110
187
|
/**
|
|
111
188
|
* A single item rendered in the Home feed.
|
|
112
189
|
*
|
|
@@ -135,6 +212,9 @@ export const FeedItemSchema = z.object({
|
|
|
135
212
|
urgency: FeedItemUrgencySchema.optional(),
|
|
136
213
|
conversationId: z.string().optional(),
|
|
137
214
|
detailPanel: FeedItemDetailPanelSchema.optional(),
|
|
215
|
+
// Present only on the canonical "Needs attention" item projecting a
|
|
216
|
+
// guardian request; see FeedItemGuardianRequestSchema.
|
|
217
|
+
guardianRequest: FeedItemGuardianRequestSchema.optional(),
|
|
138
218
|
category: FeedItemCategorySchema.optional(),
|
|
139
219
|
noteworthy: z.boolean().optional(),
|
|
140
220
|
fromAssistant: z.boolean().optional(),
|
|
@@ -150,6 +230,27 @@ export const FeedItemSchema = z.object({
|
|
|
150
230
|
});
|
|
151
231
|
export type FeedItem = z.infer<typeof FeedItemSchema>;
|
|
152
232
|
|
|
233
|
+
/**
|
|
234
|
+
* `terminalReason` value for a request auto-denied because a newer
|
|
235
|
+
* inbound message superseded it. Shared by the daemon rails that record
|
|
236
|
+
* it and the clients that render "Superseded" instead of a rejection.
|
|
237
|
+
*/
|
|
238
|
+
export const GUARDIAN_TERMINAL_REASON_SUPERSEDED = "superseded";
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Whether a feed item is the live projection of an unresolved guardian
|
|
242
|
+
* request. Shared by the daemon (bulk-dismiss protection in the feed
|
|
243
|
+
* writer) and clients (visibility carve-outs, bulk-action id sets) so
|
|
244
|
+
* "pending guardian item" means the same thing on both sides: such an
|
|
245
|
+
* item must stay actionable until the canonical request resolves, and
|
|
246
|
+
* only its terminal receipt is ordinarily clearable.
|
|
247
|
+
*/
|
|
248
|
+
export function isPendingGuardianFeedItem(
|
|
249
|
+
item: Pick<FeedItem, "guardianRequest">,
|
|
250
|
+
): boolean {
|
|
251
|
+
return item.guardianRequest?.status === "pending";
|
|
252
|
+
}
|
|
253
|
+
|
|
153
254
|
// ---------------------------------------------------------------------------
|
|
154
255
|
// Suggested prompt
|
|
155
256
|
// ---------------------------------------------------------------------------
|