feat: add queue-if-busy behavior and hub publishing to POST /v1/messages#8400
Conversation
…ges (#8391) Co-authored-by: Claude <noreply@anthropic.com>
* fix: reset activatedViaWakeWord flag on voice mode activation failure (#8180) Co-authored-by: Claude <noreply@anthropic.com> * fix: use effective base URL for polling and cancel task on dismiss (#8185) Co-authored-by: Claude <noreply@anthropic.com> * fix: derive segment count from manifest in media diagnostics (#8187) Co-authored-by: Claude <noreply@anthropic.com> * fix: enforce payload size limit on pairing proxy endpoints (#8188) Co-authored-by: Claude <noreply@anthropic.com> * fix: restore approvalConversationGenerator in RuntimeHttpServer (#8189) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: approved devices race condition and optimistic clear (#8190) Co-authored-by: Claude <noreply@anthropic.com> * fix: move zero-frame check before atomic rename in preprocess (#8191) Co-authored-by: Claude <noreply@anthropic.com> * fix: include context field in map cache config hash (#8192) Co-authored-by: Claude <noreply@anthropic.com> * fix: align lastPairedAt type to Int matching generated IPC types (#8193) Co-authored-by: Claude <noreply@anthropic.com> * improve Gmail OAuth setup UX with auto-detection and clearer messaging (#8194) Rewrite the Google OAuth setup skill prompts to feel like 3 user actions instead of a confusing 10-step process. Auto-detect sign-in completion by polling browser snapshots instead of asking users to confirm manually. Add progress callouts and clearer credential entry instructions. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: reset activatedViaWakeWord flag outside wakeWordEnabled guard (#8195) Co-authored-by: Claude <noreply@anthropic.com> * fix: send deny on PairingApprovalWindow close and supersede (#8196) When the pairing approval window is closed via the X button or superseded by a new request, sends a deny response to the daemon. Prevents iOS devices from hanging in pending state indefinitely. - Track currentPairingRequestId and responseSent flag - Add WindowCloseDelegate (NSWindowDelegate) for X-button close - denyIfNeeded() sends deny for unanswered requests in close() - Set responseSent = true in onDecision before calling close() Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: clear stale pairing overrides during v4 migration (#8197) Co-authored-by: Claude <noreply@anthropic.com> * fix: re-register QR pairing before TTL and handle missing daemon (#8199) Adds a timer to re-register the pairing request before the 5-minute TTL expires, keeping the QR code valid while the sheet is open. Also shows an error when daemon client is unavailable instead of rendering an empty sheet. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: honor task cancellation in QRPairingSheet pairing flow (#8210) Co-authored-by: Claude <noreply@anthropic.com> * fix: enforce Content-Length pre-check in pairing proxy (#8212) Co-authored-by: Claude <noreply@anthropic.com> * fix: roll back optimistic removal in removeApprovedDevice on IPC failure (#8215) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: reduce maxTokens default from 64000 to 16000 (#8217) Co-authored-by: Claude <noreply@anthropic.com> * feat: add streamThinking config flag and filter thinking deltas (#8219) Co-authored-by: Claude <noreply@anthropic.com> * fix: skip deny on same-ID retry in PairingApprovalWindow (#8223) Co-authored-by: Claude <noreply@anthropic.com> * fix: update maxTokens schema default and tests to 16000 (#8224) Co-authored-by: Claude <noreply@anthropic.com> * fix: separate iOS override migration and guard macOS cleanup (#8225) iOS: moves override cleanup into its own migratePairingOverridesIfNeeded() with a separate migration key, so existing v4-migrated users still get cleanup. macOS: only runs cleanup when legacy iosPairingUseOverride key is actually present, preserving intentional post-M9 overrides. Addresses feedback from PR #8197. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: avoid QR flicker during refresh and register on daemon connect (#8227) Keeps old QR visible during re-registration by only swapping credentials atomically on HTTP 200 success. Adds .onChange(of: daemonClient) to trigger registration when daemon becomes available after sheet opens. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle floating promise and add timeout in title generation (#8228) Co-authored-by: Claude <noreply@anthropic.com> * refactor: deduplicate isPlainObject into shared utility (#8229) Co-authored-by: Claude <noreply@anthropic.com> * perf: add database indexes on memorySegments table (#8230) Co-authored-by: Claude <noreply@anthropic.com> * feat: add backpressure and metrics to session message queue (#8231) Co-authored-by: Claude <noreply@anthropic.com> * perf: cache shell-parsing and risk classification results (#8232) Co-authored-by: Claude <noreply@anthropic.com> * security: add pino log serializer to scrub sensitive data (#8233) Co-authored-by: Claude <noreply@anthropic.com> * refactor: replace unsafe type assertions with proper type guards (#8234) Co-authored-by: Claude <noreply@anthropic.com> * feat: add circuit breaker to gateway runtime client (#8236) Co-authored-by: Claude <noreply@anthropic.com> * refactor: create centralized environment variable registry (#8235) Co-authored-by: Claude <noreply@anthropic.com> * feat: add status indication to macOS menu bar icon (#8237) Co-authored-by: Claude <noreply@anthropic.com> * refactor: replace any types with proper interfaces in test files (#8241) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove iosPairingUseOverride deletion from v4 migration (#8239) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove Porcupine iOS-only SPM dep that breaks Xcode 26.2 build (#8240) Porcupine's SPM package is iOS-only and pulls in ios-voice-processor which uses AVAudioSession (unavailable on macOS). Xcode 26.2 is now strict about this. The wake word engine is currently a stub so removing the dep is safe. Also fixes missing VellumAssistantShared imports and a stale protocol conformance in the wake word files. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * perf: add database indexes on memoryItems table (#8244) Co-authored-by: Claude <noreply@anthropic.com> * fix: add per-surface serialization to prevent race conditions (#8245) Co-authored-by: Claude <noreply@anthropic.com> * feat: source filter for Available Skills + platform catalog API (#8097) * feat: add source filter to Available Skills and fetch catalog from platform API - Add All/Vellum/Community source filter pills to Available Skills tab - Capitalize "Vellum" in empty state, hide community disclaimer for Vellum filter - Show Vellum catalog skills even if installed (with "Installed" label) - Add refresh button to Installed tab empty state - Switch catalog fetch from GitHub raw URL to platform API (/v1/skills/) - Pass platform session token (X-Session-Token) through IPC for authenticated fetch - Regenerate IPC contract with optional sessionToken on SkillsSearchRequest Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve platform token locally and fetch skill content via tar API - Remove sessionToken from IPC contract and Swift chain; the daemon now reads the platform API token from ~/.vellum/platform-token instead of receiving it via IPC messages - SessionTokenManager writes platform token to disk when set/deleted so the daemon can read it for authenticated platform API calls - Implement fetchSkillContent via platform tar API (GET /v1/skills/{id}/) which returns a tar.gz archive; extracts SKILL.md from the tarball with bundled fallback on failure - Add readPlatformToken()/getPlatformTokenPath() to platform.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: add AbortSignal support to tool implementations (#8246) Co-authored-by: Claude <noreply@anthropic.com> * refactor: split http-server.ts into focused middleware and route modules (#8243) Co-authored-by: Claude <noreply@anthropic.com> * fix: handle QR refresh failure and remove dead onChange (#8248) Shows error state after 2 consecutive refresh registration failures instead of silently leaving a stale QR. Removes dead .onChange(of: daemonClient != nil) since daemonClient is always non-nil in production. Addresses feedback from PR #8227. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add response style section, trim verbose system prompt sections, strengthen SOUL.md conciseness (#8249) * security: enforce 0o600 permissions on log files (#8252) Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate AbortSignal through permission checking (#8253) Co-authored-by: Claude <noreply@anthropic.com> * feat: complete ingress config schema with validation (#8254) Co-authored-by: Claude <noreply@anthropic.com> * perf: add database indexes on remaining tables (#8255) Co-authored-by: Claude <noreply@anthropic.com> * feat(macos): add secret dev mode toggle (#8226) * feat(macos): add secret dev mode toggle via Assistant ID tap gesture Tap the Assistant ID 7 times in Settings > Advanced to toggle dev mode, similar to Android's developer options. Dev mode gates the Feature Flags editor, Developer (env vars) section, and Platform URL in Connect tab. State persists in UserDefaults. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: default dev mode to enabled in DEBUG builds Uses `UserDefaults.object(forKey:) as? Bool ?? true` so debug builds start with dev mode on, while still respecting an explicit toggle-off. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: reset refresh timer and failure counter on QR retry (#8257) Resets consecutiveRefreshFailures to 0 and restarts the refresh timer when the user clicks Retry after a QR refresh failure. Without this, the QR would silently expire after 5 minutes and the next single failure would immediately re-trigger the error state. Addresses feedback from PR #8248. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * refactor: split lifecycle.ts into focused modules (#8259) Co-authored-by: Claude <noreply@anthropic.com> * feat: make global hotkey configurable in macOS client (#8258) Add keyboard shortcut settings UI, UserDefaults persistence, and dynamic re-registration. Defaults to Cmd+Shift+G. * feat: add structured error serialization to pino logger (#8260) Co-authored-by: Claude <noreply@anthropic.com> * fix: add numeric bounds validation for memory item confidence/importance (#8261) Co-authored-by: Claude <noreply@anthropic.com> * feat: make hardcoded daemon timeout values configurable (#8265) Co-authored-by: Claude <noreply@anthropic.com> * feat: add Zod schema for message metadata validation (#8268) Co-authored-by: Claude <noreply@anthropic.com> * refactor: separate migration code from platform utilities (#8272) Co-authored-by: Claude <noreply@anthropic.com> * refactor: use consistent pino logging throughout migration and startup code (#8273) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve all CI type-check and lint errors (#8276) - Add missing streamThinking to ThinkingConfigSchema default and test assertions - Fix boolean | undefined return type in contradiction-checker transaction - Wrap media-processing stage handlers to return Promise<void> - Cast Cron through unknown for Record<string, unknown> conversion - Update AgentLoopRun type to accept CheckpointInfo parameter - Add withSurface to ToolSetupContext test stubs - Add sessionId to ToolContext test stubs - Cast input_schema to typed interface for property access in tests - Fix fetch mock casts through unknown for Bun's preconnect property - Remove unused imports/vars and fix lint violations across 14 files Co-authored-by: Claude <noreply@anthropic.com> * feat(macos): add health check indicator to Platform URL row (#8271) * feat(macos): add health check indicator to Platform URL row Show a dynamic status icon on the Platform URL row in Settings > Connect that indicates whether the Vellum platform is reachable. Hits /healthz on appear and displays green checkmark (reachable), red xmark (unreachable), or spinner (checking). Error details shown on hover via tooltip. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: gate platform health check on dev mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: move response style directives to SOUL.md, remove buildResponseStyleSection (#8277) * Guardian Cross-Channel Approval UX Polish (#8208) * M1: Backend generative guardian copy + emoji title (#8117) * feat: replace static guardian thread copy with model-generated copy Co-Authored-By: Claude <noreply@anthropic.com> * fix: avoid blocking external channel dispatch on LLM copy generation (#8122) Co-authored-by: Claude <noreply@anthropic.com> * fix: create macOS delivery row before awaiting LLM copy generation (#8127) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M2: macOS native notification + deep link to thread (#8147) * feat: add macOS native notification + deep link for guardian requests Co-Authored-By: Claude <noreply@anthropic.com> * fix: add questionText to guardian IPC message for notification body (#8162) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M3: QA hardening + documentation (#8179) * test: add guardian copy generation tests and update architecture docs Co-Authored-By: Claude <noreply@anthropic.com> * test: add dedicated test for real buildFallbackCopy implementation (#8198) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * Fix: reject non-emoji guardian titles from model output (#8247) * fix: validate emoji prefix in generated guardian title Co-Authored-By: Claude <noreply@anthropic.com> * fix: only reject old Guardian question prefix, don't enforce emoji (#8251) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * refactor: remove assistant inbox feature flag gating from desktop UI (#8281) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: add provider abstraction and approval resilience rules to AGENTS.md (#8279) Co-authored-by: Claude <noreply@anthropic.com> * refactor: remove assistantInbox config schema and defaults (#8282) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * refactor: make ingress ACL enforcement always-on (remove feature flag gating) (#8283) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: add rule for agents to keep AGENTS.md up to date (#8284) Co-authored-by: Claude <noreply@anthropic.com> * refactor: remove assistantInbox feature-flag check from ingress ACL enforcement (#8287) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: update inbox docs to reflect always-on behavior (#8293) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: change private to internal for properties accessed from AppDelegate extension (#8294) connectionStatusCancellable, pulseTimer, and pulsePhase are accessed from AppDelegate+MenuBar.swift, which is a separate file. Private restricts access to the declaring file, so these need internal access. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: increase maxInputTokens from 180k to 200k (#8295) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve CI type-check and lint errors in ingress config and imports (#8296) Co-authored-by: Claude <noreply@anthropic.com> * fix: use Chrome debugger API for CSP-bypass eval, fix Swift Result<Void,String> build error, add influencer CLI command (#8297) Co-authored-by: marinatrajk <marina@odyseek.com> Co-authored-by: Claude <noreply@anthropic.com> * refactor: remove QuickChat feature (#8300) The QuickChat floating panel had an unfixable input focus bug — the NSPanel couldn't reliably receive keyboard input. Remove the entire feature: panel, view, hotkey monitors, notification category, settings shortcut, and background session helpers. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove stale relayPort from storage when port field is cleared (#8301) Co-authored-by: marinatrajk <marina@odyseek.com> Co-authored-by: Claude <noreply@anthropic.com> * fix(macos): use consistent paragraph style for placeholder height measurement (#8303) Co-authored-by: Claude <noreply@anthropic.com> * fix(macos): resolve CI build errors in settings views (#8306) - Remove #if DEBUG guard from SettingsPanelEnvVarsSheet so it compiles in release builds (already gated by dev mode at runtime) - Use explicit .some(true)/.some(false)/.none patterns in SettingsConnectTab switch statements to satisfy exhaustiveness checker Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: make error message bubbles span full chat width (#8312) Error bubbles were constrained to the same 520pt maxWidth as regular messages. Use .infinity for error messages so they fill the available chat area. Co-authored-by: Claude <noreply@anthropic.com> * feat: add Quick Input bar (Cmd+/) (#8309) * feat: add Quick Input bar (Cmd+/) for fast new thread creation Adds a floating Spotlight-style input bar that appears system-wide via Cmd+/ (Carbon RegisterEventHotKey). Type a message, hit Return, and it opens the main window with a new thread containing that message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review — prevent handler leak, fix multi-monitor, no-flash on submit - Track and remove Carbon event handler on teardown to prevent accumulation - Place Quick Input panel on the screen with the mouse cursor - Skip restoring previous app on submit so main window doesn't flash Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: don't restore previous app on resign-key dismissal Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix(lint): resolve lint errors in influencer client (#8316) - Replace `as any` with proper `Omit<ExtensionCommand, 'id'>` type cast - Change InfluencerProfile nullable fields from `| null` to `| undefined` - Update parseFollowerCount return type from `number | null` to `number | undefined` - Replace all `!== null` checks with `!== undefined` per project convention Co-authored-by: Claude <noreply@anthropic.com> * feat: request 16kHz mono format in AlwaysOnAudioMonitor audio tap (#8318) Co-authored-by: Claude <noreply@anthropic.com> * feat: add PorcupineBinding.swift — dlopen wrapper for Porcupine C API (#8320) Co-authored-by: Claude <noreply@anthropic.com> * feat: bundle Porcupine dylib, model, and keywords in build.sh (#8321) Co-authored-by: Claude <noreply@anthropic.com> * fix: always start daemon HTTP server for iOS pairing (#8322) The daemon's HTTP server (port 7821) is required for iOS pairing — the gateway proxies all iOS traffic through it. Previously this was gated behind the `localHttpEnabled` feature flag, so the daemon never started its HTTP server unless the flag was manually enabled via env var, causing HTTP 502 errors on iOS after QR pairing. Three fixes: - AppDelegate: always set RUNTIME_HTTP_PORT=7821 (remove flag gate) - AssistantCli: always forward RUNTIME_HTTP_PORT to CLI (remove flag gate) - CLI local.ts: add RUNTIME_HTTP_PORT to the daemon env forwarding list (the CLI was building a second minimal env and stripping it out) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: replace PorcupineWakeWordEngine stub with real Porcupine C SDK implementation (#8323) Co-authored-by: Claude <noreply@anthropic.com> * feat: add wake word keyword selection and fix APIKeyManager usage (#8324) Co-authored-by: Claude <noreply@anthropic.com> * fix(swift): fix IPC Unix socket protocol and reconnect on cancel (#8325) * fix(swift): widen access control for AppDelegate+MenuBar extension and fix Result<Void, String> error Properties accessed by AppDelegate+MenuBar.swift (in a separate file) need internal access — private restricts to the declaring file. Also wrap registration error strings in a proper Error-conforming type since Result requires its Failure type to conform to Error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(macos): resolve QR pairing issues with httpPort fallback and LAN gateway - Add resolvedHttpPort computed property that falls back to RUNTIME_HTTP_PORT env var, then default 7821, when daemonClient.httpPort is nil (happens when daemon HTTP server starts after socket connection during Qdrant timeout) - Add effectiveGatewayUrl that falls back to localLanUrl when no cloud gateway is configured, enabling LAN-only pairing - Update canGenerateQR, registration body, and QR payload to use effectiveGatewayUrl instead of gatewayUrl - Update hasGateway check to include LAN availability Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: always start daemon HTTP server for iOS pairing The daemon HTTP server was gated behind RUNTIME_HTTP_PORT env var, which relied on a fragile setenv → getenv → subprocess forwarding chain from the macOS app. ProcessInfo.processInfo.environment is a frozen snapshot from launch, so the env var often wasn't forwarded, causing the HTTP server to never start and iOS pairing requests to fail silently. Three fixes: - getRuntimeHttpPort() now defaults to 7821, so the HTTP server always starts regardless of env var forwarding - Wire setPairingBroadcast so pairing_approval_request IPC messages actually reach the macOS app - Remove conditional guard around HTTP server startup in lifecycle.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(macos): add loading state during token regeneration Show a spinner and disable the QR button while the daemon restarts after bearer token regeneration. Polls the healthz endpoint for up to 30s to avoid showing a dead-end "daemon unreachable" error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(swift): fix IPC Unix socket protocol and reconnect on cancel Two issues caused the macOS app to disconnect from the daemon after ~1 second and never reconnect: 1. DaemonConnection configured NWProtocolTCP.Options() on a Unix domain socket endpoint. Unix sockets don't use TCP — the invalid protocol stack caused the NWConnection to become unstable and disconnect shortly after connecting, before authentication could complete. 2. The .cancelled state handler didn't call scheduleReconnect() when the connection had already been established (resumed==true). This meant spontaneous disconnects were permanent — unlike .failed which properly triggers auto-reconnect. Without a stable IPC connection, all daemon broadcasts (pairing approval requests, reminders, schedule events, watcher alerts) were silently dropped because there were 0 authenticated sockets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: use AVAudioConverter for 16kHz resampling instead of custom tap format (#8326) Co-authored-by: Claude <noreply@anthropic.com> * fix: move FRAMEWORKS_DIR definition before Porcupine staleness check in build.sh (#8328) Co-authored-by: Claude <noreply@anthropic.com> * fix: add input validation and handle cleanup in PorcupineBinding (#8327) Co-authored-by: Claude <noreply@anthropic.com> * fix: prevent double dismiss of Quick Input panel on submit (#8315) Add isDismissing guard to prevent re-entrant dismiss calls when showMainWindow causes the panel to resign key while the onSubmit closure also calls dismiss explicitly. Co-authored-by: Claude <noreply@anthropic.com> * Release v0.3.6 (#8329) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix: move wake word callback outside lock and use dynamic frame length (#8331) Co-authored-by: Claude <noreply@anthropic.com> * fix: prevent duplicate audio in AVAudioConverter and use ceil for buffer capacity (#8332) Co-authored-by: Claude <noreply@anthropic.com> * perf: disable LLM reranking for memory recall (#8067) LLM reranking was calling Claude Haiku API on every message to re-score memory candidates, adding ~2.2s latency. The RRF merge already produces a well-ordered list from lexical, semantic, recency, and entity scores. Disabling reranking reduces memory recall from ~2.6s to ~700ms. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix(swift): restore NWProtocolTCP on Unix socket, keep cancelled reconnect (#8340) The previous commit removed NWProtocolTCP.Options() from Unix socket connections, but this protocol has been in use since the original IPC implementation (PR #499) and has been working in production for years. Removing it risks breaking IPC in the v0.3.7 release. Restores the TCP protocol stack while keeping the .cancelled state reconnect improvement (scheduleReconnect on post-connection cancel). Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: add explicit self. for property captures in PorcupineWakeWordEngine (#8337) Swift strict concurrency requires explicit `self.` when referencing properties inside closures. os.Logger string interpolation creates implicit closures, so `keyword` and `sensitivity` need `self.` prefix. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: add explicit self for keyword reference in os.Logger closure (#8342) The release build (universal binary) requires explicit `self.` for property references inside os.Logger string interpolation closures. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve CI failures blocking v0.3.7 release (#8344) - TypeScript: fix setPairingBroadcast type to use ServerMessage instead of loose { type: string; [key: string]: unknown } - Gateway lint: prefix unused vars with _ in whatsapp-deliver test - Swift: add explicit self. for keyword property in closure Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: broadcast pairing approval to HTTP/SSE clients (#8348) When the macOS app uses HTTP transport (localHttpEnabled flag), it connects via SSE instead of the Unix domain socket. Pairing approval requests were only broadcast to IPC socket clients, so HTTP-connected clients never received them. Two changes: 1. RuntimeHttpServer now also publishes pairing events to the AssistantEventHub with assistantId 'self' so SSE subscribers receive them. 2. AssistantEventHub allows events without sessionId (system events) to pass through to all subscribers regardless of their sessionId filter. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat(macos): add Restart menu item to status bar menu (#8347) * feat(macos): add Restart menu item to status bar menu Add a Restart option to the menu bar dropdown that stops the daemon, launches a fresh app instance, and terminates the current one. Useful for recovering from stuck states without manually quitting and relaunching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: gate app termination on successful relaunch in performRestart Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add memory degradation banner to component gallery with #F5F3EB background (#8356) Co-authored-by: Claude <noreply@anthropic.com> * Add rename option to thread right-click context menu (#8351) * M1: Add session_rename IPC message to daemon (#8338) * feat: add session_rename IPC message for client-initiated title changes Co-Authored-By: Claude <noreply@anthropic.com> * fix: validate conversation exists before renaming in handleSessionRename (#8341) Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> * M2: Add rename option to macOS thread context menu UI (#8346) * feat: add rename option to macOS thread context menu Co-Authored-By: Claude <noreply@anthropic.com> * fix: only show rename option for threads with a daemon session (#8350) Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> * fix: remove redundant conversation_id index on memory_segments (#8357) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove unscoped queue-full error and guard expireStale against exceptions (#8358) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve statusCode fallback when status is undefined in getErrorStatusCode (#8360) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve Error name/message/stack in log redaction serializer (#8359) Co-authored-by: Claude <noreply@anthropic.com> * fix: include workingDir and manifestOverride in risk cache key (#8361) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove redundant fingerprint index from memoryItems migration (#8362) Co-authored-by: Claude <noreply@anthropic.com> * fix: add missing legitimate env vars to KNOWN_VELLUM_VARS (#8363) Co-authored-by: Claude <noreply@anthropic.com> * fix: clean up settled surface mutex entries to prevent memory leak (#8364) Co-authored-by: Claude <noreply@anthropic.com> * fix: return immediately on aborted CAPTCHA wait instead of breaking (#8365) Co-authored-by: Claude <noreply@anthropic.com> * fix: rebind menu bar connection observer after client replacement and fix pulse animation (#8366) Co-authored-by: Claude <noreply@anthropic.com> * fix: limit half-open probes to single attempt and propagate CircuitBreakerOpenError (#8367) Co-authored-by: Claude <noreply@anthropic.com> * fix: read daemon timeouts without triggering loadConfig/migration side effects (#8368) Co-authored-by: Claude <noreply@anthropic.com> * fix: enforce log file permissions on existing files at startup (#8371) Co-authored-by: Claude <noreply@anthropic.com> * fix: auto-start SSE on HTTP transport connect for system events (#8370) When using HTTP transport (VELLUM_FLAG_LOCAL_HTTP_ENABLED=1), SSE was only started when MainWindowView.onAppear fired. This meant system events like pairing approval requests were lost if they arrived before the main window appeared. Auto-start the SSE stream immediately after the first health check passes in connect(). MainWindowView.onAppear still calls startSSE() but it's a no-op when the stream is already running. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: throw typed cancellation error from permission checks (#8372) Co-authored-by: Claude <noreply@anthropic.com> * feat: add pre-commit warning when modifying system-prompt.ts (#8354) * feat: add pre-commit warning when modifying system-prompt.ts Adds a non-blocking warning to the pre-commit hook that fires when system-prompt.ts is staged. Reminds contributors to consider whether their change belongs in a skill, IDENTITY.md, SOUL.md, USER.md, LOOKS.md, or skills/ instead of directly in the system prompt. Includes a 3-second pause to ensure the warning is actually read. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add BOOTSTRAP.md to system-prompt warning file list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * Release v0.3.7 (#8373) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix: initialize pairing handlers so approval responses are processed (#8374) initPairingHandlers() was defined but never called, leaving pairingStoreRef as null. When macOS sent a pairing_approval_response over IPC, the handler silently returned on the null check (line 29), so approvals never reached the PairingStore — iOS stayed stuck on "Waiting for approval" and the device was never added to the allowlist. Wire up initPairingHandlers() in lifecycle.ts after the HTTP server starts, passing the PairingStore instance and bearer token. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: improve media analysis skill defaults and add best practices (#8385) Update defaults based on real-world usage feedback: keyframe interval 3s→1s, segment duration 20s→15s, skip_dead_time on→off. Add best practices section with broad-vs-targeted map prompt guidance, sample prompt/schema, clip delivery notes, and vision analysis limitations. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add holistic Codex review phase to safe-blitz command (#8387) Co-authored-by: Claude <noreply@anthropic.com> * fix: watch http-token file so gateway picks up daemon token changes (#8389) When the daemon restarts and writes a new bearer token to ~/.vellum/http-token, a gateway process that started earlier still holds the stale token. This causes 401 errors for iOS clients that received the new token during pairing. Add a file watcher for http-token (following the existing CredentialWatcher pattern) that refreshes runtimeBearerToken, runtimeProxyBearerToken, and runtimeGatewayOriginSecret in the live config when the file changes. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: drop legacy conversation_id index and remove from Drizzle schema (#8393) Co-authored-by: Claude <noreply@anthropic.com> * fix: unreserve dedup cache entries on CircuitBreakerOpenError before returning 503 (#8394) Co-authored-by: Claude <noreply@anthropic.com> * fix: validate daemon timeout bounds in readDaemonTimeouts (#8395) Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate queue-full status through subagent message path (#8397) Co-authored-by: Claude <noreply@anthropic.com> * fix: respect env var precedence in http-token watcher (#8399) Address review feedback on #8389: 1. Skip file-based token refresh when RUNTIME_BEARER_TOKEN env var is set, preserving the same precedence as loadConfig() where env vars override the file. Prevents cloud deployments with pinned tokens from being silently overwritten by daemon file writes. 2. Wrap mkdirSync in try-catch so a non-writable parent directory doesn't crash the gateway at startup — the watcher is gracefully skipped instead. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add queue-if-busy behavior and hub publishing to POST /v1/messages (#8391) (#8400) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Alex Nork <48630278+alex-nork@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Ashlee Radka <ashleeradka@gmail.com> Co-authored-by: asharma53 <64060709+asharma53@users.noreply.github.com> Co-authored-by: siddseethepalli <siddseethepalli@gmail.com> Co-authored-by: David Vargas Fuertes <vargas@vellum.ai> Co-authored-by: NgoHarrison <harrison.ngo719@gmail.com> Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Aaron Levin <awlevin@users.noreply.github.com> Co-authored-by: V <vincent@vellum.ai> Co-authored-by: Marina Trajkovska <trajk.marina@gmail.com> Co-authored-by: marinatrajk <marina@odyseek.com> Co-authored-by: vellum-automation[bot] <192048195+vellum-automation[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Jason Zhou <jason@vellum.ai> Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Tirman Sidhu <tirmansidhu@gmail.com> Co-authored-by: Nick <127171085+ZeebBoyBlue@users.noreply.github.com> Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local>
| if (deps.sendMessageDeps) { | ||
| const smDeps = deps.sendMessageDeps; | ||
| const session = await smDeps.getOrCreateSession(mapping.conversationId); | ||
| const onEvent = makeHubPublisher(smDeps, mapping.conversationId); | ||
|
|
||
| const attachments = hasAttachments | ||
| ? smDeps.resolveAttachments(attachmentIds) | ||
| : []; | ||
|
|
||
| if (session.isProcessing()) { | ||
| // Queue the message so it's processed when the current turn completes | ||
| const requestId = crypto.randomUUID(); | ||
| const result = session.enqueueMessage( | ||
| content ?? '', | ||
| attachments, | ||
| onEvent, | ||
| requestId, | ||
| ); | ||
| if (result.rejected) { | ||
| return Response.json( | ||
| { error: 'Message queue is full. Please retry later.' }, | ||
| { status: 429 }, | ||
| ); | ||
| } | ||
| return Response.json({ accepted: true, queued: true }, { status: 202 }); | ||
| } | ||
|
|
||
| // Session is idle — persist and fire agent loop immediately | ||
| const requestId = crypto.randomUUID(); | ||
| const messageId = session.persistUserMessage(content ?? '', attachments, requestId); | ||
|
|
||
| // Fire-and-forget the agent loop; events flow to the hub via onEvent | ||
| session.runAgentLoop(content ?? '', messageId, onEvent).catch((err) => { | ||
| log.error({ err, conversationId: mapping.conversationId }, 'Agent loop failed (POST /messages)'); | ||
| }); | ||
|
|
||
| return Response.json({ accepted: true, messageId }, { status: 202 }); |
There was a problem hiding this comment.
🔴 New sendMessageDeps path bypasses secret ingress check, allowing secrets to reach model context
The new sendMessageDeps code path in handleSendMessage skips the checkIngressForSecrets check that every other message ingress path performs. When secretDetection.blockIngress is enabled, user messages containing detected secrets (API keys, passwords, etc.) will be persisted and sent to the LLM without being blocked.
Security Impact and Root Cause
Both persistAndProcessMessage (assistant/src/daemon/server.ts:690-692) and processMessage (assistant/src/daemon/server.ts:737-739) call checkIngressForSecrets(content) before processing the message:
const ingressCheck = checkIngressForSecrets(content);
if (ingressCheck.blocked) {
throw new IngressBlockedError(ingressCheck.userNotice!, ingressCheck.detectedTypes);
}The new sendMessageDeps path at assistant/src/runtime/routes/conversation-routes.ts:243-279 directly calls session.persistUserMessage() and session.runAgentLoop() without any secret scanning. This means any secret embedded in user content will be persisted to the database and sent to the LLM provider, defeating the secret detection security feature.
Impact: Secrets (API keys, passwords, tokens) in user messages bypass the ingress filter and are sent to the LLM provider, potentially leaking sensitive credentials.
Prompt for agents
In assistant/src/runtime/routes/conversation-routes.ts, add a secret ingress check at the beginning of the sendMessageDeps code path (around line 243, after the `if (deps.sendMessageDeps)` check). Import `checkIngressForSecrets` from '../../security/secret-ingress.js' and add:
const ingressCheck = checkIngressForSecrets(content ?? '');
if (ingressCheck.blocked) {
return Response.json(
{ error: ingressCheck.userNotice ?? 'Message blocked by secret detection' },
{ status: 400 },
);
}
This should be placed before the session.isProcessing() check so that secrets are blocked regardless of whether the message would be queued or processed immediately.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const smDeps = deps.sendMessageDeps; | ||
| const session = await smDeps.getOrCreateSession(mapping.conversationId); | ||
| const onEvent = makeHubPublisher(smDeps, mapping.conversationId); | ||
|
|
||
| const attachments = hasAttachments | ||
| ? smDeps.resolveAttachments(attachmentIds) | ||
| : []; | ||
|
|
||
| if (session.isProcessing()) { | ||
| // Queue the message so it's processed when the current turn completes | ||
| const requestId = crypto.randomUUID(); | ||
| const result = session.enqueueMessage( | ||
| content ?? '', | ||
| attachments, | ||
| onEvent, | ||
| requestId, | ||
| ); | ||
| if (result.rejected) { | ||
| return Response.json( | ||
| { error: 'Message queue is full. Please retry later.' }, | ||
| { status: 429 }, | ||
| ); | ||
| } | ||
| return Response.json({ accepted: true, queued: true }, { status: 202 }); | ||
| } | ||
|
|
||
| // Session is idle — persist and fire agent loop immediately | ||
| const requestId = crypto.randomUUID(); | ||
| const messageId = session.persistUserMessage(content ?? '', attachments, requestId); | ||
|
|
||
| // Fire-and-forget the agent loop; events flow to the hub via onEvent | ||
| session.runAgentLoop(content ?? '', messageId, onEvent).catch((err) => { | ||
| log.error({ err, conversationId: mapping.conversationId }, 'Agent loop failed (POST /messages)'); | ||
| }); | ||
|
|
There was a problem hiding this comment.
🔴 New sendMessageDeps path does not configure session properties (channelCapabilities, assistantId, turnChannelContext)
The new sendMessageDeps code path skips critical session configuration that the legacy paths perform before persisting and processing a message. This causes the agent loop to run without channel capabilities, assistant ID, or turn channel context.
Missing Session Configuration Details
Both persistAndProcessMessage and processMessage in assistant/src/daemon/server.ts:701-709 configure the session before running the agent loop:
session.setAssistantId(options?.assistantId ?? 'self');
session.setGuardianContext(options?.guardianContext ?? null);
session.setChannelCapabilities(resolveChannelCapabilities(sourceChannel));
session.setCommandIntent(options?.commandIntent ?? null);
const resolvedChannel = resolveTurnChannel(sourceChannel, options?.transport?.channelId);
session.setTurnChannelContext({
userMessageChannel: resolvedChannel,
assistantMessageChannel: resolvedChannel,
});The new path at assistant/src/runtime/routes/conversation-routes.ts:243-279 calls session.persistUserMessage() and session.runAgentLoop() without any of these configuration calls. This means:
channelCapabilitiesis not set, so the agent loop may not know what the channel supports (e.g., rich formatting, inline buttons)assistantIdis not set to'self', leaving it as whatever value it had from a previous turn (or undefined)turnChannelContextis not set, sopersistUserMessagewon't record the channel metadata on the persisted message, andconversationStore.setConversationOriginChannelIfUnsetwon't be called
Impact: Messages processed via the new path will lack proper channel context, potentially causing incorrect formatting of assistant responses and missing origin channel tracking. The sourceChannel parsed from the request body (line 193) is validated but never used in the new path.
Prompt for agents
In assistant/src/runtime/routes/conversation-routes.ts, after obtaining the session on line 245 and before the isProcessing() check on line 252, add session configuration calls similar to what the legacy paths do in assistant/src/daemon/server.ts:701-709. You will need to:
1. Import resolveChannelCapabilities and resolveTurnChannel from the appropriate module (check server.ts imports)
2. Add the following configuration before line 252:
session.setAssistantId('self');
session.setChannelCapabilities(resolveChannelCapabilities(sourceChannel));
const resolvedChannel = resolveTurnChannel(sourceChannel);
session.setTurnChannelContext({
userMessageChannel: resolvedChannel,
assistantMessageChannel: resolvedChannel,
});
Note: sourceChannel is already parsed and validated earlier in the function (line 193) but is never used in the sendMessageDeps path. The same configuration should also be applied for the queued message path, possibly by passing metadata to enqueueMessage so the session can be configured when the queued message is eventually processed.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ea4638a65a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (deps.sendMessageDeps) { | ||
| const smDeps = deps.sendMessageDeps; | ||
| const session = await smDeps.getOrCreateSession(mapping.conversationId); | ||
| const onEvent = makeHubPublisher(smDeps, mapping.conversationId); |
There was a problem hiding this comment.
Restore ingress secret checks for POST /v1/messages
When sendMessageDeps is present, this new branch bypasses DaemonServer.persistAndProcessMessage and calls session.persistUserMessage/enqueueMessage directly, so checkIngressForSecrets(...) is never executed for /v1/messages. Because daemon/lifecycle.ts now always wires sendMessageDeps, requests that include secrets (with default secretDetection.enabled=true and blockIngress=true) will be accepted and sent into model context instead of being rejected as IngressBlockedError (422), which is a security regression.
Useful? React with 👍 / 👎.
| const requestId = crypto.randomUUID(); | ||
| const messageId = session.persistUserMessage(content ?? '', attachments, requestId); | ||
|
|
There was a problem hiding this comment.
Preserve sourceChannel context before persisting messages
The new sendMessageDeps path parses sourceChannel but never applies it to the session before persisting/queueing. In the previous path, persistAndProcessMessage set channel capabilities and turn channel context from sourceChannel; now calls like this persist without metadata/context, so non-macos requests can inherit stale context or fall back to macos channel behavior during runAgentLoop, causing incorrect channel-specific routing/capability behavior.
Useful? React with 👍 / 👎.
* fix: reset activatedViaWakeWord flag on voice mode activation failure (#8180) Co-authored-by: Claude <noreply@anthropic.com> * fix: use effective base URL for polling and cancel task on dismiss (#8185) Co-authored-by: Claude <noreply@anthropic.com> * fix: derive segment count from manifest in media diagnostics (#8187) Co-authored-by: Claude <noreply@anthropic.com> * fix: enforce payload size limit on pairing proxy endpoints (#8188) Co-authored-by: Claude <noreply@anthropic.com> * fix: restore approvalConversationGenerator in RuntimeHttpServer (#8189) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: approved devices race condition and optimistic clear (#8190) Co-authored-by: Claude <noreply@anthropic.com> * fix: move zero-frame check before atomic rename in preprocess (#8191) Co-authored-by: Claude <noreply@anthropic.com> * fix: include context field in map cache config hash (#8192) Co-authored-by: Claude <noreply@anthropic.com> * fix: align lastPairedAt type to Int matching generated IPC types (#8193) Co-authored-by: Claude <noreply@anthropic.com> * improve Gmail OAuth setup UX with auto-detection and clearer messaging (#8194) Rewrite the Google OAuth setup skill prompts to feel like 3 user actions instead of a confusing 10-step process. Auto-detect sign-in completion by polling browser snapshots instead of asking users to confirm manually. Add progress callouts and clearer credential entry instructions. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: reset activatedViaWakeWord flag outside wakeWordEnabled guard (#8195) Co-authored-by: Claude <noreply@anthropic.com> * fix: send deny on PairingApprovalWindow close and supersede (#8196) When the pairing approval window is closed via the X button or superseded by a new request, sends a deny response to the daemon. Prevents iOS devices from hanging in pending state indefinitely. - Track currentPairingRequestId and responseSent flag - Add WindowCloseDelegate (NSWindowDelegate) for X-button close - denyIfNeeded() sends deny for unanswered requests in close() - Set responseSent = true in onDecision before calling close() Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: clear stale pairing overrides during v4 migration (#8197) Co-authored-by: Claude <noreply@anthropic.com> * fix: re-register QR pairing before TTL and handle missing daemon (#8199) Adds a timer to re-register the pairing request before the 5-minute TTL expires, keeping the QR code valid while the sheet is open. Also shows an error when daemon client is unavailable instead of rendering an empty sheet. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: honor task cancellation in QRPairingSheet pairing flow (#8210) Co-authored-by: Claude <noreply@anthropic.com> * fix: enforce Content-Length pre-check in pairing proxy (#8212) Co-authored-by: Claude <noreply@anthropic.com> * fix: roll back optimistic removal in removeApprovedDevice on IPC failure (#8215) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: reduce maxTokens default from 64000 to 16000 (#8217) Co-authored-by: Claude <noreply@anthropic.com> * feat: add streamThinking config flag and filter thinking deltas (#8219) Co-authored-by: Claude <noreply@anthropic.com> * fix: skip deny on same-ID retry in PairingApprovalWindow (#8223) Co-authored-by: Claude <noreply@anthropic.com> * fix: update maxTokens schema default and tests to 16000 (#8224) Co-authored-by: Claude <noreply@anthropic.com> * fix: separate iOS override migration and guard macOS cleanup (#8225) iOS: moves override cleanup into its own migratePairingOverridesIfNeeded() with a separate migration key, so existing v4-migrated users still get cleanup. macOS: only runs cleanup when legacy iosPairingUseOverride key is actually present, preserving intentional post-M9 overrides. Addresses feedback from PR #8197. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: avoid QR flicker during refresh and register on daemon connect (#8227) Keeps old QR visible during re-registration by only swapping credentials atomically on HTTP 200 success. Adds .onChange(of: daemonClient) to trigger registration when daemon becomes available after sheet opens. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle floating promise and add timeout in title generation (#8228) Co-authored-by: Claude <noreply@anthropic.com> * refactor: deduplicate isPlainObject into shared utility (#8229) Co-authored-by: Claude <noreply@anthropic.com> * perf: add database indexes on memorySegments table (#8230) Co-authored-by: Claude <noreply@anthropic.com> * feat: add backpressure and metrics to session message queue (#8231) Co-authored-by: Claude <noreply@anthropic.com> * perf: cache shell-parsing and risk classification results (#8232) Co-authored-by: Claude <noreply@anthropic.com> * security: add pino log serializer to scrub sensitive data (#8233) Co-authored-by: Claude <noreply@anthropic.com> * refactor: replace unsafe type assertions with proper type guards (#8234) Co-authored-by: Claude <noreply@anthropic.com> * feat: add circuit breaker to gateway runtime client (#8236) Co-authored-by: Claude <noreply@anthropic.com> * refactor: create centralized environment variable registry (#8235) Co-authored-by: Claude <noreply@anthropic.com> * feat: add status indication to macOS menu bar icon (#8237) Co-authored-by: Claude <noreply@anthropic.com> * refactor: replace any types with proper interfaces in test files (#8241) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove iosPairingUseOverride deletion from v4 migration (#8239) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove Porcupine iOS-only SPM dep that breaks Xcode 26.2 build (#8240) Porcupine's SPM package is iOS-only and pulls in ios-voice-processor which uses AVAudioSession (unavailable on macOS). Xcode 26.2 is now strict about this. The wake word engine is currently a stub so removing the dep is safe. Also fixes missing VellumAssistantShared imports and a stale protocol conformance in the wake word files. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * perf: add database indexes on memoryItems table (#8244) Co-authored-by: Claude <noreply@anthropic.com> * fix: add per-surface serialization to prevent race conditions (#8245) Co-authored-by: Claude <noreply@anthropic.com> * feat: source filter for Available Skills + platform catalog API (#8097) * feat: add source filter to Available Skills and fetch catalog from platform API - Add All/Vellum/Community source filter pills to Available Skills tab - Capitalize "Vellum" in empty state, hide community disclaimer for Vellum filter - Show Vellum catalog skills even if installed (with "Installed" label) - Add refresh button to Installed tab empty state - Switch catalog fetch from GitHub raw URL to platform API (/v1/skills/) - Pass platform session token (X-Session-Token) through IPC for authenticated fetch - Regenerate IPC contract with optional sessionToken on SkillsSearchRequest Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve platform token locally and fetch skill content via tar API - Remove sessionToken from IPC contract and Swift chain; the daemon now reads the platform API token from ~/.vellum/platform-token instead of receiving it via IPC messages - SessionTokenManager writes platform token to disk when set/deleted so the daemon can read it for authenticated platform API calls - Implement fetchSkillContent via platform tar API (GET /v1/skills/{id}/) which returns a tar.gz archive; extracts SKILL.md from the tarball with bundled fallback on failure - Add readPlatformToken()/getPlatformTokenPath() to platform.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: add AbortSignal support to tool implementations (#8246) Co-authored-by: Claude <noreply@anthropic.com> * refactor: split http-server.ts into focused middleware and route modules (#8243) Co-authored-by: Claude <noreply@anthropic.com> * fix: handle QR refresh failure and remove dead onChange (#8248) Shows error state after 2 consecutive refresh registration failures instead of silently leaving a stale QR. Removes dead .onChange(of: daemonClient != nil) since daemonClient is always non-nil in production. Addresses feedback from PR #8227. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add response style section, trim verbose system prompt sections, strengthen SOUL.md conciseness (#8249) * security: enforce 0o600 permissions on log files (#8252) Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate AbortSignal through permission checking (#8253) Co-authored-by: Claude <noreply@anthropic.com> * feat: complete ingress config schema with validation (#8254) Co-authored-by: Claude <noreply@anthropic.com> * perf: add database indexes on remaining tables (#8255) Co-authored-by: Claude <noreply@anthropic.com> * feat(macos): add secret dev mode toggle (#8226) * feat(macos): add secret dev mode toggle via Assistant ID tap gesture Tap the Assistant ID 7 times in Settings > Advanced to toggle dev mode, similar to Android's developer options. Dev mode gates the Feature Flags editor, Developer (env vars) section, and Platform URL in Connect tab. State persists in UserDefaults. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: default dev mode to enabled in DEBUG builds Uses `UserDefaults.object(forKey:) as? Bool ?? true` so debug builds start with dev mode on, while still respecting an explicit toggle-off. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: reset refresh timer and failure counter on QR retry (#8257) Resets consecutiveRefreshFailures to 0 and restarts the refresh timer when the user clicks Retry after a QR refresh failure. Without this, the QR would silently expire after 5 minutes and the next single failure would immediately re-trigger the error state. Addresses feedback from PR #8248. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * refactor: split lifecycle.ts into focused modules (#8259) Co-authored-by: Claude <noreply@anthropic.com> * feat: make global hotkey configurable in macOS client (#8258) Add keyboard shortcut settings UI, UserDefaults persistence, and dynamic re-registration. Defaults to Cmd+Shift+G. * feat: add structured error serialization to pino logger (#8260) Co-authored-by: Claude <noreply@anthropic.com> * fix: add numeric bounds validation for memory item confidence/importance (#8261) Co-authored-by: Claude <noreply@anthropic.com> * feat: make hardcoded daemon timeout values configurable (#8265) Co-authored-by: Claude <noreply@anthropic.com> * feat: add Zod schema for message metadata validation (#8268) Co-authored-by: Claude <noreply@anthropic.com> * refactor: separate migration code from platform utilities (#8272) Co-authored-by: Claude <noreply@anthropic.com> * refactor: use consistent pino logging throughout migration and startup code (#8273) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve all CI type-check and lint errors (#8276) - Add missing streamThinking to ThinkingConfigSchema default and test assertions - Fix boolean | undefined return type in contradiction-checker transaction - Wrap media-processing stage handlers to return Promise<void> - Cast Cron through unknown for Record<string, unknown> conversion - Update AgentLoopRun type to accept CheckpointInfo parameter - Add withSurface to ToolSetupContext test stubs - Add sessionId to ToolContext test stubs - Cast input_schema to typed interface for property access in tests - Fix fetch mock casts through unknown for Bun's preconnect property - Remove unused imports/vars and fix lint violations across 14 files Co-authored-by: Claude <noreply@anthropic.com> * feat(macos): add health check indicator to Platform URL row (#8271) * feat(macos): add health check indicator to Platform URL row Show a dynamic status icon on the Platform URL row in Settings > Connect that indicates whether the Vellum platform is reachable. Hits /healthz on appear and displays green checkmark (reachable), red xmark (unreachable), or spinner (checking). Error details shown on hover via tooltip. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: gate platform health check on dev mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: move response style directives to SOUL.md, remove buildResponseStyleSection (#8277) * Guardian Cross-Channel Approval UX Polish (#8208) * M1: Backend generative guardian copy + emoji title (#8117) * feat: replace static guardian thread copy with model-generated copy Co-Authored-By: Claude <noreply@anthropic.com> * fix: avoid blocking external channel dispatch on LLM copy generation (#8122) Co-authored-by: Claude <noreply@anthropic.com> * fix: create macOS delivery row before awaiting LLM copy generation (#8127) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M2: macOS native notification + deep link to thread (#8147) * feat: add macOS native notification + deep link for guardian requests Co-Authored-By: Claude <noreply@anthropic.com> * fix: add questionText to guardian IPC message for notification body (#8162) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M3: QA hardening + documentation (#8179) * test: add guardian copy generation tests and update architecture docs Co-Authored-By: Claude <noreply@anthropic.com> * test: add dedicated test for real buildFallbackCopy implementation (#8198) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * Fix: reject non-emoji guardian titles from model output (#8247) * fix: validate emoji prefix in generated guardian title Co-Authored-By: Claude <noreply@anthropic.com> * fix: only reject old Guardian question prefix, don't enforce emoji (#8251) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * refactor: remove assistant inbox feature flag gating from desktop UI (#8281) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: add provider abstraction and approval resilience rules to AGENTS.md (#8279) Co-authored-by: Claude <noreply@anthropic.com> * refactor: remove assistantInbox config schema and defaults (#8282) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * refactor: make ingress ACL enforcement always-on (remove feature flag gating) (#8283) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: add rule for agents to keep AGENTS.md up to date (#8284) Co-authored-by: Claude <noreply@anthropic.com> * refactor: remove assistantInbox feature-flag check from ingress ACL enforcement (#8287) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: update inbox docs to reflect always-on behavior (#8293) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: change private to internal for properties accessed from AppDelegate extension (#8294) connectionStatusCancellable, pulseTimer, and pulsePhase are accessed from AppDelegate+MenuBar.swift, which is a separate file. Private restricts access to the declaring file, so these need internal access. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: increase maxInputTokens from 180k to 200k (#8295) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve CI type-check and lint errors in ingress config and imports (#8296) Co-authored-by: Claude <noreply@anthropic.com> * fix: use Chrome debugger API for CSP-bypass eval, fix Swift Result<Void,String> build error, add influencer CLI command (#8297) Co-authored-by: marinatrajk <marina@odyseek.com> Co-authored-by: Claude <noreply@anthropic.com> * refactor: remove QuickChat feature (#8300) The QuickChat floating panel had an unfixable input focus bug — the NSPanel couldn't reliably receive keyboard input. Remove the entire feature: panel, view, hotkey monitors, notification category, settings shortcut, and background session helpers. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove stale relayPort from storage when port field is cleared (#8301) Co-authored-by: marinatrajk <marina@odyseek.com> Co-authored-by: Claude <noreply@anthropic.com> * fix(macos): use consistent paragraph style for placeholder height measurement (#8303) Co-authored-by: Claude <noreply@anthropic.com> * fix(macos): resolve CI build errors in settings views (#8306) - Remove #if DEBUG guard from SettingsPanelEnvVarsSheet so it compiles in release builds (already gated by dev mode at runtime) - Use explicit .some(true)/.some(false)/.none patterns in SettingsConnectTab switch statements to satisfy exhaustiveness checker Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: make error message bubbles span full chat width (#8312) Error bubbles were constrained to the same 520pt maxWidth as regular messages. Use .infinity for error messages so they fill the available chat area. Co-authored-by: Claude <noreply@anthropic.com> * feat: add Quick Input bar (Cmd+/) (#8309) * feat: add Quick Input bar (Cmd+/) for fast new thread creation Adds a floating Spotlight-style input bar that appears system-wide via Cmd+/ (Carbon RegisterEventHotKey). Type a message, hit Return, and it opens the main window with a new thread containing that message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review — prevent handler leak, fix multi-monitor, no-flash on submit - Track and remove Carbon event handler on teardown to prevent accumulation - Place Quick Input panel on the screen with the mouse cursor - Skip restoring previous app on submit so main window doesn't flash Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: don't restore previous app on resign-key dismissal Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix(lint): resolve lint errors in influencer client (#8316) - Replace `as any` with proper `Omit<ExtensionCommand, 'id'>` type cast - Change InfluencerProfile nullable fields from `| null` to `| undefined` - Update parseFollowerCount return type from `number | null` to `number | undefined` - Replace all `!== null` checks with `!== undefined` per project convention Co-authored-by: Claude <noreply@anthropic.com> * feat: request 16kHz mono format in AlwaysOnAudioMonitor audio tap (#8318) Co-authored-by: Claude <noreply@anthropic.com> * feat: add PorcupineBinding.swift — dlopen wrapper for Porcupine C API (#8320) Co-authored-by: Claude <noreply@anthropic.com> * feat: bundle Porcupine dylib, model, and keywords in build.sh (#8321) Co-authored-by: Claude <noreply@anthropic.com> * fix: always start daemon HTTP server for iOS pairing (#8322) The daemon's HTTP server (port 7821) is required for iOS pairing — the gateway proxies all iOS traffic through it. Previously this was gated behind the `localHttpEnabled` feature flag, so the daemon never started its HTTP server unless the flag was manually enabled via env var, causing HTTP 502 errors on iOS after QR pairing. Three fixes: - AppDelegate: always set RUNTIME_HTTP_PORT=7821 (remove flag gate) - AssistantCli: always forward RUNTIME_HTTP_PORT to CLI (remove flag gate) - CLI local.ts: add RUNTIME_HTTP_PORT to the daemon env forwarding list (the CLI was building a second minimal env and stripping it out) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: replace PorcupineWakeWordEngine stub with real Porcupine C SDK implementation (#8323) Co-authored-by: Claude <noreply@anthropic.com> * feat: add wake word keyword selection and fix APIKeyManager usage (#8324) Co-authored-by: Claude <noreply@anthropic.com> * fix(swift): fix IPC Unix socket protocol and reconnect on cancel (#8325) * fix(swift): widen access control for AppDelegate+MenuBar extension and fix Result<Void, String> error Properties accessed by AppDelegate+MenuBar.swift (in a separate file) need internal access — private restricts to the declaring file. Also wrap registration error strings in a proper Error-conforming type since Result requires its Failure type to conform to Error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(macos): resolve QR pairing issues with httpPort fallback and LAN gateway - Add resolvedHttpPort computed property that falls back to RUNTIME_HTTP_PORT env var, then default 7821, when daemonClient.httpPort is nil (happens when daemon HTTP server starts after socket connection during Qdrant timeout) - Add effectiveGatewayUrl that falls back to localLanUrl when no cloud gateway is configured, enabling LAN-only pairing - Update canGenerateQR, registration body, and QR payload to use effectiveGatewayUrl instead of gatewayUrl - Update hasGateway check to include LAN availability Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: always start daemon HTTP server for iOS pairing The daemon HTTP server was gated behind RUNTIME_HTTP_PORT env var, which relied on a fragile setenv → getenv → subprocess forwarding chain from the macOS app. ProcessInfo.processInfo.environment is a frozen snapshot from launch, so the env var often wasn't forwarded, causing the HTTP server to never start and iOS pairing requests to fail silently. Three fixes: - getRuntimeHttpPort() now defaults to 7821, so the HTTP server always starts regardless of env var forwarding - Wire setPairingBroadcast so pairing_approval_request IPC messages actually reach the macOS app - Remove conditional guard around HTTP server startup in lifecycle.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(macos): add loading state during token regeneration Show a spinner and disable the QR button while the daemon restarts after bearer token regeneration. Polls the healthz endpoint for up to 30s to avoid showing a dead-end "daemon unreachable" error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(swift): fix IPC Unix socket protocol and reconnect on cancel Two issues caused the macOS app to disconnect from the daemon after ~1 second and never reconnect: 1. DaemonConnection configured NWProtocolTCP.Options() on a Unix domain socket endpoint. Unix sockets don't use TCP — the invalid protocol stack caused the NWConnection to become unstable and disconnect shortly after connecting, before authentication could complete. 2. The .cancelled state handler didn't call scheduleReconnect() when the connection had already been established (resumed==true). This meant spontaneous disconnects were permanent — unlike .failed which properly triggers auto-reconnect. Without a stable IPC connection, all daemon broadcasts (pairing approval requests, reminders, schedule events, watcher alerts) were silently dropped because there were 0 authenticated sockets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: use AVAudioConverter for 16kHz resampling instead of custom tap format (#8326) Co-authored-by: Claude <noreply@anthropic.com> * fix: move FRAMEWORKS_DIR definition before Porcupine staleness check in build.sh (#8328) Co-authored-by: Claude <noreply@anthropic.com> * fix: add input validation and handle cleanup in PorcupineBinding (#8327) Co-authored-by: Claude <noreply@anthropic.com> * fix: prevent double dismiss of Quick Input panel on submit (#8315) Add isDismissing guard to prevent re-entrant dismiss calls when showMainWindow causes the panel to resign key while the onSubmit closure also calls dismiss explicitly. Co-authored-by: Claude <noreply@anthropic.com> * Release v0.3.6 (#8329) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix: move wake word callback outside lock and use dynamic frame length (#8331) Co-authored-by: Claude <noreply@anthropic.com> * fix: prevent duplicate audio in AVAudioConverter and use ceil for buffer capacity (#8332) Co-authored-by: Claude <noreply@anthropic.com> * perf: disable LLM reranking for memory recall (#8067) LLM reranking was calling Claude Haiku API on every message to re-score memory candidates, adding ~2.2s latency. The RRF merge already produces a well-ordered list from lexical, semantic, recency, and entity scores. Disabling reranking reduces memory recall from ~2.6s to ~700ms. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix(swift): restore NWProtocolTCP on Unix socket, keep cancelled reconnect (#8340) The previous commit removed NWProtocolTCP.Options() from Unix socket connections, but this protocol has been in use since the original IPC implementation (PR #499) and has been working in production for years. Removing it risks breaking IPC in the v0.3.7 release. Restores the TCP protocol stack while keeping the .cancelled state reconnect improvement (scheduleReconnect on post-connection cancel). Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: add explicit self. for property captures in PorcupineWakeWordEngine (#8337) Swift strict concurrency requires explicit `self.` when referencing properties inside closures. os.Logger string interpolation creates implicit closures, so `keyword` and `sensitivity` need `self.` prefix. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: add explicit self for keyword reference in os.Logger closure (#8342) The release build (universal binary) requires explicit `self.` for property references inside os.Logger string interpolation closures. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve CI failures blocking v0.3.7 release (#8344) - TypeScript: fix setPairingBroadcast type to use ServerMessage instead of loose { type: string; [key: string]: unknown } - Gateway lint: prefix unused vars with _ in whatsapp-deliver test - Swift: add explicit self. for keyword property in closure Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: broadcast pairing approval to HTTP/SSE clients (#8348) When the macOS app uses HTTP transport (localHttpEnabled flag), it connects via SSE instead of the Unix domain socket. Pairing approval requests were only broadcast to IPC socket clients, so HTTP-connected clients never received them. Two changes: 1. RuntimeHttpServer now also publishes pairing events to the AssistantEventHub with assistantId 'self' so SSE subscribers receive them. 2. AssistantEventHub allows events without sessionId (system events) to pass through to all subscribers regardless of their sessionId filter. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat(macos): add Restart menu item to status bar menu (#8347) * feat(macos): add Restart menu item to status bar menu Add a Restart option to the menu bar dropdown that stops the daemon, launches a fresh app instance, and terminates the current one. Useful for recovering from stuck states without manually quitting and relaunching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: gate app termination on successful relaunch in performRestart Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add memory degradation banner to component gallery with #F5F3EB background (#8356) Co-authored-by: Claude <noreply@anthropic.com> * Add rename option to thread right-click context menu (#8351) * M1: Add session_rename IPC message to daemon (#8338) * feat: add session_rename IPC message for client-initiated title changes Co-Authored-By: Claude <noreply@anthropic.com> * fix: validate conversation exists before renaming in handleSessionRename (#8341) Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> * M2: Add rename option to macOS thread context menu UI (#8346) * feat: add rename option to macOS thread context menu Co-Authored-By: Claude <noreply@anthropic.com> * fix: only show rename option for threads with a daemon session (#8350) Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local> Co-authored-by: Claude <noreply@anthropic.com> * fix: remove redundant conversation_id index on memory_segments (#8357) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove unscoped queue-full error and guard expireStale against exceptions (#8358) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve statusCode fallback when status is undefined in getErrorStatusCode (#8360) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve Error name/message/stack in log redaction serializer (#8359) Co-authored-by: Claude <noreply@anthropic.com> * fix: include workingDir and manifestOverride in risk cache key (#8361) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove redundant fingerprint index from memoryItems migration (#8362) Co-authored-by: Claude <noreply@anthropic.com> * fix: add missing legitimate env vars to KNOWN_VELLUM_VARS (#8363) Co-authored-by: Claude <noreply@anthropic.com> * fix: clean up settled surface mutex entries to prevent memory leak (#8364) Co-authored-by: Claude <noreply@anthropic.com> * fix: return immediately on aborted CAPTCHA wait instead of breaking (#8365) Co-authored-by: Claude <noreply@anthropic.com> * fix: rebind menu bar connection observer after client replacement and fix pulse animation (#8366) Co-authored-by: Claude <noreply@anthropic.com> * fix: limit half-open probes to single attempt and propagate CircuitBreakerOpenError (#8367) Co-authored-by: Claude <noreply@anthropic.com> * fix: read daemon timeouts without triggering loadConfig/migration side effects (#8368) Co-authored-by: Claude <noreply@anthropic.com> * fix: enforce log file permissions on existing files at startup (#8371) Co-authored-by: Claude <noreply@anthropic.com> * fix: auto-start SSE on HTTP transport connect for system events (#8370) When using HTTP transport (VELLUM_FLAG_LOCAL_HTTP_ENABLED=1), SSE was only started when MainWindowView.onAppear fired. This meant system events like pairing approval requests were lost if they arrived before the main window appeared. Auto-start the SSE stream immediately after the first health check passes in connect(). MainWindowView.onAppear still calls startSSE() but it's a no-op when the stream is already running. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: throw typed cancellation error from permission checks (#8372) Co-authored-by: Claude <noreply@anthropic.com> * feat: add pre-commit warning when modifying system-prompt.ts (#8354) * feat: add pre-commit warning when modifying system-prompt.ts Adds a non-blocking warning to the pre-commit hook that fires when system-prompt.ts is staged. Reminds contributors to consider whether their change belongs in a skill, IDENTITY.md, SOUL.md, USER.md, LOOKS.md, or skills/ instead of directly in the system prompt. Includes a 3-second pause to ensure the warning is actually read. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add BOOTSTRAP.md to system-prompt warning file list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * Release v0.3.7 (#8373) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix: initialize pairing handlers so approval responses are processed (#8374) initPairingHandlers() was defined but never called, leaving pairingStoreRef as null. When macOS sent a pairing_approval_response over IPC, the handler silently returned on the null check (line 29), so approvals never reached the PairingStore — iOS stayed stuck on "Waiting for approval" and the device was never added to the allowlist. Wire up initPairingHandlers() in lifecycle.ts after the HTTP server starts, passing the PairingStore instance and bearer token. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: improve media analysis skill defaults and add best practices (#8385) Update defaults based on real-world usage feedback: keyframe interval 3s→1s, segment duration 20s→15s, skip_dead_time on→off. Add best practices section with broad-vs-targeted map prompt guidance, sample prompt/schema, clip delivery notes, and vision analysis limitations. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add holistic Codex review phase to safe-blitz command (#8387) Co-authored-by: Claude <noreply@anthropic.com> * fix: watch http-token file so gateway picks up daemon token changes (#8389) When the daemon restarts and writes a new bearer token to ~/.vellum/http-token, a gateway process that started earlier still holds the stale token. This causes 401 errors for iOS clients that received the new token during pairing. Add a file watcher for http-token (following the existing CredentialWatcher pattern) that refreshes runtimeBearerToken, runtimeProxyBearerToken, and runtimeGatewayOriginSecret in the live config when the file changes. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: drop legacy conversation_id index and remove from Drizzle schema (#8393) Co-authored-by: Claude <noreply@anthropic.com> * fix: unreserve dedup cache entries on CircuitBreakerOpenError before returning 503 (#8394) Co-authored-by: Claude <noreply@anthropic.com> * fix: validate daemon timeout bounds in readDaemonTimeouts (#8395) Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate queue-full status through subagent message path (#8397) Co-authored-by: Claude <noreply@anthropic.com> * fix: respect env var precedence in http-token watcher (#8399) Address review feedback on #8389: 1. Skip file-based token refresh when RUNTIME_BEARER_TOKEN env var is set, preserving the same precedence as loadConfig() where env vars override the file. Prevents cloud deployments with pinned tokens from being silently overwritten by daemon file writes. 2. Wrap mkdirSync in try-catch so a non-writable parent directory doesn't crash the gateway at startup — the watcher is gracefully skipped instead. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add queue-if-busy behavior and hub publishing to POST /v1/messages (#8391) (#8400) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Alex Nork <48630278+alex-nork@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Ashlee Radka <ashleeradka@gmail.com> Co-authored-by: asharma53 <64060709+asharma53@users.noreply.github.com> Co-authored-by: siddseethepalli <siddseethepalli@gmail.com> Co-authored-by: David Vargas Fuertes <vargas@vellum.ai> Co-authored-by: NgoHarrison <harrison.ngo719@gmail.com> Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Aaron Levin <awlevin@users.noreply.github.com> Co-authored-by: V <vincent@vellum.ai> Co-authored-by: Marina Trajkovska <trajk.marina@gmail.com> Co-authored-by: marinatrajk <marina@odyseek.com> Co-authored-by: vellum-automation[bot] <192048195+vellum-automation[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Jason Zhou <jason@vellum.ai> Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Tirman Sidhu <tirmansidhu@gmail.com> Co-authored-by: Nick <127171085+ZeebBoyBlue@users.noreply.github.com> Co-authored-by: Nicolas Zeeb <all@Nicolass-MacBook-Pro.local>
Summary
PR 1/6 in the remove-runs-centralize-messages plan (cherry-picked to main).
Plan: .private/plans/REMOVE_RUNS_CENTRALIZE_MESSAGES.md