@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
|
@@ -184,18 +184,18 @@ export async function getOrCreateConversation(
|
|
|
184
184
|
// messages live in memory on the instance being disposed, and the queue
|
|
185
185
|
// drains via an async dispatch after the current turn releases, so
|
|
186
186
|
// `isProcessing()` can read false while a queued turn is still pending:
|
|
187
|
-
// rebuilding in that gap would silently drop those messages.
|
|
188
|
-
//
|
|
187
|
+
// rebuilding in that gap would silently drop those messages. In-flight
|
|
188
|
+
// subagents are the same: the parent reads idle between its own tool calls
|
|
189
|
+
// while children still run, and rebuilding would abort them. The
|
|
190
|
+
// conversation stays stale and is rebuilt on a later call.
|
|
189
191
|
if (
|
|
190
192
|
!conversation ||
|
|
191
|
-
(conversation.isStale() &&
|
|
192
|
-
!conversation.isProcessing() &&
|
|
193
|
-
!conversation.hasQueuedMessages())
|
|
193
|
+
(conversation.isStale() && !conversation.hasInFlightWork())
|
|
194
194
|
) {
|
|
195
195
|
if (conversation) {
|
|
196
|
-
// Stale rebuild: the conversation id lives on, so abort
|
|
197
|
-
//
|
|
198
|
-
// retention window.
|
|
196
|
+
// Stale rebuild: the conversation id lives on, so abort any children
|
|
197
|
+
// that raced into flight after the idle check and keep terminal
|
|
198
|
+
// subagent results readable for the retention window.
|
|
199
199
|
getSubagentManager().abortAllForParent(conversationId);
|
|
200
200
|
conversation.dispose();
|
|
201
201
|
}
|
|
@@ -400,32 +400,22 @@ export function clearAllActiveConversations(): number {
|
|
|
400
400
|
|
|
401
401
|
/**
|
|
402
402
|
* Evict in-memory conversations after a config/prompt/skills reload so the next
|
|
403
|
-
* turn rebuilds them against the new config.
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
403
|
+
* turn rebuilds them against the new config. Conversations with in-flight work
|
|
404
|
+
* (a live turn, a queued successor, or an active subagent) are marked stale
|
|
405
|
+
* and rebuilt by `getOrCreateConversation` once that work finishes. Idle
|
|
406
|
+
* conversations are disposed and dropped. Also used when provider credentials
|
|
407
407
|
* change.
|
|
408
408
|
*/
|
|
409
409
|
export function evictConversationsForReload(): void {
|
|
410
410
|
const subagentManager = getSubagentManager();
|
|
411
411
|
for (const [id, conversation] of conversationEntries()) {
|
|
412
|
-
|
|
413
|
-
// async dispatch after the current turn releases, so `isProcessing()` can
|
|
414
|
-
// read false while a queued turn is still pending. Disposing in that gap
|
|
415
|
-
// would silently drop the queued messages, so mark it stale instead and
|
|
416
|
-
// let it rebuild once the queue has run.
|
|
417
|
-
if (!conversation.isProcessing() && !conversation.hasQueuedMessages()) {
|
|
418
|
-
// Same protection the TTL/LRU evictor applies: an idle parent still
|
|
419
|
-
// owns mid-task children, and aborting them here would kill that work.
|
|
420
|
-
if (subagentManager.hasActiveChildren(id)) {
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
423
|
-
subagentManager.abortAllForParent(id);
|
|
424
|
-
conversation.dispose();
|
|
425
|
-
deleteConversation(id);
|
|
426
|
-
removeFromEvictor(id);
|
|
427
|
-
} else {
|
|
412
|
+
if (conversation.hasInFlightWork()) {
|
|
428
413
|
conversation.markStale();
|
|
414
|
+
continue;
|
|
429
415
|
}
|
|
416
|
+
subagentManager.abortAllForParent(id);
|
|
417
|
+
conversation.dispose();
|
|
418
|
+
deleteConversation(id);
|
|
419
|
+
removeFromEvictor(id);
|
|
430
420
|
}
|
|
431
421
|
}
|
|
@@ -543,19 +543,29 @@ export function findPersistedSurfaceInfo(
|
|
|
543
543
|
* user-action completion paths: the underlying request is already resolved, so
|
|
544
544
|
* withholding the announcement on a transient write failure strands a live,
|
|
545
545
|
* clickable approval card for a decision that has already been made.
|
|
546
|
+
*
|
|
547
|
+
* Returns whether the completion persisted durably (see
|
|
548
|
+
* {@link markSurfaceCompleted}); the broadcast goes out either way. A caller
|
|
549
|
+
* whose own receipt depends on the card staying completed across a reload
|
|
550
|
+
* (the expiry sweep) holds that receipt back on false and retries.
|
|
546
551
|
*/
|
|
547
552
|
export function completeSurfaceAndNotify(
|
|
548
553
|
conversationId: string,
|
|
549
554
|
surfaceId: string,
|
|
550
555
|
summary: string,
|
|
551
|
-
):
|
|
552
|
-
|
|
556
|
+
): boolean {
|
|
557
|
+
const persisted = markSurfaceCompleted(
|
|
558
|
+
{ conversationId },
|
|
559
|
+
surfaceId,
|
|
560
|
+
summary,
|
|
561
|
+
);
|
|
553
562
|
broadcastMessage({
|
|
554
563
|
type: "ui_surface_complete",
|
|
555
564
|
conversationId,
|
|
556
565
|
surfaceId,
|
|
557
566
|
summary,
|
|
558
567
|
});
|
|
568
|
+
return persisted;
|
|
559
569
|
}
|
|
560
570
|
|
|
561
571
|
/**
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { AssistantEvent } from "../api/index.js";
|
|
10
10
|
import {
|
|
11
|
+
type ClientOs,
|
|
11
12
|
type HostProxyCapability,
|
|
12
13
|
parseClientOs,
|
|
13
14
|
supportsHostProxy,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
import { getIsPlatform } from "../config/env-registry.js";
|
|
16
17
|
import { getConfig } from "../config/loader.js";
|
|
17
18
|
import { isMemoryEnabled } from "../config/memory-v3-gate.js";
|
|
19
|
+
import { supportsChannelReaction } from "../messaging/providers/index.js";
|
|
18
20
|
import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
19
21
|
import type { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
20
22
|
import { getBindingByConversation } from "../persistence/external-conversation-store.js";
|
|
@@ -433,8 +435,7 @@ export function createToolExecutor(
|
|
|
433
435
|
toolUseId,
|
|
434
436
|
isPlatformHosted: getIsPlatform(),
|
|
435
437
|
transportInterface: ctx.transportInterface,
|
|
436
|
-
clientOs:
|
|
437
|
-
parseClientOs(ctx.currentTurnClientOs ?? ctx.clientOs) ?? undefined,
|
|
438
|
+
clientOs: resolveTurnClientOs(ctx).clientOs,
|
|
438
439
|
overrideProfile: ctx.currentTurnOverrideProfile,
|
|
439
440
|
cronRunId: ctx.currentTurnCronRunId,
|
|
440
441
|
invokingCallSite: ctx.currentCallSite ?? "mainAgent",
|
|
@@ -713,14 +714,14 @@ export const ALLOWLIST_ONLY_TOOL_NAMES = new Set<string>([
|
|
|
713
714
|
]);
|
|
714
715
|
|
|
715
716
|
/**
|
|
716
|
-
*
|
|
717
|
-
*
|
|
717
|
+
* Host OS of the client driving this turn. The Electron renderer reports
|
|
718
|
+
* `interface: "web"` and carries the real OS in `clientOs`, so this prefers
|
|
719
|
+
* the frozen per-turn value and only falls back to a desktop transport.
|
|
718
720
|
*/
|
|
719
|
-
function
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
}
|
|
721
|
+
function resolveTurnClientOs(ctx: Conversation): {
|
|
722
|
+
clientOs: ClientOs | undefined;
|
|
723
|
+
transportInterface: Conversation["transportInterface"];
|
|
724
|
+
} {
|
|
724
725
|
const pin = ctx.toolContextPin;
|
|
725
726
|
const transportInterface = pin
|
|
726
727
|
? pin.transportInterface
|
|
@@ -731,6 +732,19 @@ function isToolSupportedOnClientOs(name: string, ctx: Conversation): boolean {
|
|
|
731
732
|
(transportInterface === "macos" || transportInterface === "windows"
|
|
732
733
|
? transportInterface
|
|
733
734
|
: undefined));
|
|
735
|
+
return { clientOs, transportInterface };
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Windows parity gate: skill tools may declare `supported_client_os`; drop
|
|
740
|
+
* them when the turn's client OS (or pinned OS for wakes) is not listed.
|
|
741
|
+
*/
|
|
742
|
+
function isToolSupportedOnClientOs(name: string, ctx: Conversation): boolean {
|
|
743
|
+
const supportedClientOs = getTool(name)?.supportedClientOs;
|
|
744
|
+
if (!supportedClientOs) {
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
const { clientOs, transportInterface } = resolveTurnClientOs(ctx);
|
|
734
748
|
return supportsClientOsForSkillTool(supportedClientOs, name, {
|
|
735
749
|
clientOs,
|
|
736
750
|
transportInterface,
|
|
@@ -808,6 +822,20 @@ export function isToolActiveForContext(
|
|
|
808
822
|
return true;
|
|
809
823
|
}
|
|
810
824
|
}
|
|
825
|
+
// The react capability follows the transport's declaration: the tool is on
|
|
826
|
+
// the wire exactly when the turn's channel transport implements `react`,
|
|
827
|
+
// so the model never sees an option the channel cannot honor. The turn's
|
|
828
|
+
// own capabilities take precedence over the conversation's structural ones
|
|
829
|
+
// (same order as `conversationSupportsDynamicUi`): an app-side turn on a
|
|
830
|
+
// channel-origin conversation has no channel message to react to, and the
|
|
831
|
+
// executor's channel comes from the turn. Wake pins read as no channel
|
|
832
|
+
// and hide it.
|
|
833
|
+
if (name === "react_to_message") {
|
|
834
|
+
const turnChannel = pin
|
|
835
|
+
? undefined
|
|
836
|
+
: (ctx.currentTurnChannelCapabilities ?? channelCapabilities)?.channel;
|
|
837
|
+
return supportsChannelReaction(turnChannel);
|
|
838
|
+
}
|
|
811
839
|
if (UI_SURFACE_TOOL_NAMES.has(name)) {
|
|
812
840
|
if (
|
|
813
841
|
channelCapabilities?.channel === "slack" &&
|
|
@@ -871,7 +899,8 @@ export function isToolActiveForContext(
|
|
|
871
899
|
if (PLATFORM_TOOL_NAMES.has(name)) {
|
|
872
900
|
// Check the *client's* platform, not the daemon's process.platform.
|
|
873
901
|
// In Docker the daemon runs on Linux but the connected client may be macOS.
|
|
874
|
-
|
|
902
|
+
const { clientOs } = resolveTurnClientOs(ctx);
|
|
903
|
+
return (clientOs === "macos" || clientOs === "windows") && !hasNoClient;
|
|
875
904
|
}
|
|
876
905
|
if (SUBAGENT_ONLY_TOOL_NAMES.has(name)) {
|
|
877
906
|
return ctx.isSubagent === true;
|
|
@@ -21,7 +21,7 @@ import { AgentLoop } from "../agent/loop.js";
|
|
|
21
21
|
import type { AssistantActivityStateEvent } from "../api/events/assistant-activity-state.js";
|
|
22
22
|
import type { ConfirmationStateChangedEvent } from "../api/events/confirmation-state-changed.js";
|
|
23
23
|
import type { AssistantEvent } from "../api/index.js";
|
|
24
|
-
import {
|
|
24
|
+
import { syncTerminalGuardianRequestStatus } from "../approvals/guardian-request-status-sync.js";
|
|
25
25
|
import type {
|
|
26
26
|
ChannelId,
|
|
27
27
|
InterfaceId,
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
derefToolResultReReads,
|
|
42
42
|
postTurnTruncateToolResults,
|
|
43
43
|
} from "../context/post-turn-tool-result-truncation.js";
|
|
44
|
+
import { readProviderMetadata } from "../messaging/read-provider-metadata.js";
|
|
44
45
|
import { isGuardianCardRow } from "../notifications/approval-card-data.js";
|
|
45
46
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
46
47
|
import { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
@@ -55,6 +56,7 @@ import {
|
|
|
55
56
|
setConversationProcessingStartedAt,
|
|
56
57
|
} from "../persistence/conversation-crud.js";
|
|
57
58
|
import { getResolvedConversationDirPath } from "../persistence/conversation-directories.js";
|
|
59
|
+
import { extractTextFromStoredMessageContent } from "../persistence/message-content.js";
|
|
58
60
|
import { reportSlowSync } from "../persistence/slow-sync-log.js";
|
|
59
61
|
import { defaultCompact } from "../plugins/defaults/compaction/compact.js";
|
|
60
62
|
import {
|
|
@@ -137,6 +139,7 @@ import type {
|
|
|
137
139
|
} from "./conversation-queue-manager.js";
|
|
138
140
|
import { MessageQueue } from "./conversation-queue-manager.js";
|
|
139
141
|
import {
|
|
142
|
+
applySightFrameRetention,
|
|
140
143
|
type ChannelCapabilities,
|
|
141
144
|
getSlackCompactionWatermarkForPrefix,
|
|
142
145
|
getSlackWatermarkAdvanceForRowPrefix,
|
|
@@ -172,6 +175,8 @@ import { filterMessagesForUntrustedActor } from "./message-provenance.js";
|
|
|
172
175
|
import type { ConversationTransportMetadata } from "./message-types/conversations.js";
|
|
173
176
|
import { isHostProxyTransport } from "./message-types/conversations.js";
|
|
174
177
|
import { conversationMetadataSyncTag } from "./message-types/sync.js";
|
|
178
|
+
import { renderReactionHistoryText } from "./reaction-history-render.js";
|
|
179
|
+
import type { QueuedReactionRecord } from "./reaction-record.js";
|
|
175
180
|
import {
|
|
176
181
|
resolveSummarizeBoundary,
|
|
177
182
|
startsNewTurn,
|
|
@@ -538,6 +543,14 @@ export class Conversation {
|
|
|
538
543
|
/** @internal */ currentTurnSourceActorPrincipalId?: string;
|
|
539
544
|
/** @internal */ loadedHistoryTrustClass?: TrustClass;
|
|
540
545
|
/** @internal */ loadedHistoryPersonalMemoryAllowed?: boolean;
|
|
546
|
+
/** @internal */ loadedHistoryStale = false;
|
|
547
|
+
/**
|
|
548
|
+
* @internal Reactions the current turn delivered, awaiting their durable
|
|
549
|
+
* rows. Written mid-turn by the react tool, drained by the agent loop at
|
|
550
|
+
* the turn boundary so the rows never land between a tool_use and its
|
|
551
|
+
* tool_result.
|
|
552
|
+
*/
|
|
553
|
+
pendingReactionRecords: QueuedReactionRecord[] = [];
|
|
541
554
|
/** @internal */ voiceCallControlPrompt?: string;
|
|
542
555
|
/** @internal */ transportHints?: string[];
|
|
543
556
|
/**
|
|
@@ -887,6 +900,16 @@ export class Conversation {
|
|
|
887
900
|
conv.createdAt,
|
|
888
901
|
);
|
|
889
902
|
},
|
|
903
|
+
// Compaction rebuilds history from the stored rows, so it can retain
|
|
904
|
+
// camera frames the turn's pre-run pass had already stubbed, and the
|
|
905
|
+
// rebuilt array is sent on the next request of that same turn. Re-running
|
|
906
|
+
// the retention pass here is what holds the frame bound across a
|
|
907
|
+
// compaction instead of only from the next turn onward. The row read
|
|
908
|
+
// repeats rather than sharing the pre-run pass's map: compactions are
|
|
909
|
+
// rare, and a map captured before the turn's awaits would describe a
|
|
910
|
+
// conversation that has since gained rows.
|
|
911
|
+
transformCompactedHistory: (messages) =>
|
|
912
|
+
applySightFrameRetention(messages, this.conversationId),
|
|
890
913
|
});
|
|
891
914
|
createContextWindowManager({
|
|
892
915
|
provider,
|
|
@@ -1052,6 +1075,9 @@ export class Conversation {
|
|
|
1052
1075
|
|
|
1053
1076
|
async loadFromDb(): Promise<LoadFromDbResult> {
|
|
1054
1077
|
const loadStartedAt = performance.now();
|
|
1078
|
+
// Cleared before the rows are read: a channel event persisted mid-load
|
|
1079
|
+
// re-marks the flag, so the next turn reloads rather than missing it.
|
|
1080
|
+
this.loadedHistoryStale = false;
|
|
1055
1081
|
const trustClass = this.trustContext?.trustClass;
|
|
1056
1082
|
const canAccessMemory = resolveCapabilities(trustClass).canAccessMemory;
|
|
1057
1083
|
const allDbMessages = getMessages(this.conversationId);
|
|
@@ -1142,6 +1168,44 @@ export class Conversation {
|
|
|
1142
1168
|
}
|
|
1143
1169
|
return v3PrunedSlugsMemo;
|
|
1144
1170
|
};
|
|
1171
|
+
// Provider-id → row-text index for reaction target resolution, built
|
|
1172
|
+
// lazily on the first reaction row: most conversations carry none, so
|
|
1173
|
+
// most loads never walk the rows a second time. Keyed over the full
|
|
1174
|
+
// (unsliced) history so a reaction whose target was compacted out of
|
|
1175
|
+
// context still resolves its quote.
|
|
1176
|
+
let reactionTargetIndexMemo: Map<string, string> | null = null;
|
|
1177
|
+
const resolveReactionTarget = (
|
|
1178
|
+
targetMessageId: string,
|
|
1179
|
+
): string | undefined => {
|
|
1180
|
+
if (reactionTargetIndexMemo === null) {
|
|
1181
|
+
reactionTargetIndexMemo = new Map();
|
|
1182
|
+
for (const row of dbMessages) {
|
|
1183
|
+
const rowMeta = readProviderMetadata(row.metadata);
|
|
1184
|
+
// A deleted target is a logical erasure (same rule as the Slack
|
|
1185
|
+
// transcript renderer), so its text is never quoted back.
|
|
1186
|
+
if (
|
|
1187
|
+
rowMeta?.eventKind === "message" &&
|
|
1188
|
+
rowMeta.deletedAt === undefined &&
|
|
1189
|
+
rowMeta.messageId
|
|
1190
|
+
) {
|
|
1191
|
+
const text = extractTextFromStoredMessageContent(row.content);
|
|
1192
|
+
if (text) {
|
|
1193
|
+
// A split reply posts several provider messages from one row;
|
|
1194
|
+
// a reaction may name any of them.
|
|
1195
|
+
for (const id of [
|
|
1196
|
+
rowMeta.messageId,
|
|
1197
|
+
...(rowMeta.additionalMessageIds ?? []),
|
|
1198
|
+
]) {
|
|
1199
|
+
if (!reactionTargetIndexMemo.has(id)) {
|
|
1200
|
+
reactionTargetIndexMemo.set(id, text);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return reactionTargetIndexMemo.get(targetMessageId);
|
|
1208
|
+
};
|
|
1145
1209
|
const parsedMessages: Message[] = slicedDbMessages.map((m, index, arr) => {
|
|
1146
1210
|
const isPreStripped = index < preStrippedCount;
|
|
1147
1211
|
const role = m.role as "user" | "assistant";
|
|
@@ -1149,6 +1213,38 @@ export class Conversation {
|
|
|
1149
1213
|
|
|
1150
1214
|
content = reinjectAttachmentPathAnnotations(content, role, m.metadata);
|
|
1151
1215
|
|
|
1216
|
+
// Channel facts stamped in metadata render at load time rather than
|
|
1217
|
+
// at persist time, so every stored row reads correctly whenever it
|
|
1218
|
+
// was written: a reaction row's "[reaction]" sentinel renders as who
|
|
1219
|
+
// reacted with what to which message, and a deleted row's original
|
|
1220
|
+
// text gives way to a deletion marker (the delete intercepts stamp
|
|
1221
|
+
// metadata and deliberately leave the content column untouched). The
|
|
1222
|
+
// substring guard keeps the per-row metadata parse off rows that can
|
|
1223
|
+
// carry neither fact: both envelopes spell these keys literally, in
|
|
1224
|
+
// providerMeta and in nested slackMeta alike.
|
|
1225
|
+
if (
|
|
1226
|
+
m.metadata &&
|
|
1227
|
+
(m.metadata.includes("reaction") || m.metadata.includes("deletedAt"))
|
|
1228
|
+
) {
|
|
1229
|
+
const providerMeta = readProviderMetadata(m.metadata);
|
|
1230
|
+
if (providerMeta?.eventKind === "reaction") {
|
|
1231
|
+
const rendered = renderReactionHistoryText(
|
|
1232
|
+
providerMeta,
|
|
1233
|
+
resolveReactionTarget,
|
|
1234
|
+
// An assistant-role reaction row is the assistant's own act,
|
|
1235
|
+
// persisted by the react tool.
|
|
1236
|
+
{ selfAuthored: role === "assistant" },
|
|
1237
|
+
);
|
|
1238
|
+
if (rendered) {
|
|
1239
|
+
content = [{ type: "text", text: rendered }];
|
|
1240
|
+
}
|
|
1241
|
+
} else if (role === "user" && providerMeta?.deletedAt !== undefined) {
|
|
1242
|
+
// Neutral marker, no actor: Discord deletes can be authorless
|
|
1243
|
+
// (`actorUnattributed`), so the marker never claims who deleted.
|
|
1244
|
+
content = [{ type: "text", text: "[This message was deleted]" }];
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1152
1248
|
// Re-inject persisted injection blocks from metadata so it survives
|
|
1153
1249
|
// conversation reloads (eviction, restart, fork).
|
|
1154
1250
|
if (role === "user" && m.metadata && !isPreStripped) {
|
|
@@ -1522,6 +1618,7 @@ export class Conversation {
|
|
|
1522
1618
|
this.trustContext,
|
|
1523
1619
|
);
|
|
1524
1620
|
if (
|
|
1621
|
+
!this.loadedHistoryStale &&
|
|
1525
1622
|
this.loadedHistoryTrustClass === currentTrustClass &&
|
|
1526
1623
|
this.loadedHistoryPersonalMemoryAllowed === currentPersonalMemoryAllowed
|
|
1527
1624
|
) {
|
|
@@ -1530,6 +1627,22 @@ export class Conversation {
|
|
|
1530
1627
|
await this.loadFromDb();
|
|
1531
1628
|
}
|
|
1532
1629
|
|
|
1630
|
+
/**
|
|
1631
|
+
* Mark the resident history stale: a channel event (a reaction, edit, or
|
|
1632
|
+
* delete) was persisted straight to the store while this conversation was
|
|
1633
|
+
* loaded, so `this.messages` no longer reflects the rows. The next
|
|
1634
|
+
* {@link ensureActorScopedHistory} reloads instead of reusing the resident
|
|
1635
|
+
* history.
|
|
1636
|
+
*/
|
|
1637
|
+
markHistoryStale(): void {
|
|
1638
|
+
this.loadedHistoryStale = true;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/** Queue a delivered reaction for its durable row at the turn boundary. */
|
|
1642
|
+
queueReactionRecord(record: QueuedReactionRecord): void {
|
|
1643
|
+
this.pendingReactionRecords.push(record);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1533
1646
|
/**
|
|
1534
1647
|
* Deliver an event through the conversation's sink, then to every observer.
|
|
1535
1648
|
* The single emission point for conversation-level events (activity state,
|
|
@@ -1878,6 +1991,18 @@ export class Conversation {
|
|
|
1878
1991
|
return !this.queue.isEmpty;
|
|
1879
1992
|
}
|
|
1880
1993
|
|
|
1994
|
+
/**
|
|
1995
|
+
* True when dropping this instance would lose work that is still in flight:
|
|
1996
|
+
* a live turn, a queued successor, or a child subagent.
|
|
1997
|
+
*/
|
|
1998
|
+
hasInFlightWork(): boolean {
|
|
1999
|
+
return (
|
|
2000
|
+
this.isProcessing() ||
|
|
2001
|
+
this.hasQueuedMessages() ||
|
|
2002
|
+
getSubagentManager().hasActiveChildren(this.conversationId)
|
|
2003
|
+
);
|
|
2004
|
+
}
|
|
2005
|
+
|
|
1881
2006
|
/** FIFO snapshot of the messages currently waiting in the in-memory queue.
|
|
1882
2007
|
* Read-only — used to surface queued user messages in history responses. */
|
|
1883
2008
|
snapshotQueuedMessages(): QueuedMessage[] {
|
|
@@ -1963,20 +2088,16 @@ export class Conversation {
|
|
|
1963
2088
|
});
|
|
1964
2089
|
|
|
1965
2090
|
// Sync the gateway request status so stale "pending" records don't get
|
|
1966
|
-
// matched by later guardian reply routing
|
|
1967
|
-
//
|
|
1968
|
-
//
|
|
1969
|
-
//
|
|
1970
|
-
//
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
2091
|
+
// matched by later guardian reply routing, and withdraw the request's
|
|
2092
|
+
// approval cards when this sync is the write that landed. Fire-and-forget:
|
|
2093
|
+
// this method is sync with many callers (HTTP handlers, /v1/confirm,
|
|
2094
|
+
// channel bridges), the in-memory resolution above is authoritative, and
|
|
2095
|
+
// a CAS miss (the decision primitive already resolved it and withdrew the
|
|
2096
|
+
// cards itself, e.g. the channel approval path) is expected and harmless.
|
|
2097
|
+
void syncTerminalGuardianRequestStatus({
|
|
2098
|
+
requestId,
|
|
1974
2099
|
status: resolvedState,
|
|
1975
|
-
|
|
1976
|
-
log.warn(
|
|
1977
|
-
{ err, requestId },
|
|
1978
|
-
"Post-confirmation guardian request status sync failed",
|
|
1979
|
-
);
|
|
2100
|
+
syncContext: "post-confirmation",
|
|
1980
2101
|
});
|
|
1981
2102
|
}
|
|
1982
2103
|
|
|
@@ -2304,6 +2425,20 @@ export class Conversation {
|
|
|
2304
2425
|
}
|
|
2305
2426
|
}
|
|
2306
2427
|
|
|
2428
|
+
/**
|
|
2429
|
+
* Trim this conversation's aged camera frames out of a history about to be
|
|
2430
|
+
* sent to a provider, keeping only the newest few as real images.
|
|
2431
|
+
*
|
|
2432
|
+
* Thin binding of {@link applySightFrameRetention} to the conversation's own
|
|
2433
|
+
* id, for the send paths that hold the instance rather than a turn context:
|
|
2434
|
+
* the compaction pipeline's summarizer input, and the wake, which sends its
|
|
2435
|
+
* run input itself. Best-effort inside, so a failed row read degrades to the
|
|
2436
|
+
* untrimmed history.
|
|
2437
|
+
*/
|
|
2438
|
+
trimAgedSightFrames(messages: Message[]): Message[] {
|
|
2439
|
+
return applySightFrameRetention(messages, this.conversationId);
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2307
2442
|
/**
|
|
2308
2443
|
* Auto-threshold compaction gate. Runs the same durable compaction
|
|
2309
2444
|
* pipeline as {@link forceCompact} (summary call, circuit-breaker
|
|
@@ -2403,8 +2538,16 @@ export class Conversation {
|
|
|
2403
2538
|
},
|
|
2404
2539
|
)
|
|
2405
2540
|
: null;
|
|
2406
|
-
|
|
2407
|
-
|
|
2541
|
+
// Trim aged camera frames out of the summarizer's own input. Compaction
|
|
2542
|
+
// sends this array to a provider like any other request, so a long camera
|
|
2543
|
+
// session would otherwise put every frame it ever captured into the summary
|
|
2544
|
+
// call, which is the payload-size and many-image rejection retention exists
|
|
2545
|
+
// to prevent. Safe against the caller-fixed boundary below: the pass
|
|
2546
|
+
// replaces blocks inside messages and never adds, drops, or reorders one,
|
|
2547
|
+
// so `fixedTailStartIndex` still names the same message.
|
|
2548
|
+
const messagesToCompact = this.trimAgedSightFrames(
|
|
2549
|
+
slackChronologicalContext?.messages ?? this.messages,
|
|
2550
|
+
);
|
|
2408
2551
|
const compactedRowCountAtCall = this.contextCompactedMessageCount;
|
|
2409
2552
|
let result = await defaultCompact({
|
|
2410
2553
|
conversationId: this.conversationId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
import {
|
|
3
3
|
closeSync,
|
|
4
4
|
existsSync,
|
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
getPidPath,
|
|
24
24
|
getWorkspaceConfigPath,
|
|
25
25
|
} from "../util/platform.js";
|
|
26
|
+
import { readRawProcessCommand } from "../util/process-table.js";
|
|
27
|
+
import { isDaemonCommand } from "../util/worker-ownership.js";
|
|
26
28
|
|
|
27
29
|
const log = getLogger("lifecycle");
|
|
28
30
|
|
|
@@ -116,24 +118,11 @@ function isProcessRunning(pid: number): boolean {
|
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
/**
|
|
119
|
-
* Check whether a PID belongs to a vellum daemon process
|
|
120
|
-
*
|
|
121
|
-
* that reused a stale PID.
|
|
121
|
+
* Check whether a PID belongs to a vellum daemon process. Prevents signalling
|
|
122
|
+
* an unrelated process that reused a stale PID.
|
|
122
123
|
*/
|
|
123
124
|
function isVellumDaemonProcess(pid: number): boolean {
|
|
124
|
-
|
|
125
|
-
const cmd = execSync(`ps -ww -p ${pid} -o command=`, {
|
|
126
|
-
encoding: "utf-8",
|
|
127
|
-
timeout: 3000,
|
|
128
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
129
|
-
}).trim();
|
|
130
|
-
// The daemon is spawned as `bun run <path>/main.ts` — look for bun
|
|
131
|
-
// running our daemon entry point.
|
|
132
|
-
return cmd.includes("bun") && cmd.includes("daemon/main.ts");
|
|
133
|
-
} catch {
|
|
134
|
-
// Process exited or ps failed — treat as not ours.
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
125
|
+
return isDaemonCommand(readRawProcessCommand(pid));
|
|
137
126
|
}
|
|
138
127
|
|
|
139
128
|
/** Normalize a bind address to a connectable host for health checks.
|
|
@@ -65,6 +65,7 @@ describe("projectProviderForWire", () => {
|
|
|
65
65
|
const wire = projectProviderForWire(gemini!);
|
|
66
66
|
const modelIds = wire.models.map((model) => model.id);
|
|
67
67
|
const expectedGemini3ModelIds = [
|
|
68
|
+
"gemini-3.7-flash",
|
|
68
69
|
"gemini-3.6-flash",
|
|
69
70
|
"gemini-3.5-flash",
|
|
70
71
|
"gemini-3.5-flash-lite",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { peekAcpSessionManager } from "../../acp/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { GUARDIAN_TERMINAL_REASON_SUPERSEDED } from "../../api/responses/home.js";
|
|
3
|
+
import { syncTerminalGuardianRequestStatus } from "../../approvals/guardian-request-status-sync.js";
|
|
3
4
|
import {
|
|
4
5
|
clearAll,
|
|
5
6
|
getConversation,
|
|
@@ -522,18 +523,16 @@ export function supersedePendingInteractionsOnEnqueue(
|
|
|
522
523
|
source: "auto_deny" as const,
|
|
523
524
|
});
|
|
524
525
|
// Sync the gateway request so stale "pending" rows aren't matched
|
|
525
|
-
// by later guardian reply routing
|
|
526
|
-
//
|
|
526
|
+
// by later guardian reply routing, and withdraw the request's
|
|
527
|
+
// delivered approval cards so no surface keeps offering a decision
|
|
528
|
+
// that can no longer resolve anything. Fire-and-forget from this
|
|
529
|
+
// sync path: the in-memory denial is authoritative, and a CAS miss
|
|
527
530
|
// (already decided elsewhere) is expected and harmless.
|
|
528
|
-
void
|
|
529
|
-
|
|
530
|
-
expectedStatus: "pending",
|
|
531
|
+
void syncTerminalGuardianRequestStatus({
|
|
532
|
+
requestId: interaction.requestId,
|
|
531
533
|
status: "denied",
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
{ err, requestId: interaction.requestId },
|
|
535
|
-
"Auto-deny guardian request status sync failed",
|
|
536
|
-
);
|
|
534
|
+
syncContext: "supersede-on-enqueue",
|
|
535
|
+
terminalReason: GUARDIAN_TERMINAL_REASON_SUPERSEDED,
|
|
537
536
|
});
|
|
538
537
|
}
|
|
539
538
|
}
|
|
@@ -262,6 +262,7 @@ function commandResolvesElsewhere(
|
|
|
262
262
|
const resolved = execFileSync("/usr/bin/which", [commandName], {
|
|
263
263
|
encoding: "utf-8",
|
|
264
264
|
stdio: ["ignore", "pipe", "ignore"],
|
|
265
|
+
windowsHide: true,
|
|
265
266
|
}).trim();
|
|
266
267
|
return resolved !== "" && !candidatePaths.has(resolved);
|
|
267
268
|
} catch {
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -461,19 +461,16 @@ export async function runDaemon(): Promise<void> {
|
|
|
461
461
|
);
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
-
// Expire
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
//
|
|
471
|
-
//
|
|
472
|
-
//
|
|
473
|
-
//
|
|
474
|
-
// kinds (access_request, tool_grant_request) that expired while the
|
|
475
|
-
// daemon was stopped are transitioned so dedup logic doesn't return
|
|
476
|
-
// stale rows.
|
|
464
|
+
// Expire interaction-bound guardian requests (tool_approval,
|
|
465
|
+
// pending_question) left over from before this process started: their
|
|
466
|
+
// in-memory pending-interaction session references are gone, so they
|
|
467
|
+
// can never be completed. Daemon-keyed by design: the daemon triggers
|
|
468
|
+
// the gateway op at its own boot, and the gateway never runs it on its
|
|
469
|
+
// own restart. Persistent kinds (access_request, tool_grant_request) are
|
|
470
|
+
// never touched here, whatever their deadline: their expiry belongs to
|
|
471
|
+
// the periodic sweep, which owns the card-withdrawal and
|
|
472
|
+
// requester-notice fan-out, and the dedupe reads are deadline-aware so
|
|
473
|
+
// a past-deadline row waiting for the sweep suppresses nothing.
|
|
477
474
|
//
|
|
478
475
|
// Startup must not block on the gateway (daemon startup philosophy):
|
|
479
476
|
// on failure the periodic sweep still reaps time-expired rows, and
|
|
@@ -20,6 +20,10 @@ export const SYNC_TAGS = {
|
|
|
20
20
|
conversationsList: "conversations:list",
|
|
21
21
|
featureFlagsClient: "feature-flags:client",
|
|
22
22
|
featureFlagsAssistant: "feature-flags:assistant",
|
|
23
|
+
/** ACP credential-failure markers, which drive the inline Connect card.
|
|
24
|
+
* Invalidated when a token write retires them, so a client holding a
|
|
25
|
+
* restored prompt for the replaced token stops offering it. */
|
|
26
|
+
acpAuthRecovery: "acp:auth-recovery",
|
|
23
27
|
} as const;
|
|
24
28
|
|
|
25
29
|
export type KnownSyncInvalidationTag =
|
|
@@ -19,10 +19,7 @@ import {
|
|
|
19
19
|
parseChannelId,
|
|
20
20
|
parseInterfaceId,
|
|
21
21
|
} from "../channels/types.js";
|
|
22
|
-
import {
|
|
23
|
-
getAttachmentsByIds,
|
|
24
|
-
getSourcePathsForAttachments,
|
|
25
|
-
} from "../persistence/attachments-store.js";
|
|
22
|
+
import { resolveAttachmentsForPersist } from "../persistence/attachments-store.js";
|
|
26
23
|
import {
|
|
27
24
|
addMessage,
|
|
28
25
|
getConversation,
|
|
@@ -362,19 +359,7 @@ async function prepareConversationForMessage(
|
|
|
362
359
|
});
|
|
363
360
|
|
|
364
361
|
const attachments = attachmentIds
|
|
365
|
-
? (
|
|
366
|
-
const resolved = getAttachmentsByIds(attachmentIds, {
|
|
367
|
-
hydrateFileData: true,
|
|
368
|
-
});
|
|
369
|
-
const sourcePaths = getSourcePathsForAttachments(attachmentIds);
|
|
370
|
-
return resolved.map((a) => ({
|
|
371
|
-
id: a.id,
|
|
372
|
-
filename: a.originalFilename,
|
|
373
|
-
mimeType: a.mimeType,
|
|
374
|
-
data: a.dataBase64,
|
|
375
|
-
...(sourcePaths.has(a.id) ? { filePath: sourcePaths.get(a.id) } : {}),
|
|
376
|
-
}));
|
|
377
|
-
})()
|
|
362
|
+
? resolveAttachmentsForPersist(attachmentIds)
|
|
378
363
|
: [];
|
|
379
364
|
|
|
380
365
|
return { conversation, attachments };
|