@vellumai/assistant 0.7.0 → 0.7.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 +6 -7
- package/Dockerfile +1 -0
- package/README.md +2 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +79 -139
- package/bun.lock +3 -0
- package/docs/architecture/security.md +18 -16
- package/knip.json +1 -0
- package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +1 -5
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -5
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -16
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +1 -9
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +12 -12
- package/node_modules/@vellumai/slack-text/bun.lock +24 -0
- package/node_modules/@vellumai/slack-text/package.json +18 -0
- package/node_modules/@vellumai/slack-text/src/index.test.ts +153 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +235 -0
- package/node_modules/@vellumai/slack-text/tsconfig.json +20 -0
- package/openapi.yaml +294 -107
- package/package.json +4 -2
- package/scripts/generate-openapi.ts +16 -111
- package/src/__tests__/agent-wake-override-profile.test.ts +23 -1
- package/src/__tests__/anthropic-provider.test.ts +56 -13
- package/src/__tests__/app-conversation-ids-backfill.test.ts +278 -0
- package/src/__tests__/app-conversation-ids.test.ts +151 -0
- package/src/__tests__/approval-cascade.test.ts +0 -15
- package/src/__tests__/approval-routes-http.test.ts +6 -17
- package/src/__tests__/assistant-event-hub.test.ts +126 -77
- package/src/__tests__/assistant-event.test.ts +0 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +37 -15
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -29
- package/src/__tests__/background-shell-host-bash.test.ts +34 -43
- package/src/__tests__/call-controller.test.ts +1 -1
- package/src/__tests__/call-site-routing-provider.test.ts +193 -0
- package/src/__tests__/channel-approval-routes.test.ts +10 -296
- package/src/__tests__/channel-approvals.test.ts +25 -17
- package/src/__tests__/channel-guardian.test.ts +100 -146
- package/src/__tests__/checker.test.ts +20 -34
- package/src/__tests__/compact-event-conversation-id-guard.test.ts +50 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/config-schema.test.ts +6 -48
- package/src/__tests__/config-watcher.test.ts +12 -0
- package/src/__tests__/connection-policy.test.ts +1 -52
- package/src/__tests__/contacts-write.test.ts +2 -64
- package/src/__tests__/context-image-dimensions.test.ts +1 -1
- package/src/__tests__/context-search-memory-source.test.ts +120 -1
- package/src/__tests__/context-search-memory-v2-source.test.ts +383 -0
- package/src/__tests__/context-search-pkb-source.test.ts +49 -0
- package/src/__tests__/context-search-workspace-source.test.ts +9 -22
- package/src/__tests__/context-window-manager.test.ts +46 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +102 -29
- package/src/__tests__/conversation-agent-loop.test.ts +980 -13
- package/src/__tests__/conversation-analysis-routes.test.ts +12 -10
- package/src/__tests__/conversation-attention-telegram.test.ts +11 -3
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -291
- package/src/__tests__/conversation-history-web-search.test.ts +4 -3
- package/src/__tests__/conversation-inference-profile-route.test.ts +12 -23
- package/src/__tests__/conversation-lifecycle.test.ts +4 -4
- package/src/__tests__/conversation-process-callsite.test.ts +79 -2
- package/src/__tests__/conversation-queue.test.ts +3 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -161
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -32
- package/src/__tests__/conversation-routes-slash-commands.test.ts +75 -66
- package/src/__tests__/conversation-runtime-assembly.test.ts +257 -3
- package/src/__tests__/conversation-slash-commands.test.ts +24 -4
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-starter-routes.test.ts +79 -2
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -5
- package/src/__tests__/conversation-surfaces-standalone.test.ts +18 -14
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +8 -46
- package/src/__tests__/conversation-usage.test.ts +253 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -39
- package/src/__tests__/credential-health-service.test.ts +68 -0
- package/src/__tests__/credential-security-e2e.test.ts +4 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -5
- package/src/__tests__/credential-token-resolver.test.ts +180 -0
- package/src/__tests__/cu-unified-flow.test.ts +33 -16
- package/src/__tests__/daemon-assistant-events.test.ts +34 -21
- package/src/__tests__/daemon-credential-client.test.ts +4 -1
- package/src/__tests__/db-connection-isolation.test.ts +125 -0
- package/src/__tests__/db-migration-rollback.test.ts +101 -0
- package/src/__tests__/db-slack-compaction-watermark-migration.test.ts +169 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +7 -80
- package/src/__tests__/document-conversations.test.ts +332 -0
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +2 -2
- package/src/__tests__/emit-event-signal.test.ts +4 -6
- package/src/__tests__/events-client-registration.test.ts +193 -49
- package/src/__tests__/filing-service.test.ts +58 -7
- package/src/__tests__/first-greeting.test.ts +156 -150
- package/src/__tests__/fixtures/mock-chrome-extension.ts +108 -66
- package/src/__tests__/get-skill-detail-audit.test.ts +3 -8
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -1
- package/src/__tests__/guardian-grant-minting.test.ts +7 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +7 -2
- package/src/__tests__/guardian-routing-state.test.ts +1 -1
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +32 -11
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -83
- package/src/__tests__/headless-browser-mode.test.ts +4 -9
- package/src/__tests__/headless-browser-navigate.test.ts +21 -20
- package/src/__tests__/heartbeat-service.test.ts +289 -7
- package/src/__tests__/helpers/channel-test-adapter.ts +2 -2
- package/src/__tests__/helpers/create-guardian-binding.ts +91 -0
- package/src/__tests__/host-bash-proxy.test.ts +46 -122
- package/src/__tests__/host-browser-e2e-cloud.test.ts +36 -497
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +26 -96
- package/src/__tests__/host-browser-proxy.test.ts +111 -185
- package/src/__tests__/host-browser-routes.test.ts +45 -75
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +26 -30
- package/src/__tests__/host-cu-proxy.test.ts +56 -111
- package/src/__tests__/host-file-proxy.test.ts +44 -98
- package/src/__tests__/host-file-read-tool.test.ts +42 -21
- package/src/__tests__/host-shell-tool.test.ts +33 -68
- package/src/__tests__/host-transfer-pending-interactions.test.ts +2 -18
- package/src/__tests__/host-transfer-proxy.test.ts +43 -53
- package/src/__tests__/http-user-message-parity.test.ts +0 -6
- package/src/__tests__/inbound-slack-persistence.test.ts +31 -0
- package/src/__tests__/injector-chain.test.ts +10 -5
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +124 -0
- package/src/__tests__/inline-command-runner.test.ts +0 -66
- package/src/__tests__/inline-skill-load-permissions.test.ts +0 -2
- package/src/__tests__/install-skill-routing.test.ts +1 -13
- package/src/__tests__/llm-callsite-catalog.test.ts +34 -0
- package/src/__tests__/llm-catalog-parity.test.ts +90 -0
- package/src/__tests__/llm-context-resolution.test.ts +180 -0
- package/src/__tests__/llm-resolver.test.ts +80 -12
- package/src/__tests__/llm-usage-store.test.ts +269 -4
- package/src/__tests__/log-export-routes.test.ts +89 -0
- package/src/__tests__/managed-profile-guard.test.ts +225 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -10
- package/src/__tests__/manual-token-reconciliation.test.ts +334 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +95 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +197 -291
- package/src/__tests__/migration-export-http.test.ts +33 -26
- package/src/__tests__/migration-export-streaming.test.ts +18 -10
- package/src/__tests__/migration-export-to-gcs.test.ts +49 -9
- package/src/__tests__/migration-import-commit-http.test.ts +66 -21
- package/src/__tests__/migration-import-from-gcs.test.ts +50 -9
- package/src/__tests__/migration-import-from-url.test.ts +20 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +95 -95
- package/src/__tests__/migration-parity-persistence.test.ts +62 -25
- package/src/__tests__/migration-transport.test.ts +115 -23
- package/src/__tests__/migration-validate-http.test.ts +105 -80
- package/src/__tests__/migration-wizard.test.ts +133 -27
- package/src/__tests__/non-member-access-request.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +19 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +21 -12
- package/src/__tests__/prechat-onboarding-contract.test.ts +31 -7
- package/src/__tests__/pricing.test.ts +68 -4
- package/src/__tests__/process-message-background-slack.test.ts +331 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +153 -17
- package/src/__tests__/provider-send-message-override-profile.test.ts +50 -0
- package/src/__tests__/provider-usage-tracking.test.ts +208 -0
- package/src/__tests__/reaction-persistence.test.ts +9 -6
- package/src/__tests__/rebind-secrets-screen.test.ts +53 -16
- package/src/__tests__/recording-handler.test.ts +64 -81
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +4 -3
- package/src/__tests__/relay-server.test.ts +18 -13
- package/src/__tests__/require-fresh-approval.test.ts +13 -22
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
- package/src/__tests__/runtime-events-sse.test.ts +3 -12
- package/src/__tests__/search-skills-unified.test.ts +9 -15
- package/src/__tests__/secret-ingress-cli.test.ts +2 -5
- package/src/__tests__/secret-ingress-http.test.ts +0 -4
- package/src/__tests__/secret-onetime-send.test.ts +4 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +24 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +42 -47
- package/src/__tests__/secret-response-routing.test.ts +29 -15
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -1
- package/src/__tests__/secret-scanner.test.ts +2 -545
- package/src/__tests__/send-endpoint-busy.test.ts +9 -24
- package/src/__tests__/settings-routes.test.ts +1 -1
- package/src/__tests__/shell-credential-ref.test.ts +0 -8
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -56
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -11
- package/src/__tests__/skill-tool-factory.test.ts +97 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -30
- package/src/__tests__/skills-files-catalog-fallback.test.ts +11 -17
- package/src/__tests__/slack-inbound-verification.test.ts +1 -62
- package/src/__tests__/subagent-fork-notifications.test.ts +57 -47
- package/src/__tests__/subagent-manager-notify.test.ts +70 -70
- package/src/__tests__/subagent-notify-parent.test.ts +80 -83
- package/src/__tests__/system-prompt.test.ts +115 -13
- package/src/__tests__/terminal-tools.test.ts +0 -89
- package/src/__tests__/thread-backfill.test.ts +945 -31
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -36
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -6
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -16
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +9 -19
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -7
- package/src/__tests__/tool-executor.test.ts +12 -19
- package/src/__tests__/tool-metrics-listener.test.ts +0 -35
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/tool-trace-listener.test.ts +0 -17
- package/src/__tests__/transfer-progress-screen.test.ts +63 -26
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -149
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -4
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +16 -5
- package/src/__tests__/usage-attribution.test.ts +247 -0
- package/src/__tests__/usage-cli.test.ts +143 -0
- package/src/__tests__/usage-grouped-buckets.test.ts +155 -0
- package/src/__tests__/usage-routes.test.ts +150 -0
- package/src/__tests__/validation-results-screen.test.ts +39 -16
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +12 -3
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +49 -137
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -7
- package/src/__tests__/voice-session-bridge.test.ts +5 -5
- package/src/__tests__/workspace-migration-062-drop-memory-v2-edges-json.test.ts +103 -0
- package/src/__tests__/workspace-migration-063-release-notes-dynamic-model-context.test.ts +77 -0
- package/src/__tests__/workspace-migration-064-unwind-main-agent-opus-seed.test.ts +225 -0
- package/src/__tests__/workspace-migration-memory-v2-init.test.ts +8 -30
- package/src/acp/index.ts +0 -15
- package/src/acp/session-manager.ts +37 -34
- package/src/agent/loop.ts +16 -1
- package/src/approvals/AGENTS.md +4 -0
- package/src/approvals/__tests__/guardian-feed-event.test.ts +10 -3
- package/src/approvals/guardian-request-resolvers.ts +10 -2
- package/src/backup/__tests__/backup-worker.test.ts +36 -8
- package/src/backup/__tests__/paths.test.ts +2 -2
- package/src/backup/__tests__/restore.test.ts +45 -28
- package/src/backup/backup-worker.ts +36 -2
- package/src/backup/paths.ts +9 -6
- package/src/browser-session/events.ts +0 -9
- package/src/calls/call-store.ts +1 -34
- package/src/calls/guardian-question-copy.ts +0 -108
- package/src/calls/relay-server.ts +0 -24
- package/src/calls/twilio-rest.ts +0 -38
- package/src/calls/twilio-routes.ts +1 -1
- package/src/calls/voice-session-bridge.ts +7 -38
- package/src/channels/types.ts +1 -36
- package/src/cli/commands/__tests__/cache.test.ts +152 -5
- package/src/cli/commands/__tests__/memory-v2.test.ts +14 -28
- package/src/cli/commands/__tests__/trust.test.ts +21 -387
- package/src/cli/commands/backup.ts +4 -4
- package/src/cli/commands/cache-fs.ts +8 -0
- package/src/cli/commands/cache.ts +153 -82
- package/src/cli/commands/clients.ts +63 -5
- package/src/cli/commands/completions.ts +3 -3
- package/src/cli/commands/contacts.ts +231 -76
- package/src/cli/commands/keys.ts +4 -1
- package/src/cli/commands/memory-v2.ts +24 -52
- package/src/cli/commands/oauth/shared.ts +2 -29
- package/src/cli/commands/pending.ts +102 -0
- package/src/cli/commands/skills.ts +77 -35
- package/src/cli/commands/trust.ts +70 -430
- package/src/cli/commands/usage.ts +25 -16
- package/src/cli/lib/daemon-credential-client.ts +14 -0
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +0 -21
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +14 -4
- package/src/config/env-registry.ts +12 -2
- package/src/config/env.ts +3 -14
- package/src/config/feature-flag-registry.json +30 -30
- package/src/config/llm-callsite-catalog.ts +12 -0
- package/src/config/llm-context-resolution.ts +80 -0
- package/src/config/llm-resolver.ts +58 -22
- package/src/config/loader.ts +3 -3
- package/src/config/schema.ts +2 -158
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +271 -0
- package/src/config/schemas/calls.ts +5 -5
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/ingress.ts +1 -1
- package/src/config/schemas/llm.ts +31 -3
- package/src/config/schemas/memory-retrieval.ts +2 -2
- package/src/config/schemas/memory-v2.ts +9 -0
- package/src/config/schemas/security.ts +1 -42
- package/src/config/schemas/services.ts +6 -6
- package/src/config/schemas/skills.ts +5 -5
- package/src/config/schemas/tts.ts +1 -1
- package/src/config/seed-inference-profiles.ts +117 -0
- package/src/config/skills.ts +0 -90
- package/src/config/types.ts +3 -6
- package/src/contacts/contact-store.ts +0 -17
- package/src/contacts/contacts-write.ts +1 -105
- package/src/context/window-manager.ts +44 -5
- package/src/credential-execution/process-manager.ts +34 -10
- package/src/credential-health/credential-health-service.ts +21 -16
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +75 -82
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -9
- package/src/daemon/connection-policy.ts +1 -26
- package/src/daemon/conversation-agent-loop-handlers.ts +53 -4
- package/src/daemon/conversation-agent-loop.ts +277 -36
- package/src/daemon/conversation-history.ts +8 -8
- package/src/daemon/conversation-launch.ts +20 -135
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-process.ts +83 -163
- package/src/daemon/conversation-runtime-assembly.ts +219 -76
- package/src/daemon/conversation-slash.ts +47 -5
- package/src/daemon/conversation-store.ts +7 -31
- package/src/daemon/conversation-surfaces.ts +22 -28
- package/src/daemon/conversation-tool-setup.ts +3 -33
- package/src/daemon/conversation-usage.ts +36 -0
- package/src/daemon/conversation.ts +117 -233
- package/src/daemon/daemon-control.ts +3 -71
- package/src/daemon/daemon-skill-host.ts +8 -11
- package/src/daemon/dictation-profile-store.ts +2 -26
- package/src/daemon/first-greeting.ts +44 -156
- package/src/daemon/handlers/config-channels.ts +12 -12
- package/src/daemon/handlers/config-ingress.ts +4 -165
- package/src/daemon/handlers/config-model.ts +1 -1
- package/src/daemon/handlers/config-voice.ts +0 -42
- package/src/daemon/handlers/conversations.ts +11 -190
- package/src/daemon/handlers/recording.ts +26 -158
- package/src/daemon/handlers/shared.ts +23 -71
- package/src/daemon/handlers/skills.ts +42 -93
- package/src/daemon/host-bash-proxy.ts +67 -45
- package/src/daemon/host-browser-proxy.ts +65 -27
- package/src/daemon/host-cu-proxy.ts +40 -39
- package/src/daemon/host-file-proxy.ts +58 -37
- package/src/daemon/host-transfer-proxy.ts +84 -46
- package/src/daemon/lifecycle.ts +49 -15
- package/src/daemon/message-types/conversations.ts +7 -0
- package/src/daemon/message-types/host-bash.ts +1 -0
- package/src/daemon/message-types/host-cu.ts +1 -0
- package/src/daemon/message-types/host-file.ts +1 -0
- package/src/daemon/message-types/host-transfer.ts +1 -0
- package/src/daemon/message-types/messages.ts +10 -9
- package/src/daemon/message-types/workspace.ts +1 -1
- package/src/daemon/process-message.ts +102 -239
- package/src/daemon/server.ts +13 -462
- package/src/daemon/shutdown-handlers.ts +2 -2
- package/src/daemon/tool-side-effects.ts +125 -107
- package/src/daemon/trust-context.ts +13 -0
- package/src/daemon/wake-target-adapter.ts +4 -9
- package/src/events/domain-events.ts +0 -8
- package/src/events/tool-audit-listener.ts +3 -1
- package/src/events/tool-domain-event-publisher.ts +0 -10
- package/src/events/tool-metrics-listener.ts +0 -17
- package/src/events/tool-trace-listener.ts +0 -14
- package/src/filing/filing-service.ts +13 -1
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +6 -2
- package/src/heartbeat/heartbeat-service.ts +23 -5
- package/src/home/__tests__/feed-writer.test.ts +0 -4
- package/src/home/__tests__/relationship-state-writer.test.ts +30 -0
- package/src/home/feed-writer.ts +1 -2
- package/src/home/relationship-state-writer.ts +16 -3
- package/src/ipc/__tests__/browser-ipc.test.ts +2 -12
- package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -1
- package/src/ipc/assistant-server.ts +3 -10
- package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +39 -20
- package/src/ipc/routes/route-adapter.ts +1 -1
- package/src/ipc/routes/trust-rules.test.ts +0 -95
- package/src/ipc/skill-ipc-types.ts +41 -0
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +13 -27
- package/src/ipc/skill-routes/__tests__/identity.test.ts +4 -23
- package/src/ipc/skill-routes/events.ts +12 -23
- package/src/ipc/skill-routes/identity.ts +4 -17
- package/src/ipc/skill-routes/index.ts +1 -1
- package/src/ipc/skill-server.ts +6 -39
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +0 -8
- package/src/live-voice/protocol.ts +4 -13
- package/src/mcp/manager.ts +0 -5
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +55 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +127 -0
- package/src/memory/app-git-service.ts +0 -32
- package/src/memory/app-store.ts +154 -0
- package/src/memory/attachments-store.ts +6 -0
- package/src/memory/context-search/sources/memory-v2.ts +578 -0
- package/src/memory/context-search/sources/memory.ts +5 -0
- package/src/memory/context-search/sources/pkb.ts +10 -1
- package/src/memory/context-search/sources/workspace.ts +3 -2
- package/src/memory/conversation-crud.ts +29 -4
- package/src/memory/conversation-disk-view.ts +1 -5
- package/src/memory/conversation-starter-checkpoints.ts +63 -0
- package/src/memory/db-connection.ts +62 -0
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.ts +3 -21
- package/src/memory/embedding-gemini.ts +0 -2
- package/src/memory/embedding-local.ts +6 -6
- package/src/memory/embedding-ollama.ts +6 -6
- package/src/memory/embedding-openai.ts +6 -6
- package/src/memory/embedding-types.ts +21 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +3 -7
- package/src/memory/graph/conversation-graph-memory.ts +35 -13
- package/src/memory/graph/injection.test.ts +2 -2
- package/src/memory/graph/injection.ts +1 -1
- package/src/memory/guardian-action-store.ts +0 -83
- package/src/memory/guardian-approvals.ts +0 -48
- package/src/memory/indexer.ts +1 -15
- package/src/memory/job-handlers/conversation-starters.ts +36 -53
- package/src/memory/job-utils.ts +0 -6
- package/src/memory/jobs-store.ts +0 -1
- package/src/memory/jobs-worker.ts +2 -16
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +129 -43
- package/src/memory/memory-v2-activation-log-store.ts +115 -0
- package/src/memory/migrations/233-document-conversations.ts +54 -0
- package/src/memory/migrations/234-memory-v2-activation-logs.ts +55 -0
- package/src/memory/migrations/235-llm-usage-attribution.ts +31 -0
- package/src/memory/migrations/235-slack-compaction-watermark.ts +44 -0
- package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +26 -0
- package/src/memory/migrations/__tests__/234-memory-v2-activation-logs.test.ts +182 -0
- package/src/memory/migrations/index.ts +14 -0
- package/src/memory/migrations/registry.ts +24 -0
- package/src/memory/raw-query.ts +2 -68
- package/src/memory/schema/conversations.ts +7 -0
- package/src/memory/schema/infrastructure.ts +25 -0
- package/src/memory/search/semantic.ts +5 -16
- package/src/memory/tool-usage-store.ts +2 -0
- package/src/memory/usage-buckets.ts +40 -1
- package/src/memory/usage-grouped-buckets.ts +127 -0
- package/src/memory/v2/__tests__/activation.test.ts +289 -90
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +2 -129
- package/src/memory/v2/__tests__/consolidation-job.test.ts +28 -11
- package/src/memory/v2/__tests__/edge-index.test.ts +278 -0
- package/src/memory/v2/__tests__/injection.test.ts +384 -15
- package/src/memory/v2/__tests__/migration.test.ts +64 -36
- package/src/memory/v2/__tests__/page-store.test.ts +191 -8
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +181 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +115 -3
- package/src/memory/v2/__tests__/static-context.test.ts +153 -0
- package/src/memory/v2/activation.ts +168 -97
- package/src/memory/v2/backfill-jobs.ts +15 -100
- package/src/memory/v2/consolidation-job.ts +14 -12
- package/src/memory/v2/edge-index.ts +191 -0
- package/src/memory/v2/injection.ts +182 -58
- package/src/memory/v2/migration.ts +57 -64
- package/src/memory/v2/now-text.ts +2 -3
- package/src/memory/v2/page-store.ts +168 -31
- package/src/memory/v2/prompts/consolidation.ts +118 -42
- package/src/memory/v2/prompts/sweep.ts +3 -3
- package/src/memory/v2/skill-store.ts +55 -7
- package/src/memory/v2/static-context.ts +62 -0
- package/src/memory/v2/types.ts +10 -20
- package/src/memory/validation.ts +0 -11
- package/src/messaging/draft-store.ts +0 -6
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/provider.ts +7 -0
- package/src/messaging/providers/gmail/client.ts +1 -121
- package/src/messaging/providers/outlook/client.ts +0 -73
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +226 -0
- package/src/messaging/providers/slack/adapter.ts +122 -21
- package/src/messaging/providers/slack/backfill.test.ts +95 -6
- package/src/messaging/providers/slack/backfill.ts +89 -11
- package/src/messaging/providers/slack/client.ts +10 -124
- package/src/messaging/providers/slack/message-metadata.ts +12 -2
- package/src/messaging/providers/slack/render-transcript.test.ts +56 -0
- package/src/messaging/providers/slack/render-transcript.ts +126 -25
- package/src/messaging/providers/slack/types.ts +1 -0
- package/src/oauth/connection-resolver.test.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -16
- package/src/oauth/credential-token-resolver.ts +97 -0
- package/src/oauth/manual-token-connection.ts +30 -34
- package/src/oauth/oauth-store.ts +6 -4
- package/src/outbound-proxy/certs.ts +0 -7
- package/src/outbound-proxy/config.ts +0 -74
- package/src/outbound-proxy/health.ts +0 -44
- package/src/outbound-proxy/index.ts +0 -22
- package/src/permissions/approval-provenance.test.ts +184 -0
- package/src/permissions/approval-provenance.ts +70 -0
- package/src/permissions/checker.ts +4 -1
- package/src/permissions/gateway-threshold-reader.ts +4 -1
- package/src/permissions/prompter.ts +9 -2
- package/src/permissions/secret-prompter.ts +21 -48
- package/src/permissions/types.ts +33 -0
- package/src/permissions/workspace-policy.ts +0 -5
- package/src/platform/sync-identity.ts +0 -8
- package/src/plugins/defaults/injectors.ts +69 -2
- package/src/plugins/defaults/overflow-reduce.ts +3 -2
- package/src/plugins/types.ts +8 -0
- package/src/prompts/system-prompt.ts +34 -70
- package/src/prompts/templates/BOOTSTRAP.md +52 -6
- package/src/prompts/update-bulletin-job.ts +2 -0
- package/src/providers/__tests__/retry-callsite.test.ts +138 -1
- package/src/providers/anthropic/client.ts +72 -33
- package/src/providers/call-site-routing.ts +42 -3
- package/src/providers/gemini/client.ts +18 -2
- package/src/providers/managed-proxy/context.ts +0 -5
- package/src/providers/model-catalog.ts +105 -19
- package/src/providers/openai/chat-completions-provider.ts +6 -0
- package/src/providers/openai/responses-provider.ts +7 -1
- package/src/providers/provider-send-message.ts +45 -2
- package/src/providers/ratelimit.ts +7 -2
- package/src/providers/registry.ts +14 -9
- package/src/providers/retry.ts +96 -8
- package/src/providers/types.ts +13 -0
- package/src/providers/usage-tracking.ts +96 -0
- package/src/runtime/AGENTS.md +10 -6
- package/src/runtime/__tests__/agent-wake.test.ts +89 -0
- package/src/runtime/agent-wake.ts +39 -2
- package/src/runtime/assistant-event-hub.ts +541 -45
- package/src/runtime/assistant-event.ts +1 -6
- package/src/runtime/auth/context.ts +0 -9
- package/src/runtime/auth/middleware.ts +1 -1
- package/src/runtime/auth/route-policy.ts +11 -9
- package/src/runtime/auth/token-service.ts +0 -11
- package/src/runtime/channel-approvals.ts +6 -2
- package/src/runtime/channel-verification-service.ts +3 -5
- package/src/runtime/http-errors.ts +0 -34
- package/src/runtime/http-router.ts +6 -3
- package/src/runtime/http-server.ts +22 -82
- package/src/runtime/http-types.ts +5 -0
- package/src/runtime/interactive-ui.ts +0 -1
- package/src/runtime/middleware/auth.ts +0 -20
- package/src/runtime/migrations/__tests__/v1-test-helpers.ts +112 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +11 -4
- package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +253 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +19 -6
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +71 -27
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +41 -2
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +143 -79
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +143 -23
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +2 -2
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +371 -0
- package/src/runtime/migrations/migration-transport.ts +46 -13
- package/src/runtime/migrations/migration-wizard.ts +2 -2
- package/src/runtime/migrations/origin-mode.ts +40 -0
- package/src/runtime/migrations/vbundle-builder.ts +133 -79
- package/src/runtime/migrations/vbundle-import-analyzer.ts +9 -7
- package/src/runtime/migrations/vbundle-importer.ts +7 -7
- package/src/runtime/migrations/vbundle-metadata-merge.ts +1 -1
- package/src/runtime/migrations/vbundle-streaming-importer.ts +3 -3
- package/src/runtime/migrations/vbundle-streaming-validator.ts +48 -26
- package/src/runtime/migrations/vbundle-validator.ts +214 -41
- package/src/runtime/pending-interactions.ts +13 -4
- package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -1
- package/src/runtime/routes/__tests__/backup-routes.test.ts +28 -19
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +235 -0
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +58 -0
- package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +54 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +7 -7
- package/src/runtime/routes/acp-routes.test.ts +0 -3
- package/src/runtime/routes/acp-routes.ts +3 -7
- package/src/runtime/routes/app-management-routes.ts +18 -9
- package/src/runtime/routes/approval-routes.ts +55 -14
- package/src/runtime/routes/avatar-routes.ts +3 -5
- package/src/runtime/routes/browser-routes.ts +1 -15
- package/src/runtime/routes/channel-guardian-routes.ts +1 -5
- package/src/runtime/routes/channel-readiness-routes.ts +3 -7
- package/src/runtime/routes/channel-route-shared.ts +2 -28
- package/src/runtime/routes/client-routes.ts +45 -12
- package/src/runtime/routes/consolidation-routes.ts +115 -0
- package/src/runtime/routes/conversation-list-routes.ts +12 -29
- package/src/runtime/routes/conversation-management-routes.ts +14 -51
- package/src/runtime/routes/conversation-query-routes.ts +120 -8
- package/src/runtime/routes/conversation-routes.ts +44 -528
- package/src/runtime/routes/conversation-starter-routes.ts +19 -40
- package/src/runtime/routes/documents-routes.ts +53 -18
- package/src/runtime/routes/events-routes.ts +59 -91
- package/src/runtime/routes/filing-routes.ts +18 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -9
- package/src/runtime/routes/host-bash-routes.ts +3 -2
- package/src/runtime/routes/host-browser-routes.ts +9 -33
- package/src/runtime/routes/host-cu-routes.ts +6 -1
- package/src/runtime/routes/host-file-routes.ts +3 -2
- package/src/runtime/routes/host-transfer-routes.ts +11 -15
- package/src/runtime/routes/identity-routes.ts +78 -6
- package/src/runtime/routes/inbound-message-handler.ts +580 -137
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -88
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -0
- package/src/runtime/routes/index.ts +4 -0
- package/src/runtime/routes/integrations/slack/channel.ts +0 -24
- package/src/runtime/routes/llm-call-sites-routes.ts +22 -0
- package/src/runtime/routes/memory-v2-routes.ts +10 -15
- package/src/runtime/routes/migration-routes.ts +188 -31
- package/src/runtime/routes/playground/guard.ts +1 -1
- package/src/runtime/routes/playground/index.ts +0 -2
- package/src/runtime/routes/recording-routes.ts +4 -24
- package/src/runtime/routes/rename-conversation-routes.ts +2 -6
- package/src/runtime/routes/schedule-routes.ts +3 -6
- package/src/runtime/routes/secret-routes.ts +87 -18
- package/src/runtime/routes/settings-routes.ts +29 -28
- package/src/runtime/routes/skills-routes.ts +12 -31
- package/src/runtime/routes/suggest-trust-rule-routes.ts +32 -1
- package/src/runtime/routes/task-routes.ts +6 -6
- package/src/runtime/routes/trust-rules-routes.ts +3 -94
- package/src/runtime/routes/types.ts +4 -4
- package/src/runtime/routes/upgrade-broadcast-routes.ts +3 -10
- package/src/runtime/routes/usage-routes.ts +87 -10
- package/src/runtime/routes/user-routes.ts +17 -31
- package/src/runtime/routes/work-items-routes.ts +1 -4
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -2
- package/src/runtime/services/analyze-conversation.ts +7 -17
- package/src/runtime/services/conversation-serializer.ts +2 -4
- package/src/runtime/verification-outbound-actions.ts +1 -1
- package/src/runtime/verification-rate-limiter.ts +1 -1
- package/src/schedule/schedule-store.ts +0 -16
- package/src/security/secret-scanner.ts +14 -547
- package/src/security/secure-keys.ts +31 -11
- package/src/security/token-manager.ts +7 -3
- package/src/signals/cancel.ts +16 -25
- package/src/signals/conversation-undo.ts +2 -27
- package/src/signals/emit-event.ts +1 -2
- package/src/signals/user-message.ts +108 -22
- package/src/skills/catalog-install.ts +1 -0
- package/src/skills/clawhub.ts +2 -2
- package/src/skills/inline-command-runner.ts +1 -7
- package/src/subagent/manager.ts +67 -84
- package/src/tasks/task-store.ts +1 -28
- package/src/telemetry/types.ts +6 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +38 -15
- package/src/telemetry/usage-telemetry-reporter.ts +3 -5
- package/src/tools/acp/spawn.test.ts +1 -2
- package/src/tools/acp/steer.test.ts +1 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +44 -127
- package/src/tools/browser/browser-execution.ts +31 -147
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +92 -68
- package/src/tools/browser/cdp-client/factory.ts +48 -76
- package/src/tools/browser/cdp-client/index.ts +1 -14
- package/src/tools/executor.ts +44 -31
- package/src/tools/host-filesystem/edit.ts +3 -2
- package/src/tools/host-filesystem/read.ts +3 -2
- package/src/tools/host-filesystem/transfer.test.ts +45 -42
- package/src/tools/host-filesystem/transfer.ts +4 -3
- package/src/tools/host-filesystem/write.ts +3 -2
- package/src/tools/host-terminal/host-shell.ts +4 -3
- package/src/tools/network/script-proxy/index.ts +1 -10
- package/src/tools/permission-checker.ts +66 -1
- package/src/tools/skills/sandbox-runner.ts +1 -6
- package/src/tools/skills/skill-tool-factory.ts +32 -0
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/terminal/shell.ts +2 -78
- package/src/tools/types.ts +12 -39
- package/src/tts/__tests__/provider-catalog.test.ts +2 -2
- package/src/tts/provider-catalog.ts +1 -1
- package/src/usage/actors.ts +2 -1
- package/src/usage/attribution.ts +185 -0
- package/src/usage/pricing.ts +166 -0
- package/src/usage/types.ts +14 -0
- package/src/util/json.ts +13 -0
- package/src/util/logger.ts +3 -3
- package/src/util/pricing.ts +50 -3
- package/src/work-items/work-item-runner.ts +15 -42
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +4 -3
- package/src/workspace/migrations/052-seed-default-inference-profiles.ts +3 -3
- package/src/workspace/migrations/060-memory-v2-init.ts +2 -18
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +59 -0
- package/src/workspace/migrations/062-drop-memory-v2-edges-json.ts +27 -0
- package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +70 -0
- package/src/workspace/migrations/064-unwind-main-agent-opus-seed.ts +64 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/provider-commit-message-generator.ts +3 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +0 -138
- package/src/__tests__/sandbox-host-parity.test.ts +0 -1024
- package/src/__tests__/secret-detection-handler.test.ts +0 -67
- package/src/__tests__/secret-scanner-executor.test.ts +0 -450
- package/src/__tests__/tcc-sandbox-deny.test.ts +0 -198
- package/src/__tests__/terminal-sandbox.test.ts +0 -374
- package/src/__tests__/tool-notification-listener.test.ts +0 -65
- package/src/context/__tests__/microcompact.test.ts +0 -805
- package/src/context/microcompact.ts +0 -443
- package/src/daemon/handlers/slack-channel-oauth-install.ts +0 -197
- package/src/events/tool-notification-listener.ts +0 -17
- package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +0 -219
- package/src/memory/v2/__tests__/edges.test.ts +0 -435
- package/src/memory/v2/edges.ts +0 -217
- package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +0 -197
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +0 -518
- package/src/runtime/__tests__/client-registry.test.ts +0 -271
- package/src/runtime/chrome-extension-registry.ts +0 -368
- package/src/runtime/client-registry.ts +0 -254
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +0 -329
- package/src/tools/secret-detection-handler.ts +0 -269
- package/src/tools/terminal/backends/native.ts +0 -327
- package/src/tools/terminal/backends/types.ts +0 -37
- package/src/tools/terminal/sandbox-diagnostics.ts +0 -87
- package/src/tools/terminal/sandbox.ts +0 -40
|
@@ -2,18 +2,35 @@
|
|
|
2
2
|
* In-process pub/sub hub for assistant events.
|
|
3
3
|
*
|
|
4
4
|
* Provides subscribe / publish primitives used by the daemon send paths
|
|
5
|
-
* and the SSE route.
|
|
5
|
+
* and the SSE route.
|
|
6
|
+
*
|
|
7
|
+
* Subscribers are typed via a discriminated union:
|
|
8
|
+
* - **ClientEntry** — an SSE-connected client (macos, chrome-extension, …)
|
|
9
|
+
* with identity, capabilities, and timestamps.
|
|
10
|
+
* - **ProcessEntry** — an in-process consumer (future: file-append logger).
|
|
11
|
+
*
|
|
12
|
+
* Client-oriented queries (list, find-by-capability) are methods on the hub.
|
|
6
13
|
*/
|
|
7
14
|
|
|
15
|
+
import type { HostProxyCapability, InterfaceId } from "../channels/types.js";
|
|
16
|
+
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
17
|
+
import { emitFeedEvent } from "../home/emit-feed-event.js";
|
|
18
|
+
import { rewriteCommandPreview } from "../home/rewrite-command-preview.js";
|
|
19
|
+
import { redactSecrets } from "../security/secret-scanner.js";
|
|
20
|
+
import { appendEventToStream } from "../signals/event-stream.js";
|
|
21
|
+
import { summarizeToolInput } from "../tools/tool-input-summary.js";
|
|
22
|
+
import { getLogger } from "../util/logger.js";
|
|
8
23
|
import type { AssistantEvent } from "./assistant-event.js";
|
|
24
|
+
import { buildAssistantEvent } from "./assistant-event.js";
|
|
25
|
+
import * as pendingInteractions from "./pending-interactions.js";
|
|
26
|
+
|
|
27
|
+
const log = getLogger("assistant-event-hub");
|
|
9
28
|
|
|
10
29
|
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
11
30
|
|
|
12
|
-
/**
|
|
31
|
+
/** Filter that determines which events a subscriber receives. */
|
|
13
32
|
export type AssistantEventFilter = {
|
|
14
|
-
/**
|
|
15
|
-
assistantId: string;
|
|
16
|
-
/** When set, further restrict to this conversation. */
|
|
33
|
+
/** When set, restrict delivery to events for this conversation. */
|
|
17
34
|
conversationId?: string;
|
|
18
35
|
};
|
|
19
36
|
|
|
@@ -28,24 +45,57 @@ export interface AssistantEventSubscription {
|
|
|
28
45
|
readonly active: boolean;
|
|
29
46
|
}
|
|
30
47
|
|
|
31
|
-
// ──
|
|
48
|
+
// ── Subscriber entries (discriminated union) ─────────────────────────────────
|
|
32
49
|
|
|
33
|
-
interface
|
|
50
|
+
interface BaseSubscriberEntry {
|
|
34
51
|
filter: AssistantEventFilter;
|
|
35
52
|
callback: AssistantEventCallback;
|
|
36
53
|
active: boolean;
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
onEvict: () => void;
|
|
55
|
+
connectedAt: Date;
|
|
56
|
+
lastActiveAt: Date;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ClientEntry extends BaseSubscriberEntry {
|
|
60
|
+
type: "client";
|
|
61
|
+
clientId: string;
|
|
62
|
+
interfaceId: InterfaceId;
|
|
63
|
+
capabilities: HostProxyCapability[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ProcessEntry extends BaseSubscriberEntry {
|
|
67
|
+
type: "process";
|
|
39
68
|
}
|
|
40
69
|
|
|
70
|
+
export type SubscriberEntry = ClientEntry | ProcessEntry;
|
|
71
|
+
|
|
72
|
+
/** Distributive Omit that preserves union discrimination. */
|
|
73
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
|
|
74
|
+
? Omit<T, K>
|
|
75
|
+
: never;
|
|
76
|
+
|
|
77
|
+
/** Input shape for `subscribe()` — hub fills `active`, `connectedAt`, `lastActiveAt` and defaults `filter`/`onEvict`. */
|
|
78
|
+
export type SubscriberInput = DistributiveOmit<
|
|
79
|
+
SubscriberEntry,
|
|
80
|
+
"active" | "connectedAt" | "lastActiveAt" | "filter" | "onEvict"
|
|
81
|
+
> & {
|
|
82
|
+
filter?: AssistantEventFilter;
|
|
83
|
+
onEvict?: () => void;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// ── Hub ───────────────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
41
88
|
/**
|
|
42
89
|
* Lightweight pub/sub hub for `AssistantEvent` messages.
|
|
43
90
|
*
|
|
44
|
-
* Filtering is applied at subscription level
|
|
45
|
-
* events
|
|
91
|
+
* Filtering is applied at subscription level:
|
|
92
|
+
* - `conversationId`: scoped events match subscribers with same conversationId
|
|
93
|
+
* or no conversationId filter (broadcast to all).
|
|
94
|
+
* - `targetCapability` (on publish): targeted events only reach subscribers
|
|
95
|
+
* whose capabilities include the target. Untargeted events fan out to all.
|
|
46
96
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
97
|
+
* Client connections register as subscribers with metadata and are queryable
|
|
98
|
+
* via `listClients()`, `getMostRecentClientByCapability()`, etc.
|
|
49
99
|
*/
|
|
50
100
|
export class AssistantEventHub {
|
|
51
101
|
private readonly subscribers = new Set<SubscriberEntry>();
|
|
@@ -58,26 +108,48 @@ export class AssistantEventHub {
|
|
|
58
108
|
/**
|
|
59
109
|
* Register a subscriber that will be called for each matching event.
|
|
60
110
|
*
|
|
111
|
+
* **Client deduplication:** When a client subscriber is registered with a
|
|
112
|
+
* `clientId` that already exists, all stale entries for that clientId are
|
|
113
|
+
* disposed first. This prevents subscriber stacking when clients reconnect
|
|
114
|
+
* (e.g. Chrome extension reload, SSE token refresh) before the old
|
|
115
|
+
* connection's abort signal fires.
|
|
116
|
+
*
|
|
61
117
|
* When the subscriber cap (`maxSubscribers`) has been reached, the **oldest**
|
|
62
118
|
* subscriber is evicted to make room: its `onEvict` callback is invoked (so
|
|
63
119
|
* it can close its SSE stream) and its entry is removed from the hub.
|
|
64
|
-
*
|
|
65
|
-
* The only case that throws is when `maxSubscribers` is 0 — there is nothing
|
|
66
|
-
* to evict and no room to add.
|
|
67
|
-
*
|
|
68
|
-
* @param options.onEvict Called if this subscriber is later evicted by a newer one.
|
|
69
|
-
* @returns A subscription handle. Call `dispose()` to unsubscribe.
|
|
70
120
|
*/
|
|
71
|
-
subscribe(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
121
|
+
subscribe(subscriber: SubscriberInput): AssistantEventSubscription {
|
|
122
|
+
// Deduplicate: dispose stale subscribers for the same clientId.
|
|
123
|
+
if (subscriber.type === "client") {
|
|
124
|
+
const stale: SubscriberEntry[] = [];
|
|
125
|
+
for (const existing of this.subscribers) {
|
|
126
|
+
if (
|
|
127
|
+
existing.type === "client" &&
|
|
128
|
+
existing.clientId === subscriber.clientId
|
|
129
|
+
) {
|
|
130
|
+
stale.push(existing);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
for (const entry of stale) {
|
|
134
|
+
entry.active = false;
|
|
135
|
+
this.subscribers.delete(entry);
|
|
136
|
+
try {
|
|
137
|
+
entry.onEvict();
|
|
138
|
+
} catch {
|
|
139
|
+
/* ignore eviction callback errors */
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (stale.length > 0) {
|
|
143
|
+
log.info(
|
|
144
|
+
{ clientId: subscriber.clientId, count: stale.length },
|
|
145
|
+
"disposed stale subscribers for reconnecting client",
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
76
150
|
if (this.subscribers.size >= this.maxSubscribers) {
|
|
77
|
-
// Evict the oldest subscriber (Sets maintain insertion order).
|
|
78
151
|
const [oldest] = this.subscribers;
|
|
79
152
|
if (!oldest) {
|
|
80
|
-
// maxSubscribers is 0 — nothing to evict, nothing to add.
|
|
81
153
|
throw new RangeError(
|
|
82
154
|
`AssistantEventHub: subscriber cap reached (${this.maxSubscribers})`,
|
|
83
155
|
);
|
|
@@ -85,17 +157,35 @@ export class AssistantEventHub {
|
|
|
85
157
|
oldest.active = false;
|
|
86
158
|
this.subscribers.delete(oldest);
|
|
87
159
|
try {
|
|
88
|
-
oldest.onEvict
|
|
160
|
+
oldest.onEvict();
|
|
89
161
|
} catch {
|
|
90
162
|
/* ignore eviction callback errors */
|
|
91
163
|
}
|
|
92
164
|
}
|
|
165
|
+
|
|
166
|
+
const now = new Date();
|
|
93
167
|
const entry: SubscriberEntry = {
|
|
94
|
-
|
|
95
|
-
|
|
168
|
+
...subscriber,
|
|
169
|
+
filter: subscriber.filter ?? {},
|
|
170
|
+
onEvict: subscriber.onEvict ?? (() => {}),
|
|
96
171
|
active: true,
|
|
97
|
-
|
|
98
|
-
|
|
172
|
+
connectedAt: now,
|
|
173
|
+
lastActiveAt: now,
|
|
174
|
+
} as SubscriberEntry;
|
|
175
|
+
|
|
176
|
+
if (entry.type === "client") {
|
|
177
|
+
log.info(
|
|
178
|
+
{
|
|
179
|
+
clientId: entry.clientId,
|
|
180
|
+
interfaceId: entry.interfaceId,
|
|
181
|
+
capabilities: entry.capabilities,
|
|
182
|
+
},
|
|
183
|
+
"subscriber registered (client)",
|
|
184
|
+
);
|
|
185
|
+
} else {
|
|
186
|
+
log.info("subscriber registered (process)");
|
|
187
|
+
}
|
|
188
|
+
|
|
99
189
|
this.subscribers.add(entry);
|
|
100
190
|
|
|
101
191
|
return {
|
|
@@ -103,6 +193,17 @@ export class AssistantEventHub {
|
|
|
103
193
|
if (entry.active) {
|
|
104
194
|
entry.active = false;
|
|
105
195
|
this.subscribers.delete(entry);
|
|
196
|
+
if (entry.type === "client") {
|
|
197
|
+
log.info(
|
|
198
|
+
{
|
|
199
|
+
clientId: entry.clientId,
|
|
200
|
+
interfaceId: entry.interfaceId,
|
|
201
|
+
},
|
|
202
|
+
"subscriber unregistered (client)",
|
|
203
|
+
);
|
|
204
|
+
} else {
|
|
205
|
+
log.info("subscriber unregistered (process)");
|
|
206
|
+
}
|
|
106
207
|
}
|
|
107
208
|
},
|
|
108
209
|
get active() {
|
|
@@ -115,32 +216,51 @@ export class AssistantEventHub {
|
|
|
115
216
|
* Publish an event to all matching subscribers.
|
|
116
217
|
*
|
|
117
218
|
* Matching rules:
|
|
118
|
-
* - `event.assistantId` must equal `filter.assistantId`
|
|
119
219
|
* - if `filter.conversationId` is set, `event.conversationId` must equal it
|
|
220
|
+
* - if `targetCapability` is set, only subscribers whose capabilities include
|
|
221
|
+
* it receive the event; untargeted events go to all
|
|
120
222
|
*
|
|
121
223
|
* Fanout is isolated: a throwing or rejecting subscriber does not abort
|
|
122
|
-
* delivery to remaining subscribers.
|
|
123
|
-
* awaited and their errors collected; any errors are re-thrown together
|
|
124
|
-
* as an `AggregateError` after all callbacks have been invoked.
|
|
125
|
-
*
|
|
126
|
-
* Subscribers are snapshotted at the start of each publish call so that
|
|
127
|
-
* callbacks adding new subscriptions do not receive the in-flight event.
|
|
224
|
+
* delivery to remaining subscribers.
|
|
128
225
|
*/
|
|
129
|
-
async publish(
|
|
226
|
+
async publish(
|
|
227
|
+
event: AssistantEvent,
|
|
228
|
+
options?: { targetCapability?: HostProxyCapability },
|
|
229
|
+
): Promise<void> {
|
|
230
|
+
if (event.conversationId) {
|
|
231
|
+
try {
|
|
232
|
+
appendEventToStream(event.conversationId, event);
|
|
233
|
+
} catch {
|
|
234
|
+
// Best-effort; file I/O failures must not block subscriber fanout.
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const targetCapability = options?.targetCapability;
|
|
130
239
|
const snapshot = Array.from(this.subscribers);
|
|
131
240
|
const errors: unknown[] = [];
|
|
132
241
|
|
|
133
242
|
for (const entry of snapshot) {
|
|
134
243
|
if (!entry.active) continue;
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
//
|
|
244
|
+
|
|
245
|
+
// Conversation scoping: scoped events skip subscribers filtering on a
|
|
246
|
+
// different conversation.
|
|
138
247
|
if (
|
|
139
248
|
event.conversationId != null &&
|
|
140
249
|
entry.filter.conversationId != null &&
|
|
141
250
|
entry.filter.conversationId !== event.conversationId
|
|
142
251
|
)
|
|
143
252
|
continue;
|
|
253
|
+
|
|
254
|
+
// Capability targeting: targeted events only go to subscribers that
|
|
255
|
+
// declare the required capability.
|
|
256
|
+
if (targetCapability != null) {
|
|
257
|
+
if (
|
|
258
|
+
entry.type !== "client" ||
|
|
259
|
+
!entry.capabilities.includes(targetCapability)
|
|
260
|
+
)
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
|
|
144
264
|
try {
|
|
145
265
|
await entry.callback(event);
|
|
146
266
|
} catch (err) {
|
|
@@ -158,14 +278,13 @@ export class AssistantEventHub {
|
|
|
158
278
|
|
|
159
279
|
/**
|
|
160
280
|
* Returns true when at least one active subscriber would receive the given
|
|
161
|
-
* event based on the same
|
|
281
|
+
* event based on the same conversation matching rules as publish().
|
|
162
282
|
*/
|
|
163
283
|
hasSubscribersForEvent(
|
|
164
|
-
event: Pick<AssistantEvent, "
|
|
284
|
+
event: Pick<AssistantEvent, "conversationId">,
|
|
165
285
|
): boolean {
|
|
166
286
|
for (const entry of this.subscribers) {
|
|
167
287
|
if (!entry.active) continue;
|
|
168
|
-
if (entry.filter.assistantId !== event.assistantId) continue;
|
|
169
288
|
if (
|
|
170
289
|
event.conversationId != null &&
|
|
171
290
|
entry.filter.conversationId != null &&
|
|
@@ -178,6 +297,106 @@ export class AssistantEventHub {
|
|
|
178
297
|
return false;
|
|
179
298
|
}
|
|
180
299
|
|
|
300
|
+
// ── Client queries ──────────────────────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
private clientEntries(): ClientEntry[] {
|
|
303
|
+
const clients: ClientEntry[] = [];
|
|
304
|
+
for (const entry of this.subscribers) {
|
|
305
|
+
if (entry.active && entry.type === "client") {
|
|
306
|
+
clients.push(entry);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return clients.sort(
|
|
310
|
+
(a, b) => b.lastActiveAt.getTime() - a.lastActiveAt.getTime(),
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Return all active client subscribers, sorted by `lastActiveAt` descending.
|
|
316
|
+
*/
|
|
317
|
+
listClients(): ClientEntry[] {
|
|
318
|
+
return this.clientEntries();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Return all client subscribers that support the given capability,
|
|
323
|
+
* sorted by `lastActiveAt` descending.
|
|
324
|
+
*/
|
|
325
|
+
listClientsByCapability(capability: HostProxyCapability): ClientEntry[] {
|
|
326
|
+
return this.clientEntries().filter((c) =>
|
|
327
|
+
c.capabilities.includes(capability),
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Return the most recently active client that supports the given
|
|
333
|
+
* capability, or `undefined` if none exists.
|
|
334
|
+
*/
|
|
335
|
+
getMostRecentClientByCapability(
|
|
336
|
+
capability: HostProxyCapability,
|
|
337
|
+
): ClientEntry | undefined {
|
|
338
|
+
return this.listClientsByCapability(capability)[0];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Return all client subscribers with the given interface type,
|
|
343
|
+
* sorted by `lastActiveAt` descending.
|
|
344
|
+
*/
|
|
345
|
+
listClientsByInterface(interfaceId: InterfaceId): ClientEntry[] {
|
|
346
|
+
return this.clientEntries().filter((c) => c.interfaceId === interfaceId);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Touch a client subscriber — update `lastActiveAt`. Used by heartbeat.
|
|
351
|
+
*/
|
|
352
|
+
touchClient(clientId: string): void {
|
|
353
|
+
const now = new Date();
|
|
354
|
+
for (const entry of this.subscribers) {
|
|
355
|
+
if (
|
|
356
|
+
entry.active &&
|
|
357
|
+
entry.type === "client" &&
|
|
358
|
+
entry.clientId === clientId
|
|
359
|
+
) {
|
|
360
|
+
entry.lastActiveAt = now;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Force-disconnect a client by disposing all subscribers for the given
|
|
367
|
+
* `clientId`. Returns the number of disposed entries.
|
|
368
|
+
*
|
|
369
|
+
* Used by `assistant clients disconnect <clientId>` to forcibly remove
|
|
370
|
+
* stale or unwanted client connections.
|
|
371
|
+
*/
|
|
372
|
+
disposeClient(clientId: string): number {
|
|
373
|
+
const targets: SubscriberEntry[] = [];
|
|
374
|
+
for (const entry of this.subscribers) {
|
|
375
|
+
if (
|
|
376
|
+
entry.type === "client" &&
|
|
377
|
+
entry.clientId === clientId
|
|
378
|
+
) {
|
|
379
|
+
targets.push(entry);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
for (const entry of targets) {
|
|
383
|
+
entry.active = false;
|
|
384
|
+
this.subscribers.delete(entry);
|
|
385
|
+
try {
|
|
386
|
+
entry.onEvict();
|
|
387
|
+
} catch {
|
|
388
|
+
/* ignore eviction callback errors */
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (targets.length > 0) {
|
|
392
|
+
log.info(
|
|
393
|
+
{ clientId, count: targets.length },
|
|
394
|
+
"force-disposed client subscribers",
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return targets.length;
|
|
398
|
+
}
|
|
399
|
+
|
|
181
400
|
/** Number of currently active subscribers (useful for tests and caps). */
|
|
182
401
|
subscriberCount(): number {
|
|
183
402
|
return this.subscribers.size;
|
|
@@ -197,3 +416,280 @@ export class AssistantEventHub {
|
|
|
197
416
|
* Import and use this in daemon send paths and the SSE route.
|
|
198
417
|
*/
|
|
199
418
|
export const assistantEventHub = new AssistantEventHub({ maxSubscribers: 100 });
|
|
419
|
+
|
|
420
|
+
// ── Convenience: ServerMessage → AssistantEvent publish ───────────────────────
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Promise chain that serializes publishes so subscribers always observe
|
|
424
|
+
* events in send order.
|
|
425
|
+
*/
|
|
426
|
+
let _hubChain = Promise.resolve();
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Wraps a `ServerMessage` in an `AssistantEvent` envelope and publishes it
|
|
430
|
+
* to the process-level hub.
|
|
431
|
+
*
|
|
432
|
+
* When `conversationId` is omitted, it is auto-extracted from the message
|
|
433
|
+
* payload (if present).
|
|
434
|
+
*
|
|
435
|
+
* This is the primary entrypoint for emitting events — handlers, routes, and
|
|
436
|
+
* services should call this directly instead of threading a broadcast callback.
|
|
437
|
+
*/
|
|
438
|
+
export function broadcastMessage(
|
|
439
|
+
msg: ServerMessage,
|
|
440
|
+
conversationId?: string,
|
|
441
|
+
options?: { targetCapability?: HostProxyCapability },
|
|
442
|
+
): void {
|
|
443
|
+
const resolvedConversationId = conversationId ?? extractConversationId(msg);
|
|
444
|
+
|
|
445
|
+
// Register pending interactions so approval/host prompts are tracked
|
|
446
|
+
// regardless of which path triggered the broadcast.
|
|
447
|
+
if (resolvedConversationId) {
|
|
448
|
+
registerPendingInteraction(msg, resolvedConversationId);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Emit feed events for confirmation requests (tool approval prompts).
|
|
452
|
+
if (msg.type === "confirmation_request" && resolvedConversationId) {
|
|
453
|
+
void emitConfirmationFeedEvent(msg, resolvedConversationId);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// `conversation_list_invalidated` is a list-level system event — publish
|
|
457
|
+
// it unscoped so every subscriber refreshes its sidebar.
|
|
458
|
+
const scopedConversationId =
|
|
459
|
+
msg.type === "conversation_list_invalidated"
|
|
460
|
+
? undefined
|
|
461
|
+
: resolvedConversationId;
|
|
462
|
+
const event = buildAssistantEvent(msg, scopedConversationId);
|
|
463
|
+
_hubChain = _hubChain
|
|
464
|
+
.then(() => assistantEventHub.publish(event, options))
|
|
465
|
+
.then(() => {
|
|
466
|
+
// When a conversation title changes, also broadcast an unscoped
|
|
467
|
+
// `conversation_list_invalidated` so every connected client's sidebar
|
|
468
|
+
// refreshes — not just the client viewing this conversation.
|
|
469
|
+
if (msg.type === "conversation_title_updated") {
|
|
470
|
+
return assistantEventHub
|
|
471
|
+
.publish(
|
|
472
|
+
buildAssistantEvent({
|
|
473
|
+
type: "conversation_list_invalidated",
|
|
474
|
+
reason: "renamed",
|
|
475
|
+
}),
|
|
476
|
+
)
|
|
477
|
+
.catch((err: unknown) => {
|
|
478
|
+
log.warn(
|
|
479
|
+
{ err },
|
|
480
|
+
"Failed to publish conversation_list_invalidated after title update",
|
|
481
|
+
);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
})
|
|
485
|
+
.catch((err: unknown) => {
|
|
486
|
+
log.warn({ err }, "assistant-events hub subscriber threw during publish");
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function extractConversationId(msg: ServerMessage): string | undefined {
|
|
491
|
+
const record = msg as unknown as Record<string, unknown>;
|
|
492
|
+
if ("conversationId" in msg && typeof record.conversationId === "string") {
|
|
493
|
+
return record.conversationId as string;
|
|
494
|
+
}
|
|
495
|
+
return undefined;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ── Pending interaction registration ──────────────────────────────────────────
|
|
499
|
+
|
|
500
|
+
function resolveCanonicalRequestSourceType(
|
|
501
|
+
sourceChannel: string,
|
|
502
|
+
): "desktop" | "channel" | "voice" {
|
|
503
|
+
if (sourceChannel === "phone") return "voice";
|
|
504
|
+
if (sourceChannel === "vellum") return "desktop";
|
|
505
|
+
return "channel";
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Register pending interactions for request-type messages so approval and
|
|
510
|
+
* host prompts are tracked regardless of which code path broadcasts them.
|
|
511
|
+
*
|
|
512
|
+
* Heavy dependencies (conversation-store, canonical-guardian-store, etc.) are
|
|
513
|
+
* imported lazily so that loading this module during tests doesn't trigger
|
|
514
|
+
* config/data-dir side effects.
|
|
515
|
+
*/
|
|
516
|
+
function registerPendingInteraction(
|
|
517
|
+
msg: ServerMessage,
|
|
518
|
+
conversationId: string,
|
|
519
|
+
): void {
|
|
520
|
+
if (msg.type === "confirmation_request") {
|
|
521
|
+
pendingInteractions.register(msg.requestId, {
|
|
522
|
+
conversationId,
|
|
523
|
+
kind: "confirmation",
|
|
524
|
+
confirmationDetails: {
|
|
525
|
+
toolName: msg.toolName,
|
|
526
|
+
input: msg.input,
|
|
527
|
+
riskLevel: msg.riskLevel,
|
|
528
|
+
executionTarget: msg.executionTarget,
|
|
529
|
+
allowlistOptions: msg.allowlistOptions,
|
|
530
|
+
scopeOptions: msg.scopeOptions,
|
|
531
|
+
persistentDecisionsAllowed: msg.persistentDecisionsAllowed,
|
|
532
|
+
},
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
// Create canonical guardian request asynchronously — heavy deps are
|
|
536
|
+
// imported lazily to avoid pulling in conversation-store (and
|
|
537
|
+
// transitively config/loader → ensureDataDir) at module-load time.
|
|
538
|
+
void createCanonicalRequestForConfirmation(msg, conversationId);
|
|
539
|
+
} else if (msg.type === "secret_request") {
|
|
540
|
+
pendingInteractions.register(msg.requestId, {
|
|
541
|
+
conversationId,
|
|
542
|
+
kind: "secret",
|
|
543
|
+
});
|
|
544
|
+
} else if (msg.type === "host_bash_request") {
|
|
545
|
+
pendingInteractions.register(msg.requestId, {
|
|
546
|
+
conversationId,
|
|
547
|
+
kind: "host_bash",
|
|
548
|
+
});
|
|
549
|
+
} else if (msg.type === "host_browser_request") {
|
|
550
|
+
pendingInteractions.register(msg.requestId, {
|
|
551
|
+
conversationId,
|
|
552
|
+
kind: "host_browser",
|
|
553
|
+
});
|
|
554
|
+
} else if (msg.type === "host_file_request") {
|
|
555
|
+
pendingInteractions.register(msg.requestId, {
|
|
556
|
+
conversationId,
|
|
557
|
+
kind: "host_file",
|
|
558
|
+
});
|
|
559
|
+
} else if (msg.type === "host_cu_request") {
|
|
560
|
+
pendingInteractions.register(msg.requestId, {
|
|
561
|
+
conversationId,
|
|
562
|
+
kind: "host_cu",
|
|
563
|
+
});
|
|
564
|
+
} else if (msg.type === "host_transfer_request") {
|
|
565
|
+
pendingInteractions.register(msg.requestId, {
|
|
566
|
+
conversationId,
|
|
567
|
+
kind: "host_transfer",
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Lazily load heavy dependencies and create a canonical guardian request +
|
|
574
|
+
* bridge for a confirmation_request message. Runs fire-and-forget from
|
|
575
|
+
* registerPendingInteraction.
|
|
576
|
+
*/
|
|
577
|
+
async function createCanonicalRequestForConfirmation(
|
|
578
|
+
msg: ServerMessage & { type: "confirmation_request" },
|
|
579
|
+
conversationId: string,
|
|
580
|
+
): Promise<void> {
|
|
581
|
+
try {
|
|
582
|
+
const [
|
|
583
|
+
{ findConversation },
|
|
584
|
+
{ createCanonicalGuardianRequest, generateCanonicalRequestCode },
|
|
585
|
+
{ redactSecrets },
|
|
586
|
+
{ summarizeToolInput },
|
|
587
|
+
{ DAEMON_INTERNAL_ASSISTANT_ID },
|
|
588
|
+
{ bridgeConfirmationRequestToGuardian },
|
|
589
|
+
] = await Promise.all([
|
|
590
|
+
import("../daemon/conversation-store.js"),
|
|
591
|
+
import("../memory/canonical-guardian-store.js"),
|
|
592
|
+
import("../security/secret-scanner.js"),
|
|
593
|
+
import("../tools/tool-input-summary.js"),
|
|
594
|
+
import("./assistant-scope.js"),
|
|
595
|
+
import("./confirmation-request-guardian-bridge.js"),
|
|
596
|
+
]);
|
|
597
|
+
|
|
598
|
+
const conversation = findConversation(conversationId);
|
|
599
|
+
const trustContext = conversation?.trustContext;
|
|
600
|
+
const sourceChannel = trustContext?.sourceChannel ?? "vellum";
|
|
601
|
+
const inputRecord = msg.input as Record<string, unknown>;
|
|
602
|
+
const activityRaw =
|
|
603
|
+
(typeof inputRecord.activity === "string"
|
|
604
|
+
? inputRecord.activity
|
|
605
|
+
: undefined) ??
|
|
606
|
+
(typeof inputRecord.reason === "string" ? inputRecord.reason : undefined);
|
|
607
|
+
const canonicalRequest = createCanonicalGuardianRequest({
|
|
608
|
+
id: msg.requestId,
|
|
609
|
+
kind: "tool_approval",
|
|
610
|
+
sourceType: resolveCanonicalRequestSourceType(sourceChannel),
|
|
611
|
+
sourceChannel,
|
|
612
|
+
conversationId,
|
|
613
|
+
requesterExternalUserId: trustContext?.requesterExternalUserId,
|
|
614
|
+
requesterChatId: trustContext?.requesterChatId,
|
|
615
|
+
guardianExternalUserId: trustContext?.guardianExternalUserId,
|
|
616
|
+
guardianPrincipalId: trustContext?.guardianPrincipalId ?? undefined,
|
|
617
|
+
toolName: msg.toolName,
|
|
618
|
+
commandPreview:
|
|
619
|
+
redactSecrets(summarizeToolInput(msg.toolName, inputRecord)) ||
|
|
620
|
+
undefined,
|
|
621
|
+
riskLevel: msg.riskLevel,
|
|
622
|
+
activityText: activityRaw ? redactSecrets(activityRaw) : undefined,
|
|
623
|
+
executionTarget: msg.executionTarget,
|
|
624
|
+
status: "pending",
|
|
625
|
+
requestCode: generateCanonicalRequestCode(),
|
|
626
|
+
expiresAt: Date.now() + 5 * 60 * 1000,
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
if (trustContext && conversation) {
|
|
630
|
+
bridgeConfirmationRequestToGuardian({
|
|
631
|
+
canonicalRequest,
|
|
632
|
+
trustContext,
|
|
633
|
+
conversationId,
|
|
634
|
+
toolName: msg.toolName,
|
|
635
|
+
assistantId: conversation.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
} catch (err) {
|
|
639
|
+
log.debug(
|
|
640
|
+
{ err, conversationId },
|
|
641
|
+
"Failed to create canonical request from broadcast",
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// ── Feed events for confirmation requests ─────────────────────────────────────
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Emit a feed event when a confirmation request (tool approval prompt) is
|
|
650
|
+
* broadcast. Emits immediately with a technical preview, then rewrites
|
|
651
|
+
* into prose in the background and updates the feed item.
|
|
652
|
+
*/
|
|
653
|
+
async function emitConfirmationFeedEvent(
|
|
654
|
+
msg: ServerMessage & { type: "confirmation_request" },
|
|
655
|
+
conversationId: string,
|
|
656
|
+
): Promise<void> {
|
|
657
|
+
try {
|
|
658
|
+
const inputRecord = msg.input as Record<string, unknown>;
|
|
659
|
+
const commandPreview =
|
|
660
|
+
redactSecrets(summarizeToolInput(msg.toolName, inputRecord)) || undefined;
|
|
661
|
+
const technicalTitle = commandPreview
|
|
662
|
+
? `Requesting permission: ${commandPreview}`
|
|
663
|
+
: `Requesting approval to use ${msg.toolName}.`;
|
|
664
|
+
const dedupKey = `tool-approval:${msg.requestId}`;
|
|
665
|
+
|
|
666
|
+
await emitFeedEvent({
|
|
667
|
+
source: "assistant",
|
|
668
|
+
title: technicalTitle,
|
|
669
|
+
summary: technicalTitle,
|
|
670
|
+
dedupKey,
|
|
671
|
+
urgency: msg.riskLevel === "high" ? "high" : "medium",
|
|
672
|
+
conversationId,
|
|
673
|
+
detailPanel: { kind: "toolPermission" },
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Background: rewrite into prose and update the feed item.
|
|
677
|
+
if (commandPreview) {
|
|
678
|
+
const prose = await rewriteCommandPreview(msg.toolName, commandPreview);
|
|
679
|
+
if (prose) {
|
|
680
|
+
const proseTitle = `Requesting permission: ${prose}`;
|
|
681
|
+
await emitFeedEvent({
|
|
682
|
+
source: "assistant",
|
|
683
|
+
title: proseTitle,
|
|
684
|
+
summary: proseTitle,
|
|
685
|
+
dedupKey,
|
|
686
|
+
urgency: msg.riskLevel === "high" ? "high" : "medium",
|
|
687
|
+
conversationId,
|
|
688
|
+
detailPanel: { kind: "toolPermission" },
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
} catch (err) {
|
|
693
|
+
log.warn({ err }, "Failed to emit confirmation feed event from broadcast");
|
|
694
|
+
}
|
|
695
|
+
}
|