@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5
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/bun.lock +20 -0
- package/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/openapi.yaml +196 -56
- package/package.json +4 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +42 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +164 -78
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/code-search-tool.test.ts +585 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/config-loader-backfill.test.ts +123 -10
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +19 -24
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +134 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/exploration-drift-hook.test.ts +3 -2
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
- package/src/__tests__/guardian-dispatch.test.ts +140 -1
- package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +290 -8
- package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +471 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
- package/src/__tests__/mcp-health-check.test.ts +2 -1
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +275 -5
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +191 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/openai-provider.test.ts +22 -12
- package/src/__tests__/openai-responses-provider.test.ts +12 -2
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +21 -83
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/plugin-pipeline.test.ts +96 -0
- package/src/__tests__/plugin-route-contribution.test.ts +6 -19
- package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
- package/src/__tests__/plugin-types.test.ts +2 -4
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-error-scenarios.test.ts +5 -4
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +4 -1
- package/src/__tests__/relay-server.test.ts +758 -32
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +49 -15
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -6
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +398 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +216 -20
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +154 -0
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +164 -1
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/events/conversation-notice.ts +26 -0
- package/src/api/index.ts +19 -1
- package/src/api/responses/llm-request-log-entry.ts +29 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/api/surfaces.ts +39 -3
- package/src/approvals/guardian-channel-delivery.ts +30 -0
- package/src/approvals/guardian-expiry-notifier.ts +148 -0
- package/src/approvals/guardian-request-resolvers.ts +17 -15
- package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +14 -9
- package/src/calls/inbound-trust-reader.ts +24 -2
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +188 -51
- package/src/calls/relay-setup-router.ts +47 -17
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/channels/types.ts +10 -20
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +242 -0
- package/src/cli/commands/plugins.ts +125 -13
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/cli/lib/publish-plugin.ts +633 -0
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/feature-flag-registry.json +5 -13
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/loader.ts +38 -5
- package/src/config/prune-seeded-callsite-defaults.ts +110 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +30 -34
- package/src/config/skills.ts +27 -5
- package/src/config/sync-gated-profiles.ts +12 -16
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -55
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/contacts/member-write-relay.ts +183 -0
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/assistant-attachments.ts +27 -4
- package/src/daemon/conversation-agent-loop.ts +28 -0
- package/src/daemon/conversation-notices.ts +60 -0
- package/src/daemon/conversation-process.ts +35 -16
- package/src/daemon/conversation-surfaces.ts +111 -38
- package/src/daemon/conversation-tool-setup.ts +71 -30
- package/src/daemon/conversation.ts +23 -1
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +17 -41
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +67 -26
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/lifecycle.ts +16 -0
- package/src/daemon/mcp-reload-service.ts +10 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/hooks/hook-loader.ts +341 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
- package/src/mcp/client.ts +15 -1
- package/src/mcp/mcp-auth-orchestrator.ts +6 -1
- package/src/mcp/mcp-header-store.ts +134 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -8
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +129 -61
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +37 -503
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +104 -0
- package/src/memory/llm-request-log-store.ts +26 -1
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +14 -8
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +106 -10
- package/src/memory/migrations/validate-migration-state.ts +105 -67
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +37 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +1294 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +204 -0
- package/src/memory/worker-process.ts +89 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/providers/slack/binding-metadata.ts +60 -0
- package/src/notifications/__tests__/broadcaster.test.ts +8 -8
- package/src/notifications/__tests__/connected-channels.test.ts +86 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +53 -25
- package/src/notifications/emit-signal.ts +34 -15
- package/src/onboarding/checkin-event.test.ts +222 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +12 -12
- package/src/plugin-api/types.ts +12 -14
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/mtime-cache.ts +186 -330
- package/src/plugins/pipeline.ts +111 -13
- package/src/plugins/registry.ts +59 -16
- package/src/plugins/surface-import.ts +121 -0
- package/src/plugins/types.ts +7 -9
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +45 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
- package/src/providers/openai/api-error-normalization.ts +270 -0
- package/src/providers/openai/chat-completions-provider.ts +37 -83
- package/src/providers/openai/responses-provider.ts +50 -46
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +10 -0
- package/src/providers/ratelimit.ts +10 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +10 -0
- package/src/providers/types.ts +22 -0
- package/src/providers/usage-tracking.ts +10 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
- package/src/runtime/access-request-helper.ts +18 -39
- package/src/runtime/actor-trust-resolver.ts +46 -19
- package/src/runtime/anchored-guardian.test.ts +109 -0
- package/src/runtime/anchored-guardian.ts +86 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +69 -8
- package/src/runtime/invite-redemption-service.ts +213 -187
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +85 -13
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
- package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +48 -57
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-query-routes.ts +72 -0
- package/src/runtime/routes/conversation-routes.ts +84 -85
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +16 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +83 -0
- package/src/runtime/routes/mcp-auth-routes.ts +171 -19
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +42 -56
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +13 -58
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +111 -40
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +254 -33
- package/src/subagent/types.ts +11 -4
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/executor.ts +4 -4
- package/src/tools/filesystem/search.ts +543 -0
- package/src/tools/registry.ts +38 -0
- package/src/tools/shared/filesystem/path-policy.ts +12 -5
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/tool-manifest.ts +3 -0
- package/src/tools/types.ts +131 -25
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/errors.ts +26 -1
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +19 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workflows/library.test.ts +140 -0
- package/src/workflows/library.ts +82 -28
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -93
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
- package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ConversationNoticeEvent } from "../api/events/conversation-notice.js";
|
|
2
|
+
|
|
3
|
+
export type PendingConversationNotice = Omit<
|
|
4
|
+
ConversationNoticeEvent,
|
|
5
|
+
"type" | "conversationId"
|
|
6
|
+
>;
|
|
7
|
+
|
|
8
|
+
const MAX_TRACKED_CONVERSATIONS = 256;
|
|
9
|
+
|
|
10
|
+
const pendingNotices = new Map<
|
|
11
|
+
string,
|
|
12
|
+
Map<string, PendingConversationNotice>
|
|
13
|
+
>();
|
|
14
|
+
|
|
15
|
+
function touchConversation(
|
|
16
|
+
conversationId: string,
|
|
17
|
+
): Map<string, PendingConversationNotice> {
|
|
18
|
+
const existing = pendingNotices.get(conversationId);
|
|
19
|
+
if (existing) {
|
|
20
|
+
pendingNotices.delete(conversationId);
|
|
21
|
+
pendingNotices.set(conversationId, existing);
|
|
22
|
+
return existing;
|
|
23
|
+
}
|
|
24
|
+
if (pendingNotices.size >= MAX_TRACKED_CONVERSATIONS) {
|
|
25
|
+
const oldest = pendingNotices.keys().next().value;
|
|
26
|
+
if (oldest !== undefined) pendingNotices.delete(oldest);
|
|
27
|
+
}
|
|
28
|
+
const next = new Map<string, PendingConversationNotice>();
|
|
29
|
+
pendingNotices.set(conversationId, next);
|
|
30
|
+
return next;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function queueConversationNotice(
|
|
34
|
+
conversationId: string,
|
|
35
|
+
key: string,
|
|
36
|
+
notice: PendingConversationNotice,
|
|
37
|
+
): void {
|
|
38
|
+
touchConversation(conversationId).set(key, notice);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function drainConversationNotices(
|
|
42
|
+
conversationId: string,
|
|
43
|
+
): ConversationNoticeEvent[] {
|
|
44
|
+
const notices = pendingNotices.get(conversationId);
|
|
45
|
+
if (!notices) return [];
|
|
46
|
+
pendingNotices.delete(conversationId);
|
|
47
|
+
return Array.from(notices.values(), (notice) => ({
|
|
48
|
+
type: "conversation_notice" as const,
|
|
49
|
+
conversationId,
|
|
50
|
+
...notice,
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function clearConversationNotices(conversationId: string): void {
|
|
55
|
+
pendingNotices.delete(conversationId);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function resetConversationNoticesForTests(): void {
|
|
59
|
+
pendingNotices.clear();
|
|
60
|
+
}
|
|
@@ -60,6 +60,21 @@ import { resolveVerificationSessionIntent } from "./verification-session-intent.
|
|
|
60
60
|
|
|
61
61
|
const log = getLogger("conversation-process");
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Daemon-injected subagent lifecycle notifications carry `subagentNotification`
|
|
65
|
+
* metadata. They are persisted into the parent conversation so the orchestrator
|
|
66
|
+
* wakes and reads the subagent's result, but they are internal scaffolding — the
|
|
67
|
+
* user sees subagent activity through the inline progress card, not a chat turn.
|
|
68
|
+
* Skip the `user_message_echo` broadcast for these so they never render as a live
|
|
69
|
+
* user bubble; the persisted row is filtered from the rendered transcript on the
|
|
70
|
+
* client.
|
|
71
|
+
*/
|
|
72
|
+
function isSubagentNotificationMessage(
|
|
73
|
+
metadata: Record<string, unknown> | undefined,
|
|
74
|
+
): boolean {
|
|
75
|
+
return metadata?.subagentNotification != null;
|
|
76
|
+
}
|
|
77
|
+
|
|
63
78
|
/** Format the result of a forced compaction into a user-facing message. */
|
|
64
79
|
export function formatCompactResult(result: ContextWindowResult): string {
|
|
65
80
|
const fmt = (n: number | undefined) => (n ?? 0).toLocaleString("en-US");
|
|
@@ -853,14 +868,16 @@ async function drainSingleMessage(
|
|
|
853
868
|
|
|
854
869
|
// Broadcast the user message to all hub subscribers so passive devices
|
|
855
870
|
// see the user turn before the assistant reply starts streaming.
|
|
856
|
-
next.
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
871
|
+
if (!isSubagentNotificationMessage(next.metadata)) {
|
|
872
|
+
next.onEvent({
|
|
873
|
+
type: "user_message_echo",
|
|
874
|
+
text: resolvedContent,
|
|
875
|
+
conversationId: conversation.conversationId,
|
|
876
|
+
messageId: userMessageId,
|
|
877
|
+
requestId: next.requestId,
|
|
878
|
+
clientMessageId: next.clientMessageId,
|
|
879
|
+
});
|
|
880
|
+
}
|
|
864
881
|
publishConversationMessagesChanged(conversation.conversationId);
|
|
865
882
|
|
|
866
883
|
// Set the active surface for the dequeued message so runAgentLoop can inject context
|
|
@@ -1204,14 +1221,16 @@ async function drainBatch(
|
|
|
1204
1221
|
|
|
1205
1222
|
// Broadcast the user message to all hub subscribers so passive devices
|
|
1206
1223
|
// see each batched user turn before the assistant reply starts streaming.
|
|
1207
|
-
qm.
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1224
|
+
if (!isSubagentNotificationMessage(qm.metadata)) {
|
|
1225
|
+
qm.onEvent({
|
|
1226
|
+
type: "user_message_echo",
|
|
1227
|
+
text: qmContent,
|
|
1228
|
+
conversationId: conversation.conversationId,
|
|
1229
|
+
messageId: lastUserMessageId,
|
|
1230
|
+
requestId: qm.requestId,
|
|
1231
|
+
clientMessageId: qm.clientMessageId,
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1215
1234
|
publishConversationMessagesChanged(conversation.conversationId);
|
|
1216
1235
|
|
|
1217
1236
|
// Persist succeeded. Update last-successful markers so a later tail
|
|
@@ -3,7 +3,10 @@ import { v4 as uuid } from "uuid";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
import { SurfaceActionSchema } from "../api/events/ui-surface-show.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CardSurfaceDataSchema,
|
|
8
|
+
FileUploadSurfaceDataSchema,
|
|
9
|
+
} from "../api/surfaces.js";
|
|
7
10
|
import { isActivationSession } from "../memory/activation-session-store.js";
|
|
8
11
|
import {
|
|
9
12
|
addAppConversationId,
|
|
@@ -56,6 +59,7 @@ import type {
|
|
|
56
59
|
ConfirmationSurfaceData,
|
|
57
60
|
CopyBlockSurfaceData,
|
|
58
61
|
DynamicPageSurfaceData,
|
|
62
|
+
FileUploadSurfaceData,
|
|
59
63
|
FormSurfaceData,
|
|
60
64
|
ListSurfaceData,
|
|
61
65
|
OAuthConnectSurfaceData,
|
|
@@ -583,12 +587,20 @@ function normalizeCardShowData(
|
|
|
583
587
|
(typeof input[k] === "string" &&
|
|
584
588
|
(input[k] as string).trim().length > 0),
|
|
585
589
|
);
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
590
|
+
try {
|
|
591
|
+
Sentry.withScope((scope) => {
|
|
592
|
+
scope.setLevel("info");
|
|
593
|
+
scope.setTag("card_normalization", "alias_recovery");
|
|
594
|
+
scope.setTag("target_field", "body");
|
|
595
|
+
scope.setContext("card_normalization", {
|
|
596
|
+
used_aliases: usedAliases,
|
|
597
|
+
candidate_count: candidates.length,
|
|
598
|
+
});
|
|
599
|
+
Sentry.captureMessage("card_normalization:alias_recovery:body");
|
|
600
|
+
});
|
|
601
|
+
} catch {
|
|
602
|
+
// Never let telemetry break card rendering.
|
|
603
|
+
}
|
|
592
604
|
}
|
|
593
605
|
}
|
|
594
606
|
for (const key of bodyAliasKeys) {
|
|
@@ -605,11 +617,16 @@ function normalizeCardShowData(
|
|
|
605
617
|
]);
|
|
606
618
|
if (aliased !== undefined) {
|
|
607
619
|
normalized.title = aliased;
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
620
|
+
try {
|
|
621
|
+
Sentry.withScope((scope) => {
|
|
622
|
+
scope.setLevel("info");
|
|
623
|
+
scope.setTag("card_normalization", "alias_recovery");
|
|
624
|
+
scope.setTag("target_field", "title");
|
|
625
|
+
Sentry.captureMessage("card_normalization:alias_recovery:title");
|
|
626
|
+
});
|
|
627
|
+
} catch {
|
|
628
|
+
// Never let telemetry break card rendering.
|
|
629
|
+
}
|
|
613
630
|
}
|
|
614
631
|
}
|
|
615
632
|
for (const key of titleAliasKeys) {
|
|
@@ -634,11 +651,16 @@ function normalizeCardShowData(
|
|
|
634
651
|
]);
|
|
635
652
|
if (aliased !== undefined) {
|
|
636
653
|
normalized.subtitle = aliased;
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
654
|
+
try {
|
|
655
|
+
Sentry.withScope((scope) => {
|
|
656
|
+
scope.setLevel("info");
|
|
657
|
+
scope.setTag("card_normalization", "alias_recovery");
|
|
658
|
+
scope.setTag("target_field", "subtitle");
|
|
659
|
+
Sentry.captureMessage("card_normalization:alias_recovery:subtitle");
|
|
660
|
+
});
|
|
661
|
+
} catch {
|
|
662
|
+
// Never let telemetry break card rendering.
|
|
663
|
+
}
|
|
642
664
|
}
|
|
643
665
|
}
|
|
644
666
|
for (const key of subtitleAliasKeys) {
|
|
@@ -688,12 +710,21 @@ function normalizeCardShowData(
|
|
|
688
710
|
{ droppedKeys },
|
|
689
711
|
"ui_show card data carried keys the card contract does not model; their content will not render",
|
|
690
712
|
);
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
713
|
+
try {
|
|
714
|
+
Sentry.withScope((scope) => {
|
|
715
|
+
scope.setLevel("warning");
|
|
716
|
+
scope.setTag("card_normalization", "dropped_keys");
|
|
717
|
+
scope.setContext("card_normalization", {
|
|
718
|
+
dropped_count: droppedKeys.length,
|
|
719
|
+
});
|
|
720
|
+
// Key names are model-controlled, so they ride in `extra`, which
|
|
721
|
+
// beforeSend redacts (it does not scrub `contexts`) — see instrument.ts.
|
|
722
|
+
scope.setExtra("dropped_keys", droppedKeys);
|
|
723
|
+
Sentry.captureMessage("card_normalization:dropped_keys");
|
|
724
|
+
});
|
|
725
|
+
} catch {
|
|
726
|
+
// Never let telemetry break card rendering.
|
|
727
|
+
}
|
|
697
728
|
}
|
|
698
729
|
const parsed = CardSurfaceDataSchema.safeParse(normalized);
|
|
699
730
|
if (parsed.success) {
|
|
@@ -829,6 +860,24 @@ function normalizeOAuthConnectShowData(
|
|
|
829
860
|
};
|
|
830
861
|
}
|
|
831
862
|
|
|
863
|
+
function normalizeFileUploadShowData(
|
|
864
|
+
rawData: Record<string, unknown>,
|
|
865
|
+
): FileUploadSurfaceData {
|
|
866
|
+
// Parse against the canonical schema so the surface carries the shape the
|
|
867
|
+
// renderer expects. The schema is tolerant (every field optional and coerced)
|
|
868
|
+
// and recovers the common malformed `acceptedTypes` shapes — a comma-joined or
|
|
869
|
+
// bare string — into the `string[]` the renderer requires.
|
|
870
|
+
const parsed = FileUploadSurfaceDataSchema.safeParse(rawData);
|
|
871
|
+
if (parsed.success) {
|
|
872
|
+
return parsed.data;
|
|
873
|
+
}
|
|
874
|
+
log.warn(
|
|
875
|
+
{ issues: parsed.error.issues },
|
|
876
|
+
"ui_show file_upload data failed FileUploadSurfaceDataSchema; rendering an empty file_upload surface",
|
|
877
|
+
);
|
|
878
|
+
return {};
|
|
879
|
+
}
|
|
880
|
+
|
|
832
881
|
function buildChoiceActions(data: ChoiceSurfaceData): Array<{
|
|
833
882
|
id: string;
|
|
834
883
|
label: string;
|
|
@@ -2895,7 +2944,9 @@ export async function surfaceProxyResolver(
|
|
|
2895
2944
|
? normalizeOAuthConnectShowData(rawData)
|
|
2896
2945
|
: surfaceType === "dynamic_page"
|
|
2897
2946
|
? normalizeDynamicPageShowData(input, rawData)
|
|
2898
|
-
:
|
|
2947
|
+
: surfaceType === "file_upload"
|
|
2948
|
+
? normalizeFileUploadShowData(rawData)
|
|
2949
|
+
: (rawData as SurfaceData);
|
|
2899
2950
|
// Parse actions through the schema instead of typecasting raw model output.
|
|
2900
2951
|
// The model may place actions inside `data` instead of the top-level
|
|
2901
2952
|
// `actions` param — recover them so they aren't silently dropped.
|
|
@@ -2912,18 +2963,27 @@ export async function surfaceProxyResolver(
|
|
|
2912
2963
|
if (result.success) {
|
|
2913
2964
|
valid.push(result.data);
|
|
2914
2965
|
} else {
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2966
|
+
try {
|
|
2967
|
+
Sentry.withScope((scope) => {
|
|
2968
|
+
scope.setLevel("warning");
|
|
2969
|
+
scope.setTag("card_normalization", "action_parse_failure");
|
|
2970
|
+
scope.setContext("card_normalization", {
|
|
2971
|
+
issue_paths: result.error.issues.map((i) => i.path.join(".")),
|
|
2972
|
+
});
|
|
2973
|
+
// raw object keys are model-controlled, so they ride in `extra`,
|
|
2974
|
+
// which beforeSend redacts (it does not scrub `contexts`) — see
|
|
2975
|
+
// instrument.ts.
|
|
2976
|
+
scope.setExtra(
|
|
2977
|
+
"raw_keys",
|
|
2922
2978
|
typeof raw === "object" && raw !== null
|
|
2923
2979
|
? Object.keys(raw)
|
|
2924
2980
|
: [typeof raw],
|
|
2925
|
-
|
|
2926
|
-
|
|
2981
|
+
);
|
|
2982
|
+
Sentry.captureMessage("card_normalization:action_parse_failure");
|
|
2983
|
+
});
|
|
2984
|
+
} catch {
|
|
2985
|
+
// Never let telemetry break card rendering.
|
|
2986
|
+
}
|
|
2927
2987
|
}
|
|
2928
2988
|
}
|
|
2929
2989
|
inputActions = valid.length > 0 ? valid : undefined;
|
|
@@ -2961,11 +3021,24 @@ export async function surfaceProxyResolver(
|
|
|
2961
3021
|
!hasTemplate &&
|
|
2962
3022
|
!hasActions
|
|
2963
3023
|
) {
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3024
|
+
try {
|
|
3025
|
+
Sentry.withScope((scope) => {
|
|
3026
|
+
scope.setLevel("warning");
|
|
3027
|
+
scope.setTag("card_normalization", "empty_card_rejected");
|
|
3028
|
+
scope.setContext("card_normalization", {
|
|
3029
|
+
surface_type: surfaceType,
|
|
3030
|
+
has_title: hasTitle,
|
|
3031
|
+
has_body: hasBody,
|
|
3032
|
+
has_subtitle: hasSubtitle,
|
|
3033
|
+
has_metadata: hasMetadata,
|
|
3034
|
+
has_template: hasTemplate,
|
|
3035
|
+
has_actions: hasActions,
|
|
3036
|
+
});
|
|
3037
|
+
Sentry.captureMessage("card_normalization:empty_card_rejected");
|
|
3038
|
+
});
|
|
3039
|
+
} catch {
|
|
3040
|
+
// Never let telemetry break card rendering.
|
|
3041
|
+
}
|
|
2969
3042
|
return {
|
|
2970
3043
|
content:
|
|
2971
3044
|
"Error: ui_show card requires content — provide `data.body`, a `template` (e.g. task_progress with steps), `data.metadata`, `data.subtitle`, a `title`, or `actions`. The surface was not displayed because it carried no renderable content. Resend ui_show with populated card content.",
|
|
@@ -17,12 +17,17 @@ import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
|
17
17
|
import { getBindingByConversation } from "../memory/external-conversation-store.js";
|
|
18
18
|
import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
19
19
|
import type { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
20
|
-
import { advisorEnabledForProfile } from "../plugins/defaults/advisor/advisor-gate.js";
|
|
21
20
|
import type { Message, ToolDefinition } from "../providers/types.js";
|
|
22
21
|
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
23
22
|
import { registerConversationSender } from "../tools/browser/browser-screencast.js";
|
|
24
23
|
import type { ToolExecutor } from "../tools/executor.js";
|
|
25
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
getMcpToolDefinitions,
|
|
26
|
+
getPluginToolDefinitions,
|
|
27
|
+
getTool,
|
|
28
|
+
getWorkspaceToolDefinitions,
|
|
29
|
+
getWorkspaceToolNames,
|
|
30
|
+
} from "../tools/registry.js";
|
|
26
31
|
import {
|
|
27
32
|
ACTIVITY_SKIP_SET,
|
|
28
33
|
injectActivityField,
|
|
@@ -41,6 +46,7 @@ import {
|
|
|
41
46
|
type ToolExecutionResult,
|
|
42
47
|
type ToolLifecycleEventHandler,
|
|
43
48
|
} from "../tools/types.js";
|
|
49
|
+
import { loadWorkspaceTools } from "../tools/workspace-tools/loader.js";
|
|
44
50
|
import {
|
|
45
51
|
resolveUsageAttribution,
|
|
46
52
|
type UsageAttributionSnapshot,
|
|
@@ -238,7 +244,10 @@ export function createToolExecutor(
|
|
|
238
244
|
});
|
|
239
245
|
}
|
|
240
246
|
},
|
|
241
|
-
isInteractive:
|
|
247
|
+
isInteractive:
|
|
248
|
+
ctx.currentTurnIsNonInteractive !== undefined
|
|
249
|
+
? !ctx.currentTurnIsNonInteractive
|
|
250
|
+
: !ctx.hasNoClient && !ctx.headlessLock,
|
|
242
251
|
proxyToolResolver: (
|
|
243
252
|
toolName: string,
|
|
244
253
|
proxyInput: Record<string, unknown>,
|
|
@@ -506,6 +515,7 @@ const PLATFORM_TOOL_NAMES = new Set(["request_system_permission"]);
|
|
|
506
515
|
*/
|
|
507
516
|
export const SUBAGENT_ONLY_TOOL_NAMES = new Set<string>([
|
|
508
517
|
"file_list",
|
|
518
|
+
"code_search",
|
|
509
519
|
"notify_parent",
|
|
510
520
|
]);
|
|
511
521
|
|
|
@@ -564,15 +574,6 @@ export function isToolActiveForContext(
|
|
|
564
574
|
return true;
|
|
565
575
|
}
|
|
566
576
|
}
|
|
567
|
-
if (name === "advisor") {
|
|
568
|
-
// Gated per chat-profile (`ProfileEntry.advisorEnabled`): when the resolved
|
|
569
|
-
// profile disables the advisor, omit the tool from the wire list so the
|
|
570
|
-
// model never sees a tool it can only no-op on. Resolves the profile the
|
|
571
|
-
// same way the advisor's execution-time guard does (the per-turn override,
|
|
572
|
-
// else the active profile). The wire list is fixed before PRE_MODEL_CALL
|
|
573
|
-
// hooks run, so later profile changes from hooks are not reflected here.
|
|
574
|
-
return advisorEnabledForProfile(ctx.currentTurnOverrideProfile ?? null);
|
|
575
|
-
}
|
|
576
577
|
if (UI_SURFACE_TOOL_NAMES.has(name)) {
|
|
577
578
|
if (
|
|
578
579
|
channelCapabilities?.channel === "slack" &&
|
|
@@ -650,11 +651,13 @@ export function isToolActiveForContext(
|
|
|
650
651
|
* allowedToolNames so newly-activated skill tools aren't blocked by
|
|
651
652
|
* the executor's stale gate.
|
|
652
653
|
*
|
|
653
|
-
* Core (non-MCP) tool definitions are captured at conversation
|
|
654
|
-
* reused on each turn. MCP tool definitions are
|
|
655
|
-
* registry on each turn so that tools registered
|
|
656
|
-
*
|
|
657
|
-
* requiring conversation disposal or app restart
|
|
654
|
+
* Core (non-MCP, non-workspace) tool definitions are captured at conversation
|
|
655
|
+
* creation and reused on each turn. MCP and workspace tool definitions are
|
|
656
|
+
* re-read from the global registry on each turn so that tools registered or
|
|
657
|
+
* changed after conversation creation are automatically picked up without
|
|
658
|
+
* requiring conversation disposal or app restart — MCP via `vellum mcp
|
|
659
|
+
* reload`, workspace tools via edits under `<workspaceDir>/tools/` that the
|
|
660
|
+
* per-turn reconcile (kicked below) folds into the registry.
|
|
658
661
|
*/
|
|
659
662
|
export function createResolveToolsCallback(
|
|
660
663
|
toolDefs: ToolDefinition[],
|
|
@@ -662,16 +665,30 @@ export function createResolveToolsCallback(
|
|
|
662
665
|
): ((history: Message[]) => ToolDefinition[]) | undefined {
|
|
663
666
|
if (toolDefs.length === 0) return undefined;
|
|
664
667
|
|
|
665
|
-
// Separate the initial tool defs into core (stable) and
|
|
666
|
-
// We keep core tools from the snapshot
|
|
668
|
+
// Separate the initial tool defs into core (stable) and the dynamic
|
|
669
|
+
// categories (MCP, workspace, plugin). We keep core tools from the snapshot
|
|
670
|
+
// and re-read the dynamic categories from the registry each turn. They differ
|
|
671
|
+
// downstream: plugin tools flow through the same context filter + subagent
|
|
672
|
+
// allowlist as core, while MCP and workspace tools are added raw.
|
|
667
673
|
const initialMcpDefs = getMcpToolDefinitions();
|
|
674
|
+
const initialPluginDefs = getPluginToolDefinitions();
|
|
668
675
|
const initialMcpNames = new Set(initialMcpDefs.map((d) => d.name));
|
|
669
|
-
const
|
|
676
|
+
const initialWorkspaceNames = new Set(getWorkspaceToolNames());
|
|
677
|
+
const initialPluginNames = new Set(initialPluginDefs.map((d) => d.name));
|
|
678
|
+
const coreToolDefs = toolDefs.filter(
|
|
679
|
+
(d) =>
|
|
680
|
+
!initialMcpNames.has(d.name) &&
|
|
681
|
+
!initialWorkspaceNames.has(d.name) &&
|
|
682
|
+
!initialPluginNames.has(d.name),
|
|
683
|
+
);
|
|
670
684
|
log.debug(
|
|
671
685
|
{
|
|
672
686
|
coreCount: coreToolDefs.length,
|
|
673
687
|
mcpCount: initialMcpDefs.length,
|
|
674
688
|
mcpTools: initialMcpDefs.map((d) => d.name),
|
|
689
|
+
workspaceCount: initialWorkspaceNames.size,
|
|
690
|
+
pluginCount: initialPluginDefs.length,
|
|
691
|
+
pluginTools: initialPluginDefs.map((d) => d.name),
|
|
675
692
|
},
|
|
676
693
|
"Conversation tool resolver initialized",
|
|
677
694
|
);
|
|
@@ -685,11 +702,25 @@ export function createResolveToolsCallback(
|
|
|
685
702
|
return [];
|
|
686
703
|
}
|
|
687
704
|
|
|
688
|
-
//
|
|
689
|
-
//
|
|
705
|
+
// Reconcile workspace tool overrides under `<workspaceDir>/tools/` into
|
|
706
|
+
// the registry, then re-read them below — the on-read replacement for a
|
|
707
|
+
// filesystem watcher. Fire-and-forget: the reconcile is idempotent,
|
|
708
|
+
// mtime-cached (a no-op costs one readdir + a stat per file) and
|
|
709
|
+
// serialized, so the registry settles for a subsequent turn to read.
|
|
710
|
+
void loadWorkspaceTools();
|
|
711
|
+
|
|
712
|
+
// Re-read plugin tool definitions from the registry each turn so a plugin
|
|
713
|
+
// installed/removed at runtime (activated by the per-turn scan in
|
|
714
|
+
// `plugins/mtime-cache.ts`) is picked up without recreating the
|
|
715
|
+
// conversation. Plugin tools share core's context filter + allowlist path,
|
|
716
|
+
// so combine them with the core snapshot before filtering.
|
|
717
|
+
const currentPluginDefs = getPluginToolDefinitions();
|
|
718
|
+
|
|
719
|
+
// Filter core + plugin tools based on current conversation context so that
|
|
720
|
+
// tools irrelevant to this turn (e.g. UI tools when no client is connected)
|
|
690
721
|
// are omitted from the definitions sent to the provider.
|
|
691
|
-
const filteredCoreDefs = coreToolDefs.filter(
|
|
692
|
-
isToolActiveForContext(d.name, ctx),
|
|
722
|
+
const filteredCoreDefs = [...coreToolDefs, ...currentPluginDefs].filter(
|
|
723
|
+
(d) => isToolActiveForContext(d.name, ctx),
|
|
693
724
|
);
|
|
694
725
|
|
|
695
726
|
// When the conversation is acting as a subagent, restrict core tools to
|
|
@@ -705,24 +736,34 @@ export function createResolveToolsCallback(
|
|
|
705
736
|
? filteredCoreDefs.filter((d) => wireAllowlist.has(d.name))
|
|
706
737
|
: filteredCoreDefs;
|
|
707
738
|
|
|
708
|
-
// Re-read MCP tool definitions from the registry each turn
|
|
709
|
-
// automatically pick up tools added/removed by `vellum
|
|
739
|
+
// Re-read MCP and workspace tool definitions from the registry each turn
|
|
740
|
+
// so conversations automatically pick up tools added/removed by `vellum
|
|
741
|
+
// mcp reload` and workspace-tool edits reconciled from disk, without
|
|
742
|
+
// recreating the conversation.
|
|
710
743
|
const currentMcpDefs = getMcpToolDefinitions();
|
|
744
|
+
const currentWorkspaceDefs = getWorkspaceToolDefinitions();
|
|
711
745
|
log.debug(
|
|
712
746
|
{
|
|
713
747
|
coreCount: scopedCoreDefs.length,
|
|
714
748
|
mcpCount: currentMcpDefs.length,
|
|
715
749
|
mcpTools: currentMcpDefs.map((d) => d.name),
|
|
750
|
+
workspaceCount: currentWorkspaceDefs.length,
|
|
751
|
+
workspaceTools: currentWorkspaceDefs.map((d) => d.name),
|
|
716
752
|
},
|
|
717
|
-
"MCP tools resolved for turn",
|
|
753
|
+
"MCP and workspace tools resolved for turn",
|
|
718
754
|
);
|
|
719
755
|
const scopedMcpDefs = wireAllowlist
|
|
720
756
|
? currentMcpDefs.filter((d) => wireAllowlist.has(d.name))
|
|
721
757
|
: currentMcpDefs;
|
|
758
|
+
const scopedWorkspaceDefs = wireAllowlist
|
|
759
|
+
? currentWorkspaceDefs.filter((d) => wireAllowlist.has(d.name))
|
|
760
|
+
: currentWorkspaceDefs;
|
|
722
761
|
const excluded = new Set(getConfig().tools.exclude);
|
|
723
|
-
const allBaseDefs = [
|
|
724
|
-
|
|
725
|
-
|
|
762
|
+
const allBaseDefs = [
|
|
763
|
+
...scopedCoreDefs,
|
|
764
|
+
...scopedWorkspaceDefs,
|
|
765
|
+
...scopedMcpDefs,
|
|
766
|
+
].filter((d) => !excluded.has(d.name));
|
|
726
767
|
|
|
727
768
|
const effectivePreactivated = [
|
|
728
769
|
...DEFAULT_PREACTIVATED_SKILL_IDS,
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
getMessages,
|
|
51
51
|
resolveOverrideProfile,
|
|
52
52
|
setConversationHistoryStrippedAt,
|
|
53
|
+
setConversationProcessingStartedAt,
|
|
53
54
|
} from "../memory/conversation-crud.js";
|
|
54
55
|
import { getResolvedConversationDirPath } from "../memory/conversation-directories.js";
|
|
55
56
|
import { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
|
|
@@ -91,7 +92,7 @@ import {
|
|
|
91
92
|
isActivationMomentParam,
|
|
92
93
|
} from "../telemetry/activation-funnel.js";
|
|
93
94
|
import { ToolExecutor } from "../tools/executor.js";
|
|
94
|
-
import { getAllToolDefinitions } from "../tools/registry.js";
|
|
95
|
+
import { getAllToolDefinitions, getTool } from "../tools/registry.js";
|
|
95
96
|
import type { ToolLifecycleEvent } from "../tools/types.js";
|
|
96
97
|
import type { OnboardingContext } from "../types/onboarding-context.js";
|
|
97
98
|
import type { AbortReason } from "../util/abort-reasons.js";
|
|
@@ -242,6 +243,14 @@ export interface ConversationConstructorOptions {
|
|
|
242
243
|
speedOverride?: Speed;
|
|
243
244
|
cacheTtl?: "5m" | "1h";
|
|
244
245
|
modelOverride?: string;
|
|
246
|
+
/**
|
|
247
|
+
* Give this conversation's LLM calls provider-native (server-side) web
|
|
248
|
+
* search when the resolved provider supports it (see
|
|
249
|
+
* {@link AgentLoopConfig.enableNativeWebSearch}). Set by the subagent manager
|
|
250
|
+
* for the tool-less advisor consult so it can ground guidance with live web
|
|
251
|
+
* access; non-native providers get nothing. Defaults to false.
|
|
252
|
+
*/
|
|
253
|
+
enableNativeWebSearch?: boolean;
|
|
245
254
|
}
|
|
246
255
|
|
|
247
256
|
export class Conversation {
|
|
@@ -376,6 +385,7 @@ export class Conversation {
|
|
|
376
385
|
*/
|
|
377
386
|
wakePersonaOverride?: SystemPromptPersonaOverride;
|
|
378
387
|
/** @internal */ currentTurnOverrideProfile?: string;
|
|
388
|
+
/** @internal */ currentTurnIsNonInteractive?: boolean;
|
|
379
389
|
/** @internal */ authContext?: AuthContext;
|
|
380
390
|
/** @internal */ currentTurnAuthContext?: AuthContext;
|
|
381
391
|
/** @internal */ currentTurnSourceActorPrincipalId?: string;
|
|
@@ -590,6 +600,7 @@ export class Conversation {
|
|
|
590
600
|
options?: ConversationConstructorOptions,
|
|
591
601
|
) {
|
|
592
602
|
const { maxTokens, speedOverride, cacheTtl, modelOverride } = options ?? {};
|
|
603
|
+
const enableNativeWebSearch = options?.enableNativeWebSearch ?? false;
|
|
593
604
|
this.conversationId = conversationId;
|
|
594
605
|
this.systemPrompt = systemPrompt;
|
|
595
606
|
this.provider = provider;
|
|
@@ -688,6 +699,7 @@ export class Conversation {
|
|
|
688
699
|
? { speed: resolvedSpeed }
|
|
689
700
|
: {}),
|
|
690
701
|
...(cacheTtl ? { cacheTtl } : {}),
|
|
702
|
+
...(enableNativeWebSearch ? { enableNativeWebSearch: true } : {}),
|
|
691
703
|
};
|
|
692
704
|
if (configuredMaxTokens !== undefined) {
|
|
693
705
|
agentLoopConfig.maxTokens = configuredMaxTokens;
|
|
@@ -701,6 +713,9 @@ export class Conversation {
|
|
|
701
713
|
tools: toolDefs.length > 0 ? toolDefs : undefined,
|
|
702
714
|
toolExecutor: toolDefs.length > 0 ? toolExecutor : undefined,
|
|
703
715
|
resolveTools,
|
|
716
|
+
// A tool the registry marks exclusive (e.g. `advisor`) runs alone in its
|
|
717
|
+
// turn; the loop defers any sibling calls until the next turn.
|
|
718
|
+
isExclusiveTool: (name) => getTool(name)?.exclusive === true,
|
|
704
719
|
resolveConversationDir: () => {
|
|
705
720
|
const conv = getConversation(this.conversationId);
|
|
706
721
|
if (!conv) return null;
|
|
@@ -1335,6 +1350,13 @@ export class Conversation {
|
|
|
1335
1350
|
setProcessing(value: boolean): void {
|
|
1336
1351
|
const wasProcessing = this._processing;
|
|
1337
1352
|
this._processing = value;
|
|
1353
|
+
// Persist the cross-process source of truth so out-of-process callers
|
|
1354
|
+
// (retrospective CLI, future detached workers) can detect mid-turn state
|
|
1355
|
+
// by reading the conversations row directly.
|
|
1356
|
+
setConversationProcessingStartedAt(
|
|
1357
|
+
this.conversationId,
|
|
1358
|
+
value ? Date.now() : null,
|
|
1359
|
+
);
|
|
1338
1360
|
if (wasProcessing && !value) {
|
|
1339
1361
|
void publishSyncInvalidation([
|
|
1340
1362
|
conversationMetadataSyncTag(this.conversationId),
|
|
@@ -24,6 +24,12 @@ export const DISK_PRESSURE_CLEAR_THRESHOLD_PERCENT = 90;
|
|
|
24
24
|
// clears the warning state, which discards the banner's (state-scoped) dismissal
|
|
25
25
|
// so it re-appears the moment usage ticks back up.
|
|
26
26
|
export const DISK_PRESSURE_WARNING_CLEAR_THRESHOLD_PERCENT = 77;
|
|
27
|
+
// Absolute free-space floor (MiB). Regardless of usage percentage, never enter
|
|
28
|
+
// the warning or critical state while at least this much space remains free. A
|
|
29
|
+
// high usage percentage on a large disk can still leave many gigabytes
|
|
30
|
+
// available, where locking is pointless. Small volumes (where a high percentage
|
|
31
|
+
// genuinely means near-full) drop below the floor and remain protected.
|
|
32
|
+
export const DISK_PRESSURE_MIN_FREE_FLOOR_MB = 2048;
|
|
27
33
|
export const DISK_PRESSURE_CHECK_INTERVAL_MS = 60_000;
|
|
28
34
|
export const DISK_PRESSURE_OVERRIDE_CONFIRMATION = "I understand the risks";
|
|
29
35
|
export const DISK_PRESSURE_BLOCKED_CAPABILITIES = [
|
|
@@ -219,7 +225,10 @@ export function evaluateDiskPressureNow(): DiskPressureStatus {
|
|
|
219
225
|
const criticalThreshold = state.status.locked
|
|
220
226
|
? DISK_PRESSURE_CLEAR_THRESHOLD_PERCENT
|
|
221
227
|
: DISK_PRESSURE_THRESHOLD_PERCENT;
|
|
222
|
-
|
|
228
|
+
// Absolute free-space floor overrides the percentage thresholds: while ample
|
|
229
|
+
// space remains free, report "ok" no matter how full the volume is by percent.
|
|
230
|
+
const hasAmpleFreeSpace = usageInfo.freeMb >= DISK_PRESSURE_MIN_FREE_FLOOR_MB;
|
|
231
|
+
const isCritical = !hasAmpleFreeSpace && usagePercent >= criticalThreshold;
|
|
223
232
|
// Mirror the critical deadband for the warning band: once in an active
|
|
224
233
|
// pressure state (warning or critical), hold warning until usage clears the
|
|
225
234
|
// lower warning-clear threshold. Treating "critical" as active here matters
|
|
@@ -235,7 +244,8 @@ export function evaluateDiskPressureNow(): DiskPressureStatus {
|
|
|
235
244
|
const warningThreshold = inActivePressureState
|
|
236
245
|
? DISK_PRESSURE_WARNING_CLEAR_THRESHOLD_PERCENT
|
|
237
246
|
: DISK_PRESSURE_WARNING_THRESHOLD_PERCENT;
|
|
238
|
-
const isWarning =
|
|
247
|
+
const isWarning =
|
|
248
|
+
!hasAmpleFreeSpace && !isCritical && usagePercent >= warningThreshold;
|
|
239
249
|
const lastCheckedAt = new Date().toISOString();
|
|
240
250
|
|
|
241
251
|
if (!isCritical && !isWarning) {
|