M3: Guardian approval decision -> verification kickoff#9460
Conversation
Co-Authored-By: Claude <noreply@anthropic.com>
| if (approval.status !== 'pending') { | ||
| return { handled: true, type: 'idempotent' }; | ||
| } |
There was a problem hiding this comment.
🔴 Idempotency check uses stale in-memory approval.status instead of DB state, allowing duplicate verification sessions
The idempotency guard at line 70 checks approval.status (the in-memory object passed by the caller) rather than comparing against the freshly-read database state returned by resolveApprovalRequest. When two concurrent requests both fetch the same pending approval before either resolves it, both will have approval.status === 'pending', causing both to bypass the idempotency check and create duplicate verification sessions.
Root cause and race condition scenario
The race is exploitable through the conversational engine path in guardian-approval-interception.ts, where await runApprovalConversationTurn(...) at line 269 yields back to the event loop between the approval fetch (line 146) and the call to handleAccessRequestApproval (line 328–336):
- Request A: fetches
guardianApproval(status=pending) at line 146 - Request A: calls
await runApprovalConversationTurn(...)at line 269 → yields - Request B: fetches the same approval (still
pending) at line 146 - Request B: calls
await runApprovalConversationTurn(...)→ yields - Request A: engine returns, calls
handleAccessRequestDecision→resolveApprovalRequesttransitionspending→approved, creates session feat: initialize Next.js app in /web directory #1 - Request B: engine returns, calls
handleAccessRequestDecision→resolveApprovalRequestreturns the existingapprovedrecord (idempotent), butapproval.status === 'pending'(stale!), so the check at line 70 does not trigger → creates session feat: add platform terraform for GKE deployment #2, which revokes session feat: initialize Next.js app in /web directory #1
The guardian receives two verification codes, but only the second one is valid because createOutboundSession (channel-guardian-service.ts:410-431) auto-revokes prior sessions. If the guardian relays the first (now-revoked) code to the requester, verification fails.
Impact: In concurrent approval scenarios, the first delivered verification code is silently invalidated, causing a confusing UX where the guardian's code doesn't work. The requester also receives duplicate "approved" notifications.
Prompt for agents
In assistant/src/runtime/routes/access-request-decision.ts, the idempotency check at lines 70-72 uses `approval.status` (the in-memory object passed by the caller) which may be stale. Instead, it should detect idempotency by comparing the resolved record's state to determine whether the DB transition actually happened in THIS call vs a prior call.
Option 1: Have resolveApprovalRequest in assistant/src/memory/channel-guardian-store.ts return a discriminated result that distinguishes 'newly_resolved' from 'already_resolved_same_decision' (e.g., include a `wasAlreadyResolved: boolean` flag).
Option 2: In handleAccessRequestDecision, re-read the approval from the DB after resolveApprovalRequest returns, and compare the updatedAt timestamp or status to detect whether the transition was made by this call.
Option 3 (simplest): Replace the check at line 70 with `if (resolved.status === decision && resolved.updatedAt !== resolved.createdAt && resolved.updatedAt < Date.now() - some_small_threshold)` — though this is fragile. The cleanest fix is option 1: modify resolveApprovalRequest to return `{ record, transitioned: boolean }` so the caller can reliably distinguish.
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: d31fe2570e
ℹ️ 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 (approval.status !== 'pending') { | ||
| return { handled: true, type: 'idempotent' }; |
There was a problem hiding this comment.
Make access-request approvals truly idempotent
resolveApprovalRequest already returns the existing row for same-decision replays, but this code checks approval.status (the caller’s snapshot) instead of whether this call actually transitioned the DB row. If the same approval is processed again from a stale pending snapshot (for example duplicate/retried callback handling across workers), the replay is treated as a fresh approval and creates a new outbound session/code, revoking the prior code and sending conflicting verification instructions to guardian/requester.
Useful? React with 👍 / 👎.
Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com>
* docs: add trusted contact access design doc (#9452) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: notify guardian when non-member requests access (#9459) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: handle guardian approval decision for access requests (#9460) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: activate trusted contact on successful verification (#9471) On successful verification of the 6-digit code, upserts assistant_ingress_members with status=active and policy=allow. The requester can immediately send messages after verification. Key changes: - validateAndConsumeChallenge now distinguishes guardian vs trusted contact verification via verificationType field - Identity-bound outbound sessions (trusted contacts) no longer create guardian bindings - New template for trusted contact verification success message - Existing guardian verification flow unchanged (backward compatible) Closes #9437 Co-authored-by: Harrison Ngo <harrison@vellum.ai> * feat: add HTTP routes for ingress member/invite management (#9475) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: add trusted contacts management skill (#9479) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: add notification signals for trusted contact lifecycle (#9481) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: channel-agnostic rollout and operator runbook for trusted contacts (#9529) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: wrap notifyGuardianOfAccessRequest in try-catch for graceful degradation (#9641) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: scope notification dedupe key to approval request ID (#9643) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: add requestId to access request approval for callback button routing (#9644) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: restore guardian binding on outbound verification by distinguishing verification purpose (#9695) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate guardian code delivery failures to prevent premature requester notification (#9733) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: replace runtime-port URLs with gateway URLs in trusted-contacts skill (#9756) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com>
* docs: add trusted contact access design doc (#9452) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: notify guardian when non-member requests access (#9459) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: handle guardian approval decision for access requests (#9460) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: activate trusted contact on successful verification (#9471) On successful verification of the 6-digit code, upserts assistant_ingress_members with status=active and policy=allow. The requester can immediately send messages after verification. Key changes: - validateAndConsumeChallenge now distinguishes guardian vs trusted contact verification via verificationType field - Identity-bound outbound sessions (trusted contacts) no longer create guardian bindings - New template for trusted contact verification success message - Existing guardian verification flow unchanged (backward compatible) Closes #9437 Co-authored-by: Harrison Ngo <harrison@vellum.ai> * feat: add HTTP routes for ingress member/invite management (#9475) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: add trusted contacts management skill (#9479) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: add notification signals for trusted contact lifecycle (#9481) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: channel-agnostic rollout and operator runbook for trusted contacts (#9529) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: wrap notifyGuardianOfAccessRequest in try-catch for graceful degradation (#9641) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: scope notification dedupe key to approval request ID (#9643) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: add requestId to access request approval for callback button routing (#9644) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: restore guardian binding on outbound verification by distinguishing verification purpose (#9695) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate guardian code delivery failures to prevent premature requester notification (#9733) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: replace runtime-port URLs with gateway URLs in trusted-contacts skill (#9756) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com>
…) (#9925) * M1: Land remote skill policy primitive and enable skills.sh-first baseline (#9789) * test: land remote skill policy primitive and enable skills.sh-first baseline Co-Authored-By: Claude <noreply@anthropic.com> * fix: decouple skills.sh risk check from blockSuspicious gate and coerce unknown risk labels - Remove the `policy.blockSuspicious &&` guard from the skills.sh risk threshold check so `maxSkillsShRisk` is always enforced independently of the Clawhub-specific blockSuspicious flag. - Validate that risk labels are known keys in SKILLS_SH_RISK_RANK and coerce unrecognized values to 'unknown' (fail closed) to prevent novel/typo'd risk strings from bypassing the threshold. - Add tests for both fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use Object.hasOwn for risk label validation to prevent prototype pollution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: exclude 'unknown' from valid maxSkillsShRisk threshold values Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M2: Add skills.sh discovery with audit report retrieval (#9824) * feat: add skills.sh discovery with audit report retrieval Co-Authored-By: Claude <noreply@anthropic.com> * fix: guard risk-rank lookup against prototype property labels in deriveOverallRisk Use Object.hasOwn(RISK_RANK, dim.risk) instead of checking RISK_RANK[dim.risk] !== undefined to prevent inherited prototype properties (toString, constructor, etc.) from bypassing the fail-closed check. Added test case covering this edge case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M3: Model-mediated security recommendation and user override flow (#9838) * feat: add model-mediated security recommendation and explicit override flow Co-Authored-By: Claude <noreply@anthropic.com> * fix: snapshot override entries and include unrecognized risk labels in flagged providers --------- Co-authored-by: Claude <noreply@anthropic.com> * M4: Install skills.sh skills into managed store via assistant flow (#9870) * feat: install skills.sh skills into managed store via assistant flow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: skip provenance from integrity hash and validate skill IDs before install Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * M5: Fallback to skills.sh when native capability fails (#9896) * feat: fallback to skills.sh when native capability fails Add skills.sh fallback skill and CLI wrapper for the search -> evaluate -> install workflow. The skill teaches the assistant to discover, evaluate, and install third-party skills from skills.sh with security audit visibility and user override. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: set exit code on JSON mode failures and use relative CLI path in SKILL.md - Add `if (!result.success) process.exitCode = 1` after JSON write in the install command so callers can detect failures via exit code even in JSON mode - Replace hardcoded `assistant/src/skills/skillssh-cli.ts` paths in SKILL.md with relative `src/skills/skillssh-cli.ts` so the skill works when the daemon runs from the assistant/ directory (and in packaged deployments) - Add exit code assertion to the JSON mode install failure test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use import.meta.main for direct-execution guard --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: show installed skill provenance and source links in UI Add provenance label derivation and source URL construction for installed skills. Distinguishes Vellum (bundled), third-party (skills.sh/Clawhub), user, and community skills with source metadata. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove dead voice source classifier branch (#9775) Co-authored-by: Claude <noreply@anthropic.com> * feat: extend voice mode timeout during tool execution (#9781) Co-authored-by: Claude <noreply@anthropic.com> * feat: capture app context for voice mode messages (#9786) Co-authored-by: Claude <noreply@anthropic.com> * feat: add TTS feedback during computer use escalation in voice mode (#9787) Co-authored-by: Claude <noreply@anthropic.com> * Use Sonnet 4.6 instead of Haiku for low-tier responses (#9779) * fix: render file upload surface inline instead of fallback chip (#9773) The file_upload surface was classified as a "chip only" surface in InlineSurfaceRouter, causing it to render as a tiny fallback chip instead of the full drag-and-drop FileUploadSurfaceView. Moved the view to the shared module and wired it up in the inline router. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore voice source classifier bypass for Fn-hold dictation (#9790) Co-authored-by: Claude <noreply@anthropic.com> * fix: rephrase computer-use prompt to avoid narrowing sensitive-data ban (#9791) Co-authored-by: Claude <noreply@anthropic.com> * perf: add compound index on memoryItems(scopeId, status) (#9792) Co-authored-by: Claude <noreply@anthropic.com> * fix: use selectableText modifier in ChatBubbleTextContent (#9793) Co-authored-by: Claude <noreply@anthropic.com> * feat: define standard HTTP error response format (#9794) Co-authored-by: Claude <noreply@anthropic.com> * feat: add per-bearer-token rate limiting for HTTP API (#9795) Co-authored-by: Claude <noreply@anthropic.com> * fix: add deduplication check in notification dispatch (#9796) Co-authored-by: Claude <noreply@anthropic.com> * feat: add circuit breaker for Qdrant client (#9797) Co-authored-by: Claude <noreply@anthropic.com> * feat: add FTS index reconciliation mechanism (#9799) Co-authored-by: Claude <noreply@anthropic.com> * refactor: extract PermissionChecker class from executor.ts (#9798) Co-authored-by: Claude <noreply@anthropic.com> * feat: add migration crash recovery for stalled migrations (#9800) Co-authored-by: Claude <noreply@anthropic.com> * fix: externalize onnxruntime-node in daemon build to fix dylib loading (#9802) bun build --compile bundles onnxruntime_binding.node into the binary and extracts it to a temp dir at runtime, but doesn't extract the sibling libonnxruntime.1.21.0.dylib — causing dlopen to fail. Fix by marking onnxruntime-node as --external so the compiled binary resolves it via node_modules/ at runtime, and copying the package (with onnxruntime-common) alongside the daemon binary. Also codesigns the native .node/.dylib files in the app bundle. Co-authored-by: Claude <noreply@anthropic.com> * perf: add index on notificationEvents(dedupeKey) (#9804) Co-authored-by: Claude <noreply@anthropic.com> * feat: surface Qdrant circuit-breaker state in memory retrieval (#9805) Co-authored-by: Claude <noreply@anthropic.com> * feat: add per-IP rate limiting fallback for unauthenticated endpoints (#9806) Co-authored-by: Claude <noreply@anthropic.com> * feat: add /v1/debug introspection endpoint (#9807) Co-authored-by: Claude <noreply@anthropic.com> * refactor: extract SecretDetectionHandler class from executor.ts (#9808) Co-authored-by: Claude <noreply@anthropic.com> * docs: design HTTP token refresh protocol for iOS client (#9809) Co-authored-by: Claude <noreply@anthropic.com> * fix: correct async return types and remove invalid local keyword in build.sh (#9810) - Fix async functions returning void instead of Promise<void> in session/daemon code - Remove invalid 'local' keyword used outside function scope in macOS build.sh Co-authored-by: Claude <noreply@anthropic.com> * refactor: split channel-delivery-store.ts into focused modules (#9811) Co-authored-by: Claude <noreply@anthropic.com> * refactor: migrate console.log/error/warn to structured Pino logger (#9812) Co-authored-by: Claude <noreply@anthropic.com> * refactor: split channel-guardian-store.ts into focused modules (#9813) Co-authored-by: Claude <noreply@anthropic.com> * refactor: split conversation-store.ts into focused modules (#9814) Co-authored-by: Claude <noreply@anthropic.com> * fix: prevent voice context prefix from leaking into chat UI, sanitize inputs, capture context earlier (#9815) Co-authored-by: Claude <noreply@anthropic.com> * refactor: migrate route error responses to standard format (#9816) Co-authored-by: Claude <noreply@anthropic.com> * fix: replace as-unknown-as casts with proper types in core modules (#9817) Co-authored-by: Claude <noreply@anthropic.com> * fix: add durable pause flag for conversation timeout during CU escalation (#9818) Co-authored-by: Claude <noreply@anthropic.com> * fix: use login names and reactions for safe-do reviewer activity gating (#9819) Co-authored-by: Claude <noreply@anthropic.com> * fix: sign all bundled node_modules files and strip non-runtime assets (#9820) codesign treats everything under Contents/MacOS/ as code objects, so all files in node_modules — not just native .node/.dylib binaries — must carry a valid signature. Also strip source files, docs, sourcemaps, and type declarations that aren't needed at runtime. Co-authored-by: Claude <noreply@anthropic.com> * feat: add HTTP request/response logging middleware (#9821) * fix: sign all bundled node_modules files and strip non-runtime assets codesign treats everything under Contents/MacOS/ as code objects, so all files in node_modules — not just native .node/.dylib binaries — must carry a valid signature. Also strip source files, docs, sourcemaps, and type declarations that aren't needed at runtime. Co-Authored-By: Claude <noreply@anthropic.com> * feat: add HTTP request/response logging middleware Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * fix: await async addMessage calls in tests (#9822) Co-authored-by: Claude <noreply@anthropic.com> * refactor: extract ToolApprovalHandler class from executor.ts (#9823) Co-authored-by: Claude <noreply@anthropic.com> * feat: add provider status to /v1/debug endpoint (#9825) Co-authored-by: Claude <noreply@anthropic.com> * perf: add database indexes, FK cascades, job timeout detection, and schema cleanup (#9826) Co-authored-by: Claude <noreply@anthropic.com> * fix: capture app context concurrently with transcription, check emptiness after sanitization (#9827) Co-authored-by: Claude <noreply@anthropic.com> * fix: replace any types with proper types in http-server WebSocket handlers (#9828) Co-authored-by: Claude <noreply@anthropic.com> * fix: destructure handler from webhook factory return values in gateway tests (#9829) Co-authored-by: Claude <noreply@anthropic.com> * feat: implement HTTP token refresh in iOS client (#9830) Co-authored-by: Claude <noreply@anthropic.com> * fix: reduce thread header spacing and open apps in view-only mode (#9831) - Shrink Threads heading from 18pt to 13pt - Remove vertical padding from divider above Threads header - Reduce Threads header vertical padding from 8pt to 4pt - Reset isAppChatOpen when opening an app so it defaults to view-only mode instead of sticky edit mode Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: await async addMessage/sendMessage calls in tests (#9832) Co-authored-by: Claude <noreply@anthropic.com> * fix: check conversationTimeoutPaused in isThinking subscriber (#9833) Co-authored-by: Claude <noreply@anthropic.com> * fix: run DictationContextCapture.capture() on MainActor instead of async let (#9835) Co-authored-by: Claude <noreply@anthropic.com> * fix: add authenticationRequired case to SessionErrorCategory switch (#9836) Co-authored-by: Claude <noreply@anthropic.com> * fix: defer PID file write until daemon is ready (#9839) Co-authored-by: Claude <noreply@anthropic.com> * fix: kill stale daemon processes before spawning new one (#9840) Co-authored-by: Claude <noreply@anthropic.com> * fix: clean up PID file on daemon startup crash (#9841) Co-authored-by: Claude <noreply@anthropic.com> * fix: validate daemon PID is alive in macOS client health check (#9842) Co-authored-by: Claude <noreply@anthropic.com> * fix: handle missing onnxruntime-node gracefully in compiled binary (#9843) Co-authored-by: Claude <noreply@anthropic.com> * fix: constellation graph — opaque rounded-square nodes, adjust spacing, remove .md files (#9845) - Replace circle node shapes with rounded squares (RoundedRectangle) - Add opaque VColor.background fill behind translucent color so edges don't show through nodes - Adjust edge lengths: center→category 200pt, category→subcategory 160pt, subcategory→skill 160pt for more breathing room at leaf level - Remove workspace .md files (IDENTITY.md, USER.md, etc.) from the Knowledge category — only actual skills appear in the graph - Fix exhaustive switch for new authenticationRequired SessionErrorCode Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve TypeScript type-safety issues in work-item handlers and test fixtures (#9846) - Fix unsafe type assertions in headlessLock cleanup (work-items.ts, work-item-runner.ts) - Add missing startedAt field to work item test fixtures (memory-regressions.test.ts) - Fix double-assertion for legacy trust rule migration cast (trust-store.ts) Co-authored-by: Claude <noreply@anthropic.com> * fix: add missing authenticationRequired case to ChatErrorToastView switch (#9847) PR #9836 added the authenticationRequired case to the SessionErrorCategory enum but missed updating the exhaustive switch in ChatErrorToastView. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle authenticationRequired case in macOS and iOS icon switches (#9848) Co-authored-by: Claude <noreply@anthropic.com> * fix: write PID file before throwing on daemon startup timeout (#9849) Co-authored-by: Claude <noreply@anthropic.com> * fix: add VELLUM_UNSAFE_AUTH_BYPASS to env registry known vars (#9850) Co-authored-by: Claude <noreply@anthropic.com> * fix: gracefully degrade when proactive TLS renewal fails (#9851) Co-authored-by: Claude <noreply@anthropic.com> * fix: defer semantic search until after early termination check (#9852) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9771 (#9854) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9757 (#9855) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9765 (#9853) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9753 — restore unsafe-inline for LLM-generated apps, refactor gallery to use addEventListener (#9856) Co-authored-by: Claude <noreply@anthropic.com> * feat: auto-deny pending tool confirmations in daemon on new user message (#9788) * feat: auto-deny pending tool confirmations in daemon when user sends a message Co-Authored-By: Claude <noreply@anthropic.com> * fix: move auto-deny to just before dispatchUserMessage Prevents auto-denying confirmations when the message is intercepted by secret-block, recording commands, or other early-return paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: auto-deny pending confirmations in HTTP message ingress path too Ensures the auto-deny behavior works for all clients, not just IPC. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove client-side auto-deny, daemon handles it now The daemon auto-denies pending confirmations when a new user message arrives, so the client no longer needs to duplicate this logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: clear stale pending-interactions entries on auto-deny When denyAllPendingConfirmations resolves prompter requests directly, the pending-interactions tracker entries become stale. Clean them up so channel approval flows don't see phantom pending approvals. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9769 (#9857) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9826 (#9861) Co-authored-by: Claude <noreply@anthropic.com> * fix: capture app context before transcription finalization (#9863) Co-authored-by: Claude <noreply@anthropic.com> * Trusted Contact Access (Chat-First) (#9530) * docs: add trusted contact access design doc (#9452) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: notify guardian when non-member requests access (#9459) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: handle guardian approval decision for access requests (#9460) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: activate trusted contact on successful verification (#9471) On successful verification of the 6-digit code, upserts assistant_ingress_members with status=active and policy=allow. The requester can immediately send messages after verification. Key changes: - validateAndConsumeChallenge now distinguishes guardian vs trusted contact verification via verificationType field - Identity-bound outbound sessions (trusted contacts) no longer create guardian bindings - New template for trusted contact verification success message - Existing guardian verification flow unchanged (backward compatible) Closes #9437 Co-authored-by: Harrison Ngo <harrison@vellum.ai> * feat: add HTTP routes for ingress member/invite management (#9475) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: add trusted contacts management skill (#9479) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: add notification signals for trusted contact lifecycle (#9481) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: channel-agnostic rollout and operator runbook for trusted contacts (#9529) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: wrap notifyGuardianOfAccessRequest in try-catch for graceful degradation (#9641) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: scope notification dedupe key to approval request ID (#9643) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: add requestId to access request approval for callback button routing (#9644) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: restore guardian binding on outbound verification by distinguishing verification purpose (#9695) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate guardian code delivery failures to prevent premature requester notification (#9733) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: replace runtime-port URLs with gateway URLs in trusted-contacts skill (#9756) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: close localhost /v1 runtime URL guard gap (#9859) * fix: guard PID cleanup to current process on startup failure (#9873) Co-authored-by: Claude <noreply@anthropic.com> * fix: use dynamic arch detection in incremental ONNX binary stripping (#9874) Co-authored-by: Claude <noreply@anthropic.com> * fix: defer jobs on QdrantCircuitOpenError instead of failing them permanently (#9875) Co-authored-by: Claude <noreply@anthropic.com> * fix: include destination in duplicate-delivery lookup (#9876) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove schema-only dedupe_key index that is never created at runtime (#9877) Co-authored-by: Claude <noreply@anthropic.com> * fix: replace full schedule scan with aggregate SQL counts in debug endpoint (#9878) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve classified risk level on permission-check exceptions (#9879) Co-authored-by: Claude <noreply@anthropic.com> * fix: properly handle async rejections from addPointerMessage, persistCallCompletionMessage, and sweepExpiredGuardianActions (#9880) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9795 (#9881) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove redundant scope_id+status index already covered by 3-column index (#9792) (#9882) Co-authored-by: Claude <noreply@anthropic.com> * Preserve user-uploaded images across context compaction and retry stripping (#9871) * fix: use getDbPath() for recovery instructions instead of hardcoded path (#9883) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9799 (#9884) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9805 (#9885) Co-authored-by: Claude <noreply@anthropic.com> * fix: report 'down' health when no active provider can be selected (#9887) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9806 (#9888) Co-authored-by: Claude <noreply@anthropic.com> * fix: handle undefined response in request logger for WebSocket upgrades (#9889) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9809 (#9890) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9816 (#9891) Co-authored-by: Claude <noreply@anthropic.com> * fix: accept empty sessionId on session errors as broadcast for 401 recovery (#9892) Co-authored-by: Claude <noreply@anthropic.com> * fix: await recursive drainQueue calls to prevent unhandled rejections (#9886) Co-authored-by: Claude <noreply@anthropic.com> * Voice ASK_GUARDIAN Generative Timeout + Callback Follow-Up (#9735) * M1: Guardian Action Follow-Up Data Model + Store Transitions (#9573) * feat: add guardian action follow-up data model and store transitions Co-Authored-By: Claude <noreply@anthropic.com> * fix: require expired status in follow-up state transitions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: restrict terminal follow-up transitions to finalizeFollowup only Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove none->awaiting_guardian_choice from progressFollowupState transitions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * feat: add daemon-injected guardian action copy generator (#9604) Co-authored-by: Claude <noreply@anthropic.com> * M3: Voice Timeout Flow Migration to Generated Assistant Turn (#9632) * feat: replace hardcoded voice timeout with generated assistant turn Co-Authored-By: Claude <noreply@anthropic.com> * fix: send guardian expiry notices from call-timeout path When markTimedOutWithReason is called in the call controller's consultation timeout, the guardian action request transitions from 'pending' to 'expired' immediately. The sweepExpiredGuardianActions sweep only looks for 'pending' requests, so these requests were never picked up by the sweep — meaning guardians never received the "question expired" notice (thread messages, channel replies). Extract the per-request notification logic from the sweep into a shared sendGuardianExpiryNotices helper. The call controller timeout path now captures deliveries before marking the request as timed out, then calls the helper to send expiry notices to all guardian destinations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M4: Late Reply Interception + Generated Guardian Follow-Up Prompt (#9650) * feat: intercept late guardian replies and initiate follow-up flow Co-Authored-By: Claude <noreply@anthropic.com> * fix: address review feedback on guardian timeout PR - Differentiate guardian_stale_answered vs guardian_stale_expired scenarios so answered-from-another-channel gets the correct message instead of the generic 'expired' text. Also update the mac path in session-process.ts to use the composed message for consistency. - Gate the expired guardian action late-reply interception on !hasCallbackData to prevent inline button presses from being misclassified as late answers. - Add request-code disambiguation for multiple expired deliveries, mirroring the existing pending-delivery disambiguation pattern, so late replies bind to the correct expired request. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M5: Guardian Follow-Up Conversation Engine (Structured Decisions) (#9669) * feat: guardian follow-up conversation engine with structured decisions Co-Authored-By: Claude <noreply@anthropic.com> * fix: wire generator to mac channel and add followup disambiguation - Wire guardianFollowUpConversationGenerator to session-process.ts via module-level injection from lifecycle.ts, so the mac/IPC channel path can classify follow-up replies (previously always returned keep_pending) - Add request-code disambiguation for multiple awaiting_guardian_choice follow-up deliveries in inbound-message-handler.ts, matching the existing pattern used for pending and expired deliveries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * feat: execute callback/message-back actions and guardian completion replies (#9701) Co-authored-by: Claude <noreply@anthropic.com> * fix: harden late-reply interception edge cases (#9706) * fix: add non-empty guard to voice bullet regression test (#9688) Add expect(voiceBullet).not.toHaveLength(0) assertion before the negative assertions to prevent vacuous passes when the voice bullet format changes. Co-authored-by: Claude <noreply@anthropic.com> * fix: order pending guardian requests by recency in getPendingRequestByCallSessionId (#9689) Add ORDER BY created_at DESC to ensure the most recent pending request is returned when multiple exist for the same call session. Co-authored-by: Claude <noreply@anthropic.com> * fix: tighten gateway-only CI guard by removing temporary allowlist (#9690) Co-authored-by: Claude <noreply@anthropic.com> * fix: guard pointer emission against invalid origin conversations in relay-server (#9691) Wrap the cross-conversation pointer write in a try/catch so that a stale or invalid originConversationId doesn't abort the success/failure handler and leave the outbound call in a bad state. Co-authored-by: Claude <noreply@anthropic.com> * fix: keep polling in safe-do feedback loop when reviews are still pending (#9692) Instead of treating 'no new reviews within 3 minutes' as done, continue polling until both reviewers respond or a longer timeout is reached. Co-authored-by: Claude <noreply@anthropic.com> * fix: guardian intent routing — use normalized text, fix phone regex, preserve user content (#9693) - Use normalized text (filler-stripped) for direct guardian pattern matching - Fix phone regex: phone(?:\s+number)? to match 'verify my phone' - Preserve original user message when forcing guardian setup flow Co-authored-by: Claude <noreply@anthropic.com> * Persist recording-related messages across app restart (#9666) * M1: Persist original user message for _with_remainder recording intents (#9634) * feat: persist original user message for _with_remainder recording intents Co-Authored-By: Claude <noreply@anthropic.com> * fix: preserve displayContent when slash resolves to unknown When a _with_remainder message strips down to an unknown slash command, the unknown-slash branch in session-process.ts was persisting the stripped content directly instead of using displayContent. For example, "start recording /foo" would store "/foo" in the DB instead of the original text. Thread displayContent through the unknown-slash persistence path, mirroring the pattern used in persistUserMessage in session-messaging.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: preserve displayContent in drainQueue unknown-slash path Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * M2: Persist user + assistant messages for _only recording commands (#9647) * feat: persist user + assistant messages for _only recording commands Co-Authored-By: Claude <noreply@anthropic.com> * fix: add fallback for msg.task in misc.ts persistence calls * fix: sync session.messages with persisted recording command messages After persisting recording command user/assistant messages to SQLite via conversationStore.addMessage(), also push them to the session's in-memory messages array. This prevents divergence between DB and in-memory history that could break operations like regenerate() which rely on both sources. In sessions.ts the session is always available (created at handler entry). In misc.ts the session may or may not exist (task_submit creates conversations not sessions), so a defensive ctx.sessions.get() lookup is used. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * M3: Align finalization text and persist error-path messages (#9662) * feat: align finalization text and persist error-path messages Co-Authored-By: Claude <noreply@anthropic.com> * fix: guard error-path addMessage calls against persistence failures Wrap each error-path conversationStore.addMessage() call in finalizeAndPublishRecording() with try/catch so the IPC notification (assistant_text_delta + message_complete) is always sent regardless of whether the DB write succeeds. Logs a warning if the DB write fails. This prevents FK violations (conversation deleted), SQLite busy errors, or other persistence failures from blocking the ctx.send() calls and causing the function to fail unexpectedly instead of returning { success: false }. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: guard session.messages push behind processing check to prevent agent loop corruption Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: replace timezone free-text input with searchable picker popover (#9696) Replace the error-prone free-text timezone input with a popover-based picker that shows a searchable list of all IANA timezones. Includes a one-click "Use system" button to populate from the Mac's timezone. Invalid input is now impossible since all selections come from valid IANA identifiers. Also fixes a pre-existing Swift build error in ChatBubbleTextContent where a ternary mixed incompatible TextSelectability types. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: blitz command — use check-pr-reviews for polling, add rebase handling, update README (#9697) - Use check-pr-reviews script for review polling to detect Codex rate limits - Add explicit rebase-and-retry for conflicted approved PRs - Update README slash commands section for new blitz behavior Co-authored-by: Claude <noreply@anthropic.com> * fix: address gateway-only guard test feedback from PR #9684 (#9698) Co-authored-by: Claude <noreply@anthropic.com> * fix: restore retry logic for transient errors in video fetch after gateway migration (#9699) Co-authored-by: Claude <noreply@anthropic.com> * fix: re-trigger reviews after rebase in safe-do conflict resolution (#9700) After resolving merge conflicts and force-pushing, re-request Codex/Devin reviews before proceeding to merge, since the post-rebase diff may differ. Co-authored-by: Claude <noreply@anthropic.com> * fix: harden late-reply interception in inbound-message-handler - Skip late-reply interception for callback payloads - Don't initiate follow-up when answerCall already succeeded - Handle failed late-followup start instead of falling through - Add else branch for channel path followup failure with stale message Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jason Zhou <jason@vellum.ai> Co-authored-by: Vellum Assistant <assistant@vellum.ai> * fix: check follow-up state transition results before replying to guardian (#9708) Co-authored-by: Claude <noreply@anthropic.com> * fix: gate follow-up action execution on successful state transition (#9709) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on guardian follow-up executor - Fix outbound_message_copy fallback to include lateAnswerText and correct audience framing - Fix counterparty resolution to use toNumber for outbound calls (via initiatedFromConversationId heuristic) - Gate follow-up action execution on successful progressFollowupState transition to prevent duplicate side effects (#9710) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve counterparty by call direction and include late answer in outbound SMS fallback (#9713) Co-authored-by: Claude <noreply@anthropic.com> * docs: guardian timeout/follow-up architecture docs and hardening guards (#9721) Co-authored-by: Claude <noreply@anthropic.com> * fix: address holistic review - remove provider guard, pass mac generator, complete test scenarios Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: pass copy generator to remaining stale followup composer calls in mac path (#9774) * Remove open_tasks_window IPC message and task_list_show side effect (#9723) Co-authored-by: Claude <noreply@anthropic.com> * Update task tool tests for conversation-only output (#9727) Co-authored-by: Claude <noreply@anthropic.com> * Update docs to reflect conversation-first task management (#9731) Co-authored-by: Claude <noreply@anthropic.com> * Remove macOS standalone Tasks window (#9732) Co-authored-by: Claude <noreply@anthropic.com> * fix: add explicit instruction allowing computer use navigation to consoles and dashboards (#9734) The model's built-in safety training was causing the assistant to refuse navigating to websites like the Anthropic console for API key creation, even though the user explicitly asked. The existing 'never type passwords' rule was being over-extrapolated into 'never go near anything credential-adjacent.' Add a counter-instruction clarifying that navigating to consoles, dashboards, login pages, and admin panels is expected behavior when the user asks. Co-authored-by: Claude <noreply@anthropic.com> * fix: fall back to any valid codesigning identity before ad-hoc (#9736) The build script only checked for 'Developer ID Application' and 'Apple Development'/'Mac Developer' certificates. If a self-signed codesigning certificate (e.g. 'Vellum Development') was the only identity available, it was skipped and the build fell through to ad-hoc signing. Ad-hoc signing produces a new code hash on every rebuild, causing macOS TCC to revoke all granted permissions (Accessibility, Screen Recording, Microphone, etc.). Add a generic fallback that picks any valid codesigning identity before resorting to ad-hoc, so permissions persist across rebuilds. Co-authored-by: Claude <noreply@anthropic.com> * Fix test assertion for updated done-status error message (#9741) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve macOS build warnings (#9743) Co-authored-by: Claude <noreply@anthropic.com> * Trim redundant words from BOOTSTRAP.md first message (#9740) Co-authored-by: Claude <noreply@anthropic.com> * M1: Enrich SOUL.md template (#9737) * Enrich SOUL.md template with warmth and personality Co-Authored-By: Claude <noreply@anthropic.com> * Fix: use asterisk italic to avoid comment stripping The _ prefix is treated as a comment by stripCommentLines and gets removed from the system prompt. Using * for italic instead. --------- Co-authored-by: Claude <noreply@anthropic.com> * Warm up IDENTITY.md template with inviting field descriptions (#9738) Co-authored-by: Claude <noreply@anthropic.com> * Enrich USER.md template with coaching prompts and sign-off (#9739) Co-authored-by: Claude <noreply@anthropic.com> * Add memory persistence, group chat, and platform formatting guidance to system prompt (#9742) Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate schedule source to task-runner conversations (#9745) When a schedule triggers a task via run_task:<id>, the task runner created conversations without source: 'schedule', defaulting to 'user'. This caused schedule-triggered task threads to appear in the regular Threads section instead of the Scheduled section in the macOS sidebar. Add optional source field to TaskRunOptions and pass 'schedule' from both the scheduler tick and the manual 'run now' handler. Co-authored-by: Claude <noreply@anthropic.com> * feat: add startup warning when VELLUM_DAEMON_NOAUTH is set (#9747) Co-authored-by: Claude <noreply@anthropic.com> * feat: add database index on conversations(updatedAt) (#9748) Co-authored-by: Claude <noreply@anthropic.com> * feat: add startup warning when DISABLE_HTTP_AUTH is set (#9749) Co-authored-by: Claude <noreply@anthropic.com> * chore: rename colliding migration files (026, 027) (#9750) Co-authored-by: Claude <noreply@anthropic.com> * fix: add max deferral limit to prevent indefinite job deferral (#9752) Co-authored-by: Claude <noreply@anthropic.com> * security: reduce TLS certificate validity from 10 years to 1 year (#9751) Co-authored-by: Claude <noreply@anthropic.com> * security: remove unsafe-inline from CSP script-src directive (#9753) Co-authored-by: Claude <noreply@anthropic.com> * docs: document why hooks-runner.test.ts is skipped (#9754) Co-authored-by: Claude <noreply@anthropic.com> * feat: add periodic background cleanup for gateway dedup caches (#9755) Co-authored-by: Claude <noreply@anthropic.com> * feat: add skill tool name collision detection (#9757) Co-authored-by: Claude <noreply@anthropic.com> * fix: add accessibility label to timezone picker clear button (#9758) Co-authored-by: Claude <noreply@anthropic.com> * feat: use local file path for recording playback and thumbnails (#9744) Pass the on-disk file path from daemon to client via IPC so recordings play directly from the local file instead of fetching via HTTP. The client generates thumbnails natively with AVAssetImageGenerator, eliminating the ffmpeg dependency and fixing the 3:4 portrait fallback. Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: record last_fix_push_time after re-request comments in blitz (#9759) Co-authored-by: Claude <noreply@anthropic.com> * fix: use original user text for guardian conversation title generation (#9760) Co-authored-by: Claude <noreply@anthropic.com> * fix: pass copy generator to remaining stale followup composer calls in mac path Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Aaron Levin <awlevin@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: siddseethepalli <siddseethepalli@gmail.com> Co-authored-by: Jason Zhou <jason@vellum.ai> Co-authored-by: Vellum Assistant <assistant@vellum.ai> * fix: add missing await on addMessage calls and catch unhandled rejection in sweep --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jason Zhou <jason@vellum.ai> Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Aaron Levin <awlevin@users.noreply.github.com> Co-authored-by: siddseethepalli <siddseethepalli@gmail.com> * fix: improve auto-deny UX when user chats during pending confirmation (#9893) - Update decisionContext to instruct the agent to stop and respond to the user's message instead of immediately re-requesting the tool - Mark confirmation card as denied in the UI when user sends a follow-up Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: index user-uploaded attachments for asset_search (#9895) * fix: index user-uploaded attachments so asset_search can find them User attachments (e.g. zip files dropped into chat) were embedded inline in the message content JSON but never inserted into the attachments table. This meant asset_search returned nothing and asset_materialize couldn't locate them. Now persistUserMessage() calls uploadAttachment() + linkAttachmentToMessage() for each user attachment after persisting the message, with content-hash dedup to avoid duplicates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: validate user attachments before indexing Add validateAttachmentUpload() check before uploadAttachment() to ensure dangerous file extensions and unsupported MIME types are rejected, matching the validation that the HTTP upload route already enforces. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review feedback on PR #9871 (#9894) * fix: address review feedback on PR #9871 - Restore keep-latest logic for top-level images in media retry so context-too-large recovery can actually reduce payload size - Fix stripContextSummaryTags to find closing tag by indexOf instead of endsWith, preventing summary corruption when preserved images append trailing text blocks - Carry forward previously preserved images from existing summary message across multiple compaction cycles * fix: address Codex and Devin review feedback on PR #9894 - Use lastIndexOf for </context_summary> tag to avoid truncating summaries that legitimately mention the tag - Cap preserved image blocks at 5 to prevent unbounded accumulation across compaction cycles * fix: buffer existing cert/key before proactive TLS renewal attempt (#9911) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove nonce from CSP style-src to allow unsafe-inline styles (#9912) Co-authored-by: Claude <noreply@anthropic.com> * fix: map x86_64 to x64 in ONNX binary stripping for Intel Mac support (#9913) Co-authored-by: Claude <noreply@anthropic.com> * fix: skip local embedding backend after onnxruntime import failure (#9915) Co-authored-by: Claude <noreply@anthropic.com> * fix: scope stale-daemon kills to current workspace PID (#9917) Co-authored-by: Claude <noreply@anthropic.com> * fix: reject custom secret patterns that produce zero-length matches (#9918) Co-authored-by: Claude <noreply@anthropic.com> * fix: recreate FTS triggers after dropping them in clearAll (#9916) Co-authored-by: Claude <noreply@anthropic.com> * fix: align dedup check with DB unique index on (decision, channel) (#9922) Co-authored-by: Claude <noreply@anthropic.com> * fix: persist token to disk before updating in-memory auth state in rotation flow (#9923) Co-authored-by: Claude <noreply@anthropic.com> * fix: allow zero candidate budget and conditionally omit degradation notice (#9924) Co-authored-by: Claude <noreply@anthropic.com> * fix: change permission status icon from red X to muted circle (#9928) The red xmark.circle.fill icon looked like a clickable remove button. Replace with an unfilled circle in textMuted color so it clearly reads as a 'not yet configured' status indicator. Co-authored-by: Claude <noreply@anthropic.com> * fix: inject X-Forwarded-For in gateway proxy and trust it conditionally in runtime (#9926) Co-authored-by: Claude <noreply@anthropic.com> * Slack Channel (Socket Mode) — First-Class Channel Ingress/Egress (#9907) * M1: Gateway Slack config, credentials, and credential watcher (#9872) * feat: add Slack channel config, credentials, and credential watcher to gateway Co-Authored-By: Claude <noreply@anthropic.com> * fix: add slackChannelChanged handler to credential watcher callback in index.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: guard Slack credential watcher callback with env var override check Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * feat: add /deliver/slack route, schema, and transport hints (#9899) Co-authored-by: Claude <noreply@anthropic.com> * feat: add Slack Socket Mode adapter and event normalization (#9901) Co-authored-by: Claude <noreply@anthropic.com> * feat: add Slack channel config endpoints to runtime (#9900) Co-authored-by: Claude <noreply@anthropic.com> * feat: add Slack channel card to macOS Connect UI (#9903) Co-authored-by: Claude <noreply@anthropic.com> * test: add gateway and runtime tests for Slack channel (#9905) Co-authored-by: Claude <noreply@anthropic.com> * docs: update architecture docs for Slack channel (#9906) Co-authored-by: Claude <noreply@anthropic.com> * fix: check process.env directly for slackFromEnv guard (#9914) Co-authored-by: Claude <noreply@anthropic.com> * fix: add JSON body guard and fix OpenAPI schema chatId/to aliasing (#9919) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove non-functional guardian row from Slack card and fix xbot- typo (#9920) Co-authored-by: Claude <noreply@anthropic.com> * fix: return stored credential state in Slack config error responses (#9921) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * feat: add locale-aware time-sent indicator on hover near copy icon (#9910) (#9927) Co-authored-by: Claude <noreply@anthropic.com> * fix: prevent multiple threads from appearing active in side nav (#9939) Rewrite the isSelected logic in threadItem() to use an exhaustive switch on windowState.selection. Previously, persistentThreadId and selection were checked independently, so when they pointed to different threads (e.g. after openConversationThread sets activeThreadId without updating selection), both threads evaluated as selected. Now, when selection is explicitly .thread(id) or .appEditing(_, threadId), only that thread is highlighted. The persistentThreadId fallback only applies when selection is .app or .none. Co-authored-by: Claude <noreply@anthropic.com> * feat: rename Wake Word tab to Voice and add PTT activation key picker (#9940) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add assistant-driven judgement principle to AGENTS.md (#9941) Co-authored-by: Claude <noreply@anthropic.com> * feat: add permission-aware PTT first-use flow (#9942) Co-authored-by: Claude <noreply@anthropic.com> * feat: add PTT key indicator to toolbar and menu bar tooltip (#9943) Co-authored-by: Claude <noreply@anthropic.com> * feat: move ElevenLabs TTS config into the Voice settings tab (#9944) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add deep-linking to specific settings tabs via IPC (#9945) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: always sync persistentThreadId on active thread change to prevent stale highlight (#9952) Co-authored-by: Claude <noreply@anthropic.com> * feat: add PTT state to system prompt and channel capabilities (#9953) Co-authored-by: Claude <noreply@anthropic.com> * fix: new thread button silently failing when active thread has assistant-only messages (#9955) The empty-thread-reuse guard in createThread() checked for the absence of user messages (!vm.messages.contains(where: { .role == .user })). This caused the guard to fire when the active thread had assistant-only content (e.g. greetings, notifications, restored sessions) but no user messages yet. The user would click "+" and nothing would happen. Tighten the guard to only reuse a thread when it is truly fresh: vm.messages.isEmpty (no messages at all), no sessionId (not a restored conversation), and not a private thread. Co-authored-by: Claude <noreply@anthropic.com> * fix: mark local embedding backend broken on embed-time init failures (#9956) Co-authored-by: Claude <noreply@anthropic.com> * fix: bypass PID liveness gate for custom socket transports and guard cleanup (#9957) Co-authored-by: Claude <noreply@anthropic.com> * fix: move FTS trigger recreation after base table DELETEs in clearAll (#9958) Co-authored-by: Claude <noreply@anthropic.com> * fix: guard scanText loop against zero-length regex matches (#9959) Co-authored-by: Claude <noreply@anthropic.com> * fix: compute degradation notice budget correctly without separator when no candidates (#9960) Co-authored-by: Claude <noreply@anthropic.com> * fix: reuse isPrivateAddress for trusted-peer detection in rate limiter (#9961) Co-authored-by: Claude <noreply@anthropic.com> * fix: add socket health check to detect alive-but-unresponsive daemons (#9962) Co-authored-by: Claude <noreply@anthropic.com> * fix: sort sidebar threads by createdAt instead of lastInteractedAt for stable ordering (#9964) Co-authored-by: Claude <noreply@anthropic.com> * feat: add IPC message for updating client settings (activation key) (#9963) Co-authored-by: Claude <noreply@anthropic.com> * Voice guardian timeout: route remaining copy through daemon (#9908) * voice: route guardian timeout copy through daemon composer * fix: address review feedback on guardian timeout copy PR * fix: preserve token revocation when disk write fails (#9965) Co-authored-by: Claude <noreply@anthropic.com> * feat: add assistant tool for changing PTT activation key (#9966) Co-authored-by: Claude <noreply@anthropic.com> * fix: ensure notification clicks navigate to the correct conversation thread (#9967) Three issues caused inconsistent notification-to-thread navigation: 1. openConversationThread set activeThreadId but never cleared windowState.selection — if the user was viewing a panel, app, or settings when clicking a notification, the UI stayed on that view instead of showing the chat thread. 2. ACTIVITY_COMPLETE notification handler ignored the sessionId in userInfo and just called showMainWindow() without deep-linking. 3. makeViewModel() passed an empty sessionId to activity notifications, making deep-linking impossible even if the handler extracted it. Fix: reset windowState.selection to nil in openConversationThread so the chat thread is always visible; extract sessionId in the ACTIVITY_COMPLETE handler; capture a weak viewModel reference to pass the real sessionId to activity notifications. Co-authored-by: Claude <noreply@anthropic.com> * fix: use pendingSettingsTab instead of notification for PTT indicator navigation (#9969) Co-authored-by: Claude <noreply@anthropic.com> * fix: only post activationKeyChanged when activation key is updated (#9970) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove duplicate ElevenLabs config from Connect tab (#9971) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: differentiate mic vs speech recognition in PTT permission prompt (#9972) Co-authored-by: Claude <noreply@anthropic.com> * fix: validate PTT key in system prompt and propagate metadata for queued messages (#9973) Co-authored-by: Claude <noreply@anthropic.com> * feat: add bundled UPDATES.md template for release bulletins (#9975) Introduces a release-note source template following the existing template conventions (comment lines with _ prefix, freeform markdown). The template will be materialized to ~/.vellum/workspace/UPDATES.md and used to surface release update notes to the assistant. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add release-block formatter/parser helpers for update bulletin (#9976) Pure, side-effect-free functions for working with release markers in the update bulletin file: generating markers, detecting duplicates, appending new blocks (merge-safe), and extracting version IDs. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add bulletin checkpoint state helpers for active/completed releases (#9977) Adds read/write helpers for tracking active and completed release bulletins via memory checkpoints. Includes deduplication, sorting, and graceful degradation for corrupt checkpoint content. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * test: add ConfigWatcher test harness for workspace prompt file watching (#9978) Adds deterministic test coverage for ConfigWatcher workspace file watcher behavior by mocking fs.watch and platform paths. Verifies that prompt file changes (SOUL.md, IDENTITY.md, USER.md, LOOKS.md) trigger session eviction, config.json changes trigger config refresh, and unknown files are ignored. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: sort sidebar threads by lastInteractedAt instead of createdAt (#9979) Threads now move to the top of the sidebar when messages are sent or received, but NOT when the thread is clicked/selected. The lastInteractedAt timestamp is already updated on user message send and assistant message arrival but not on thread selection, making it the correct sort key for this behavior. Co-authored-by: Claude <noreply@anthropic.com> * fix: remove incorrect ctrl+shift mapping and duplicate settings broadcast (#9980) Co-authored-by: Claude <noreply@anthropic.com> * fix: restore indentation on showingTrustRules property (#9981) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add UPDATES.md template contract test (#9982) * fix: open general Privacy settings when both permissions denied (#9983) Co-authored-by: Claude <noreply@anthropic.com> * feat: allow file_read/file_edit/file_write for UPDATES.md (#9984) Add UPDATES.md to the WORKSPACE_PROMPT_FILES allowlist so the assistant can read, edit, and write it without prompting. Adds corresponding permission checker tests. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: load workspace UPDATES.md into system prompt (#9985) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: evict sessions on UPDATES.md file changes (#9986) * feat: add startup sync to materialize current release bulletin (#9987) PR 12 of the UPDATES.md bulletin rollout plan. Implements syncUpdateBulletinOnStartup() which reads the bundled UPDATES.md template, strips comment lines, and appends a release block to the workspace UPDATES.md for the current APP_VERSION. Skips completed releases and avoids duplicating existing markers. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add bulletin behavior instructions to system prompt (#9988) PR 04 of the UPDATES.md bulletin rollout plan. Enhances the updates section in the system prompt with judgment-based behavioral instructions for how the assistant should handle update notes — surface relevant updates naturally, apply assistant-relevant changes silently, and delete UPDATES.md when all updates have been actioned. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add deletion completion and merge semantics to bulletin sync (#9989) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: deny all pending confirmations in UI, not just the first one (#9904) Server-side denyAllPending() denies every pending confirmation, but the client only updated the first one via firstIndex. Use a for loop to mark all pending confirmations as denied. Co-authored-by: Claude <noreply@anthropic.com> * feat: allow rm UPDATES.md in workspace scope (#9990) Add a default allow rule so the assistant can delete UPDATES.md without permission friction, mirroring the existing bootstrap delete rule. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: write bearer token file early in daemon startup (#9974) Move the http-token resolution and file write to right after ensureDataDir(), before DB init, Qdrant, or any other slow steps. The CLI polls for this file during gateway startup with a 30s timeout, which was being exceeded when Qdrant init was slow. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add atomic write path for bulletin sync (#9992) Replaces direct writeFileSync calls with a temp-file + rename pattern to prevent partial/truncated UPDATES.md writes if the process crashes mid-write. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add UPDATES.md to prompt config section (#9993) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: pipe real createdAt through IPC contract for stable thread ordering (#9994) Co-authored-by: Claude <noreply@anthropic.com> * feat: add thread reuse decision contract and candidate context (M1) (#9995) Co-authored-by: Claude <noreply@anthropic.com> * Release v0.3.16 (#9996) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * feat: wire bulletin sync into daemon startup lifecycle (#9997) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: use pickup framing for inbound non-guardian voice openers (#9991) * fix: match inner corner radius to outer in drawer theme toggle (#9998) Co-authored-by: Claude <noreply@anthropic.com> * docs: add update bulletin architecture, README, and AGENTS guidance (#10000) Document the new update bulletin system that surfaces release notes to the assistant via the system prompt. Adds architecture docs describing the data flow, checkpoint keys, and key source files. Adds README guidance for release maintainers. Adds AGENTS.md section on release update hygiene. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove invalid @escaping from tuple return type in PermissionPromptOverlay (#9999) @escaping is only valid on function parameters, not in tuple return types. Closures in returned tuples are inherently escaping. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: make createdAt backward-compatible in IPC and HTTP session decoding (#10001) Co-authored-by: Claude <noreply@anthropic.com> * perf: batch guardian queries and add destination_conversation_id index in thread-candidates (#10002) Co-authored-by: Claude <noreply@anthropic.com> * fix: normalize conversationId and escape titles in thread candidate handling (#10005) Co-authored-by: Claude <noreply@anthropic.com> * fix: extract topBarView to resolve Swift type-checker timeout in MainWindowView (#10006) The coreLayoutView computed property exceeded the Swift compiler's type-checker complexity limit. Extract the top toolbar into a separate topBarView property to reduce nesting depth. Also fix latent .wakeWord enum case (should be .voice) that was masked by the type-checker timeout. Co-authored-by: Claude <noreply@anthropic.com> * feat: update afternoon wake-up greeting to 'Wake up, my friend' (#10007) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve Swift type-checker timeout in MainWindowView (#10008) Extract topBarView from coreLayoutView to reduce expression complexity that caused the compiler to bail. Also fix latent .wakeWord reference (should be .voice) that was hidden by the type-checker timeout. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove duplicate topBarView declaration in MainWindowView (#10009) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: remove name from all wake-up greetings so assistant is unnamed at hatch (#10010) Co-authored-by: Claude <noreply@anthropic.com> * fix: add package.json fallback for APP_VERSION in bulletin sync (#10018) * test: add atomic write failure path test for bulletin sync (#10019) Simulates a write failure by making the temp directory read-only, then verifies that original file content is preserved, no temp file leftovers remain, and the function throws. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: align empty-template policy across template, tests, and docs (#10020) Make the bundled UPDATES.md template comment-only so no bulletin is materialized for releases without explicit update notes. Relax the contract test to allow empty/comment-only templates. Update bulletin tests to swap in a test template with real content during setup and restore the original afterwards, plus add a new test verifying the comment-only skip path. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: prevent clicking a thread from reordering it to the top (#10013) When an LRU-evicted ViewModel is re-created on thread click, history loading triggers the messages Combine publisher which calls updateLastInteracted, bumping the thread to the top of the sidebar. Skip updateLastInteracted while history is loading or not yet loaded. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: include assistant name in inbound voice pickup intro (#10022) * fix: harden getDefaultRuleTemplates against partial config mocks (#10031) Use optional chaining and runtime type guards in getDefaultRuleTemplates() so partial config mocks (missing sandbox/skills branches) don't crash rule generation. Adds a test covering the partial-config path. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: prevent empty thread from getting stuck on loading spinner (#10029) trimForBackground() blindly reset isHistoryLoaded to false, even for threads with no session. When switching back, loadHistoryIfNeeded bails (no sessionId) but isHistoryLoaded stays false, leaving the UI stuck on a loading spinner. Only reset when there's a session to reload from. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * Migrate browser relay clients to gateway ingress (#9938) * Route browser relay and clients through gateway * Exclude browser relay websocket from auth rate limiter * Address bot feedback on readiness probe and token logging * Gate browser relay ingress behind runtime proxy flag * Harden browser relay upgrades to local/private peers * fix: remove ok:false from slack deliver error response for consistency (#10034) Co-authored-by: Claude <noreply@anthropic.com> * docs: clarify warning field is POST-only in Slack config docs (#10035) Co-authored-by: Claude <noreply@anthropic.com> * fix: isolate per-table failures in FTS reconciliation (#10039) Co-authored-by: Claude <noreply@anthropic.com> * fix: move buffered cert reads inside TLS renewal fallback (#10040) Co-authored-by: Claude <noreply@anthropic.com> * fix: reset global regex lastIndex before extractReleaseIds loop (#10041) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve symlink targets in bulletin atomic write (#10042) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve 0.0.0-dev fallback for local development (#10043) Co-authored-by: Claude <noreply@anthropic.com> * fix: default sandbox to enabled when config is missing (#10044) Co-authored-by: Claude <noreply@anthropic.com> * docs: document token revocation failure semantics (#10045) Co-authored-by: Claude <noreply@anthropic.com> * fix: wire up ConfigWatcher test assertions and remove unused locals (#10046) Co-authored-by: Claude <noreply@anthropic.com> * fix: match custom socket check with resolveSocketPath semantics (#10047) Co-authored-by: Claude <noreply@anthropic.com> * fix: use deterministic mock for bulletin write failure test (#10048) Co-authored-by: Claude <noreply@anthropic.com> * fix: harden daemon startup lock and dev command cleanup (#10049) Co-authored-by: Claude <noreply@anthropic.com> * Fix browser relay host-spoof bypass in peer guard (#10037) * fix: decouple bulletin tests from real template file (#10051) Co-authored-by: Claude <noreply@anthropic.com> * chore: skip approval prompt before final sweep in safe-blitz (#10056) Co-authored-by: Claude <noreply@anthropic.com> * feat: change default host_bash trust policy from ask to allow (#10053) (#10057) Co-authored-by: Claude <noreply@anthropic.com> * chore: remove defunct --auto flag from safe-blitz (#10069) Co-authored-by: Claude <noreply@anthropic.com> * fix: re-throw aggregate FTS reconciliation failures (#10070) Co-authored-by: Claude <noreply@anthropic.com> * fix: update sandbox default test to match new enabled-by-default behavior (#10072) Co-authored-by: Claude <noreply@anthropic.com> * fix: handle dangling symlinks in bulletin atomic write (#10071) Co-authored-by: Claude <noreply@anthropic.com> * docs: fix daemon-restart token recovery semantics (#10073) Co-authored-by: Claude <noreply@anthropic.com> * fix: avoid killing unrelated processes via stale PID files in dev startup (#10074) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve actor isolation warnings in activation key observer (#10081) Co-authored-by: Claude <noreply@anthropic.com> * docs: remove --auto references and add deprecated alias in safe-blitz (#10082) Co-authored-by: Claude <noreply@anthropic.com> * fix: mark live-streaming threads as history-loaded so assistant activity updates are not dropped (#10086) Co-authored-…
…) (#9925) * M1: Land remote skill policy primitive and enable skills.sh-first baseline (#9789) * test: land remote skill policy primitive and enable skills.sh-first baseline Co-Authored-By: Claude <noreply@anthropic.com> * fix: decouple skills.sh risk check from blockSuspicious gate and coerce unknown risk labels - Remove the `policy.blockSuspicious &&` guard from the skills.sh risk threshold check so `maxSkillsShRisk` is always enforced independently of the Clawhub-specific blockSuspicious flag. - Validate that risk labels are known keys in SKILLS_SH_RISK_RANK and coerce unrecognized values to 'unknown' (fail closed) to prevent novel/typo'd risk strings from bypassing the threshold. - Add tests for both fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use Object.hasOwn for risk label validation to prevent prototype pollution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: exclude 'unknown' from valid maxSkillsShRisk threshold values Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M2: Add skills.sh discovery with audit report retrieval (#9824) * feat: add skills.sh discovery with audit report retrieval Co-Authored-By: Claude <noreply@anthropic.com> * fix: guard risk-rank lookup against prototype property labels in deriveOverallRisk Use Object.hasOwn(RISK_RANK, dim.risk) instead of checking RISK_RANK[dim.risk] !== undefined to prevent inherited prototype properties (toString, constructor, etc.) from bypassing the fail-closed check. Added test case covering this edge case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M3: Model-mediated security recommendation and user override flow (#9838) * feat: add model-mediated security recommendation and explicit override flow Co-Authored-By: Claude <noreply@anthropic.com> * fix: snapshot override entries and include unrecognized risk labels in flagged providers --------- Co-authored-by: Claude <noreply@anthropic.com> * M4: Install skills.sh skills into managed store via assistant flow (#9870) * feat: install skills.sh skills into managed store via assistant flow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: skip provenance from integrity hash and validate skill IDs before install Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * M5: Fallback to skills.sh when native capability fails (#9896) * feat: fallback to skills.sh when native capability fails Add skills.sh fallback skill and CLI wrapper for the search -> evaluate -> install workflow. The skill teaches the assistant to discover, evaluate, and install third-party skills from skills.sh with security audit visibility and user override. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: set exit code on JSON mode failures and use relative CLI path in SKILL.md - Add `if (!result.success) process.exitCode = 1` after JSON write in the install command so callers can detect failures via exit code even in JSON mode - Replace hardcoded `assistant/src/skills/skillssh-cli.ts` paths in SKILL.md with relative `src/skills/skillssh-cli.ts` so the skill works when the daemon runs from the assistant/ directory (and in packaged deployments) - Add exit code assertion to the JSON mode install failure test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use import.meta.main for direct-execution guard --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: show installed skill provenance and source links in UI Add provenance label derivation and source URL construction for installed skills. Distinguishes Vellum (bundled), third-party (skills.sh/Clawhub), user, and community skills with source metadata. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove dead voice source classifier branch (#9775) Co-authored-by: Claude <noreply@anthropic.com> * feat: extend voice mode timeout during tool execution (#9781) Co-authored-by: Claude <noreply@anthropic.com> * feat: capture app context for voice mode messages (#9786) Co-authored-by: Claude <noreply@anthropic.com> * feat: add TTS feedback during computer use escalation in voice mode (#9787) Co-authored-by: Claude <noreply@anthropic.com> * Use Sonnet 4.6 instead of Haiku for low-tier responses (#9779) * fix: render file upload surface inline instead of fallback chip (#9773) The file_upload surface was classified as a "chip only" surface in InlineSurfaceRouter, causing it to render as a tiny fallback chip instead of the full drag-and-drop FileUploadSurfaceView. Moved the view to the shared module and wired it up in the inline router. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore voice source classifier bypass for Fn-hold dictation (#9790) Co-authored-by: Claude <noreply@anthropic.com> * fix: rephrase computer-use prompt to avoid narrowing sensitive-data ban (#9791) Co-authored-by: Claude <noreply@anthropic.com> * perf: add compound index on memoryItems(scopeId, status) (#9792) Co-authored-by: Claude <noreply@anthropic.com> * fix: use selectableText modifier in ChatBubbleTextContent (#9793) Co-authored-by: Claude <noreply@anthropic.com> * feat: define standard HTTP error response format (#9794) Co-authored-by: Claude <noreply@anthropic.com> * feat: add per-bearer-token rate limiting for HTTP API (#9795) Co-authored-by: Claude <noreply@anthropic.com> * fix: add deduplication check in notification dispatch (#9796) Co-authored-by: Claude <noreply@anthropic.com> * feat: add circuit breaker for Qdrant client (#9797) Co-authored-by: Claude <noreply@anthropic.com> * feat: add FTS index reconciliation mechanism (#9799) Co-authored-by: Claude <noreply@anthropic.com> * refactor: extract PermissionChecker class from executor.ts (#9798) Co-authored-by: Claude <noreply@anthropic.com> * feat: add migration crash recovery for stalled migrations (#9800) Co-authored-by: Claude <noreply@anthropic.com> * fix: externalize onnxruntime-node in daemon build to fix dylib loading (#9802) bun build --compile bundles onnxruntime_binding.node into the binary and extracts it to a temp dir at runtime, but doesn't extract the sibling libonnxruntime.1.21.0.dylib — causing dlopen to fail. Fix by marking onnxruntime-node as --external so the compiled binary resolves it via node_modules/ at runtime, and copying the package (with onnxruntime-common) alongside the daemon binary. Also codesigns the native .node/.dylib files in the app bundle. Co-authored-by: Claude <noreply@anthropic.com> * perf: add index on notificationEvents(dedupeKey) (#9804) Co-authored-by: Claude <noreply@anthropic.com> * feat: surface Qdrant circuit-breaker state in memory retrieval (#9805) Co-authored-by: Claude <noreply@anthropic.com> * feat: add per-IP rate limiting fallback for unauthenticated endpoints (#9806) Co-authored-by: Claude <noreply@anthropic.com> * feat: add /v1/debug introspection endpoint (#9807) Co-authored-by: Claude <noreply@anthropic.com> * refactor: extract SecretDetectionHandler class from executor.ts (#9808) Co-authored-by: Claude <noreply@anthropic.com> * docs: design HTTP token refresh protocol for iOS client (#9809) Co-authored-by: Claude <noreply@anthropic.com> * fix: correct async return types and remove invalid local keyword in build.sh (#9810) - Fix async functions returning void instead of Promise<void> in session/daemon code - Remove invalid 'local' keyword used outside function scope in macOS build.sh Co-authored-by: Claude <noreply@anthropic.com> * refactor: split channel-delivery-store.ts into focused modules (#9811) Co-authored-by: Claude <noreply@anthropic.com> * refactor: migrate console.log/error/warn to structured Pino logger (#9812) Co-authored-by: Claude <noreply@anthropic.com> * refactor: split channel-guardian-store.ts into focused modules (#9813) Co-authored-by: Claude <noreply@anthropic.com> * refactor: split conversation-store.ts into focused modules (#9814) Co-authored-by: Claude <noreply@anthropic.com> * fix: prevent voice context prefix from leaking into chat UI, sanitize inputs, capture context earlier (#9815) Co-authored-by: Claude <noreply@anthropic.com> * refactor: migrate route error responses to standard format (#9816) Co-authored-by: Claude <noreply@anthropic.com> * fix: replace as-unknown-as casts with proper types in core modules (#9817) Co-authored-by: Claude <noreply@anthropic.com> * fix: add durable pause flag for conversation timeout during CU escalation (#9818) Co-authored-by: Claude <noreply@anthropic.com> * fix: use login names and reactions for safe-do reviewer activity gating (#9819) Co-authored-by: Claude <noreply@anthropic.com> * fix: sign all bundled node_modules files and strip non-runtime assets (#9820) codesign treats everything under Contents/MacOS/ as code objects, so all files in node_modules — not just native .node/.dylib binaries — must carry a valid signature. Also strip source files, docs, sourcemaps, and type declarations that aren't needed at runtime. Co-authored-by: Claude <noreply@anthropic.com> * feat: add HTTP request/response logging middleware (#9821) * fix: sign all bundled node_modules files and strip non-runtime assets codesign treats everything under Contents/MacOS/ as code objects, so all files in node_modules — not just native .node/.dylib binaries — must carry a valid signature. Also strip source files, docs, sourcemaps, and type declarations that aren't needed at runtime. Co-Authored-By: Claude <noreply@anthropic.com> * feat: add HTTP request/response logging middleware Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * fix: await async addMessage calls in tests (#9822) Co-authored-by: Claude <noreply@anthropic.com> * refactor: extract ToolApprovalHandler class from executor.ts (#9823) Co-authored-by: Claude <noreply@anthropic.com> * feat: add provider status to /v1/debug endpoint (#9825) Co-authored-by: Claude <noreply@anthropic.com> * perf: add database indexes, FK cascades, job timeout detection, and schema cleanup (#9826) Co-authored-by: Claude <noreply@anthropic.com> * fix: capture app context concurrently with transcription, check emptiness after sanitization (#9827) Co-authored-by: Claude <noreply@anthropic.com> * fix: replace any types with proper types in http-server WebSocket handlers (#9828) Co-authored-by: Claude <noreply@anthropic.com> * fix: destructure handler from webhook factory return values in gateway tests (#9829) Co-authored-by: Claude <noreply@anthropic.com> * feat: implement HTTP token refresh in iOS client (#9830) Co-authored-by: Claude <noreply@anthropic.com> * fix: reduce thread header spacing and open apps in view-only mode (#9831) - Shrink Threads heading from 18pt to 13pt - Remove vertical padding from divider above Threads header - Reduce Threads header vertical padding from 8pt to 4pt - Reset isAppChatOpen when opening an app so it defaults to view-only mode instead of sticky edit mode Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: await async addMessage/sendMessage calls in tests (#9832) Co-authored-by: Claude <noreply@anthropic.com> * fix: check conversationTimeoutPaused in isThinking subscriber (#9833) Co-authored-by: Claude <noreply@anthropic.com> * fix: run DictationContextCapture.capture() on MainActor instead of async let (#9835) Co-authored-by: Claude <noreply@anthropic.com> * fix: add authenticationRequired case to SessionErrorCategory switch (#9836) Co-authored-by: Claude <noreply@anthropic.com> * fix: defer PID file write until daemon is ready (#9839) Co-authored-by: Claude <noreply@anthropic.com> * fix: kill stale daemon processes before spawning new one (#9840) Co-authored-by: Claude <noreply@anthropic.com> * fix: clean up PID file on daemon startup crash (#9841) Co-authored-by: Claude <noreply@anthropic.com> * fix: validate daemon PID is alive in macOS client health check (#9842) Co-authored-by: Claude <noreply@anthropic.com> * fix: handle missing onnxruntime-node gracefully in compiled binary (#9843) Co-authored-by: Claude <noreply@anthropic.com> * fix: constellation graph — opaque rounded-square nodes, adjust spacing, remove .md files (#9845) - Replace circle node shapes with rounded squares (RoundedRectangle) - Add opaque VColor.background fill behind translucent color so edges don't show through nodes - Adjust edge lengths: center→category 200pt, category→subcategory 160pt, subcategory→skill 160pt for more breathing room at leaf level - Remove workspace .md files (IDENTITY.md, USER.md, etc.) from the Knowledge category — only actual skills appear in the graph - Fix exhaustive switch for new authenticationRequired SessionErrorCode Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve TypeScript type-safety issues in work-item handlers and test fixtures (#9846) - Fix unsafe type assertions in headlessLock cleanup (work-items.ts, work-item-runner.ts) - Add missing startedAt field to work item test fixtures (memory-regressions.test.ts) - Fix double-assertion for legacy trust rule migration cast (trust-store.ts) Co-authored-by: Claude <noreply@anthropic.com> * fix: add missing authenticationRequired case to ChatErrorToastView switch (#9847) PR #9836 added the authenticationRequired case to the SessionErrorCategory enum but missed updating the exhaustive switch in ChatErrorToastView. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle authenticationRequired case in macOS and iOS icon switches (#9848) Co-authored-by: Claude <noreply@anthropic.com> * fix: write PID file before throwing on daemon startup timeout (#9849) Co-authored-by: Claude <noreply@anthropic.com> * fix: add VELLUM_UNSAFE_AUTH_BYPASS to env registry known vars (#9850) Co-authored-by: Claude <noreply@anthropic.com> * fix: gracefully degrade when proactive TLS renewal fails (#9851) Co-authored-by: Claude <noreply@anthropic.com> * fix: defer semantic search until after early termination check (#9852) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9771 (#9854) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9757 (#9855) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9765 (#9853) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9753 — restore unsafe-inline for LLM-generated apps, refactor gallery to use addEventListener (#9856) Co-authored-by: Claude <noreply@anthropic.com> * feat: auto-deny pending tool confirmations in daemon on new user message (#9788) * feat: auto-deny pending tool confirmations in daemon when user sends a message Co-Authored-By: Claude <noreply@anthropic.com> * fix: move auto-deny to just before dispatchUserMessage Prevents auto-denying confirmations when the message is intercepted by secret-block, recording commands, or other early-return paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: auto-deny pending confirmations in HTTP message ingress path too Ensures the auto-deny behavior works for all clients, not just IPC. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove client-side auto-deny, daemon handles it now The daemon auto-denies pending confirmations when a new user message arrives, so the client no longer needs to duplicate this logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: clear stale pending-interactions entries on auto-deny When denyAllPendingConfirmations resolves prompter requests directly, the pending-interactions tracker entries become stale. Clean them up so channel approval flows don't see phantom pending approvals. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9769 (#9857) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9826 (#9861) Co-authored-by: Claude <noreply@anthropic.com> * fix: capture app context before transcription finalization (#9863) Co-authored-by: Claude <noreply@anthropic.com> * Trusted Contact Access (Chat-First) (#9530) * docs: add trusted contact access design doc (#9452) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: notify guardian when non-member requests access (#9459) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: handle guardian approval decision for access requests (#9460) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: activate trusted contact on successful verification (#9471) On successful verification of the 6-digit code, upserts assistant_ingress_members with status=active and policy=allow. The requester can immediately send messages after verification. Key changes: - validateAndConsumeChallenge now distinguishes guardian vs trusted contact verification via verificationType field - Identity-bound outbound sessions (trusted contacts) no longer create guardian bindings - New template for trusted contact verification success message - Existing guardian verification flow unchanged (backward compatible) Closes #9437 Co-authored-by: Harrison Ngo <harrison@vellum.ai> * feat: add HTTP routes for ingress member/invite management (#9475) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: add trusted contacts management skill (#9479) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: add notification signals for trusted contact lifecycle (#9481) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * docs: channel-agnostic rollout and operator runbook for trusted contacts (#9529) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: wrap notifyGuardianOfAccessRequest in try-catch for graceful degradation (#9641) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: scope notification dedupe key to approval request ID (#9643) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: add requestId to access request approval for callback button routing (#9644) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: restore guardian binding on outbound verification by distinguishing verification purpose (#9695) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate guardian code delivery failures to prevent premature requester notification (#9733) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: replace runtime-port URLs with gateway URLs in trusted-contacts skill (#9756) Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Harrison Ngo <harrison@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: close localhost /v1 runtime URL guard gap (#9859) * fix: guard PID cleanup to current process on startup failure (#9873) Co-authored-by: Claude <noreply@anthropic.com> * fix: use dynamic arch detection in incremental ONNX binary stripping (#9874) Co-authored-by: Claude <noreply@anthropic.com> * fix: defer jobs on QdrantCircuitOpenError instead of failing them permanently (#9875) Co-authored-by: Claude <noreply@anthropic.com> * fix: include destination in duplicate-delivery lookup (#9876) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove schema-only dedupe_key index that is never created at runtime (#9877) Co-authored-by: Claude <noreply@anthropic.com> * fix: replace full schedule scan with aggregate SQL counts in debug endpoint (#9878) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve classified risk level on permission-check exceptions (#9879) Co-authored-by: Claude <noreply@anthropic.com> * fix: properly handle async rejections from addPointerMessage, persistCallCompletionMessage, and sweepExpiredGuardianActions (#9880) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9795 (#9881) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove redundant scope_id+status index already covered by 3-column index (#9792) (#9882) Co-authored-by: Claude <noreply@anthropic.com> * Preserve user-uploaded images across context compaction and retry stripping (#9871) * fix: use getDbPath() for recovery instructions instead of hardcoded path (#9883) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9799 (#9884) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9805 (#9885) Co-authored-by: Claude <noreply@anthropic.com> * fix: report 'down' health when no active provider can be selected (#9887) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9806 (#9888) Co-authored-by: Claude <noreply@anthropic.com> * fix: handle undefined response in request logger for WebSocket upgrades (#9889) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9809 (#9890) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on PR #9816 (#9891) Co-authored-by: Claude <noreply@anthropic.com> * fix: accept empty sessionId on session errors as broadcast for 401 recovery (#9892) Co-authored-by: Claude <noreply@anthropic.com> * fix: await recursive drainQueue calls to prevent unhandled rejections (#9886) Co-authored-by: Claude <noreply@anthropic.com> * Voice ASK_GUARDIAN Generative Timeout + Callback Follow-Up (#9735) * M1: Guardian Action Follow-Up Data Model + Store Transitions (#9573) * feat: add guardian action follow-up data model and store transitions Co-Authored-By: Claude <noreply@anthropic.com> * fix: require expired status in follow-up state transitions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: restrict terminal follow-up transitions to finalizeFollowup only Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove none->awaiting_guardian_choice from progressFollowupState transitions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * feat: add daemon-injected guardian action copy generator (#9604) Co-authored-by: Claude <noreply@anthropic.com> * M3: Voice Timeout Flow Migration to Generated Assistant Turn (#9632) * feat: replace hardcoded voice timeout with generated assistant turn Co-Authored-By: Claude <noreply@anthropic.com> * fix: send guardian expiry notices from call-timeout path When markTimedOutWithReason is called in the call controller's consultation timeout, the guardian action request transitions from 'pending' to 'expired' immediately. The sweepExpiredGuardianActions sweep only looks for 'pending' requests, so these requests were never picked up by the sweep — meaning guardians never received the "question expired" notice (thread messages, channel replies). Extract the per-request notification logic from the sweep into a shared sendGuardianExpiryNotices helper. The call controller timeout path now captures deliveries before marking the request as timed out, then calls the helper to send expiry notices to all guardian destinations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M4: Late Reply Interception + Generated Guardian Follow-Up Prompt (#9650) * feat: intercept late guardian replies and initiate follow-up flow Co-Authored-By: Claude <noreply@anthropic.com> * fix: address review feedback on guardian timeout PR - Differentiate guardian_stale_answered vs guardian_stale_expired scenarios so answered-from-another-channel gets the correct message instead of the generic 'expired' text. Also update the mac path in session-process.ts to use the composed message for consistency. - Gate the expired guardian action late-reply interception on !hasCallbackData to prevent inline button presses from being misclassified as late answers. - Add request-code disambiguation for multiple expired deliveries, mirroring the existing pending-delivery disambiguation pattern, so late replies bind to the correct expired request. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * M5: Guardian Follow-Up Conversation Engine (Structured Decisions) (#9669) * feat: guardian follow-up conversation engine with structured decisions Co-Authored-By: Claude <noreply@anthropic.com> * fix: wire generator to mac channel and add followup disambiguation - Wire guardianFollowUpConversationGenerator to session-process.ts via module-level injection from lifecycle.ts, so the mac/IPC channel path can classify follow-up replies (previously always returned keep_pending) - Add request-code disambiguation for multiple awaiting_guardian_choice follow-up deliveries in inbound-message-handler.ts, matching the existing pattern used for pending and expired deliveries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * feat: execute callback/message-back actions and guardian completion replies (#9701) Co-authored-by: Claude <noreply@anthropic.com> * fix: harden late-reply interception edge cases (#9706) * fix: add non-empty guard to voice bullet regression test (#9688) Add expect(voiceBullet).not.toHaveLength(0) assertion before the negative assertions to prevent vacuous passes when the voice bullet format changes. Co-authored-by: Claude <noreply@anthropic.com> * fix: order pending guardian requests by recency in getPendingRequestByCallSessionId (#9689) Add ORDER BY created_at DESC to ensure the most recent pending request is returned when multiple exist for the same call session. Co-authored-by: Claude <noreply@anthropic.com> * fix: tighten gateway-only CI guard by removing temporary allowlist (#9690) Co-authored-by: Claude <noreply@anthropic.com> * fix: guard pointer emission against invalid origin conversations in relay-server (#9691) Wrap the cross-conversation pointer write in a try/catch so that a stale or invalid originConversationId doesn't abort the success/failure handler and leave the outbound call in a bad state. Co-authored-by: Claude <noreply@anthropic.com> * fix: keep polling in safe-do feedback loop when reviews are still pending (#9692) Instead of treating 'no new reviews within 3 minutes' as done, continue polling until both reviewers respond or a longer timeout is reached. Co-authored-by: Claude <noreply@anthropic.com> * fix: guardian intent routing — use normalized text, fix phone regex, preserve user content (#9693) - Use normalized text (filler-stripped) for direct guardian pattern matching - Fix phone regex: phone(?:\s+number)? to match 'verify my phone' - Preserve original user message when forcing guardian setup flow Co-authored-by: Claude <noreply@anthropic.com> * Persist recording-related messages across app restart (#9666) * M1: Persist original user message for _with_remainder recording intents (#9634) * feat: persist original user message for _with_remainder recording intents Co-Authored-By: Claude <noreply@anthropic.com> * fix: preserve displayContent when slash resolves to unknown When a _with_remainder message strips down to an unknown slash command, the unknown-slash branch in session-process.ts was persisting the stripped content directly instead of using displayContent. For example, "start recording /foo" would store "/foo" in the DB instead of the original text. Thread displayContent through the unknown-slash persistence path, mirroring the pattern used in persistUserMessage in session-messaging.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: preserve displayContent in drainQueue unknown-slash path Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * M2: Persist user + assistant messages for _only recording commands (#9647) * feat: persist user + assistant messages for _only recording commands Co-Authored-By: Claude <noreply@anthropic.com> * fix: add fallback for msg.task in misc.ts persistence calls * fix: sync session.messages with persisted recording command messages After persisting recording command user/assistant messages to SQLite via conversationStore.addMessage(), also push them to the session's in-memory messages array. This prevents divergence between DB and in-memory history that could break operations like regenerate() which rely on both sources. In sessions.ts the session is always available (created at handler entry). In misc.ts the session may or may not exist (task_submit creates conversations not sessions), so a defensive ctx.sessions.get() lookup is used. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * M3: Align finalization text and persist error-path messages (#9662) * feat: align finalization text and persist error-path messages Co-Authored-By: Claude <noreply@anthropic.com> * fix: guard error-path addMessage calls against persistence failures Wrap each error-path conversationStore.addMessage() call in finalizeAndPublishRecording() with try/catch so the IPC notification (assistant_text_delta + message_complete) is always sent regardless of whether the DB write succeeds. Logs a warning if the DB write fails. This prevents FK violations (conversation deleted), SQLite busy errors, or other persistence failures from blocking the ctx.send() calls and causing the function to fail unexpectedly instead of returning { success: false }. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * fix: guard session.messages push behind processing check to prevent agent loop corruption Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude <noreply@anthropic.com> * feat: replace timezone free-text input with searchable picker popover (#9696) Replace the error-prone free-text timezone input with a popover-based picker that shows a searchable list of all IANA timezones. Includes a one-click "Use system" button to populate from the Mac's timezone. Invalid input is now impossible since all selections come from valid IANA identifiers. Also fixes a pre-existing Swift build error in ChatBubbleTextContent where a ternary mixed incompatible TextSelectability types. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: blitz command — use check-pr-reviews for polling, add rebase handling, update README (#9697) - Use check-pr-reviews script for review polling to detect Codex rate limits - Add explicit rebase-and-retry for conflicted approved PRs - Update README slash commands section for new blitz behavior Co-authored-by: Claude <noreply@anthropic.com> * fix: address gateway-only guard test feedback from PR #9684 (#9698) Co-authored-by: Claude <noreply@anthropic.com> * fix: restore retry logic for transient errors in video fetch after gateway migration (#9699) Co-authored-by: Claude <noreply@anthropic.com> * fix: re-trigger reviews after rebase in safe-do conflict resolution (#9700) After resolving merge conflicts and force-pushing, re-request Codex/Devin reviews before proceeding to merge, since the post-rebase diff may differ. Co-authored-by: Claude <noreply@anthropic.com> * fix: harden late-reply interception in inbound-message-handler - Skip late-reply interception for callback payloads - Don't initiate follow-up when answerCall already succeeded - Handle failed late-followup start instead of falling through - Add else branch for channel path followup failure with stale message Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jason Zhou <jason@vellum.ai> Co-authored-by: Vellum Assistant <assistant@vellum.ai> * fix: check follow-up state transition results before replying to guardian (#9708) Co-authored-by: Claude <noreply@anthropic.com> * fix: gate follow-up action execution on successful state transition (#9709) Co-authored-by: Claude <noreply@anthropic.com> * fix: address review feedback on guardian follow-up executor - Fix outbound_message_copy fallback to include lateAnswerText and correct audience framing - Fix counterparty resolution to use toNumber for outbound calls (via initiatedFromConversationId heuristic) - Gate follow-up action execution on successful progressFollowupState transition to prevent duplicate side effects (#9710) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve counterparty by call direction and include late answer in outbound SMS fallback (#9713) Co-authored-by: Claude <noreply@anthropic.com> * docs: guardian timeout/follow-up architecture docs and hardening guards (#9721) Co-authored-by: Claude <noreply@anthropic.com> * fix: address holistic review - remove provider guard, pass mac generator, complete test scenarios Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: pass copy generator to remaining stale followup composer calls in mac path (#9774) * Remove open_tasks_window IPC message and task_list_show side effect (#9723) Co-authored-by: Claude <noreply@anthropic.com> * Update task tool tests for conversation-only output (#9727) Co-authored-by: Claude <noreply@anthropic.com> * Update docs to reflect conversation-first task management (#9731) Co-authored-by: Claude <noreply@anthropic.com> * Remove macOS standalone Tasks window (#9732) Co-authored-by: Claude <noreply@anthropic.com> * fix: add explicit instruction allowing computer use navigation to consoles and dashboards (#9734) The model's built-in safety training was causing the assistant to refuse navigating to websites like the Anthropic console for API key creation, even though the user explicitly asked. The existing 'never type passwords' rule was being over-extrapolated into 'never go near anything credential-adjacent.' Add a counter-instruction clarifying that navigating to consoles, dashboards, login pages, and admin panels is expected behavior when the user asks. Co-authored-by: Claude <noreply@anthropic.com> * fix: fall back to any valid codesigning identity before ad-hoc (#9736) The build script only checked for 'Developer ID Application' and 'Apple Development'/'Mac Developer' certificates. If a self-signed codesigning certificate (e.g. 'Vellum Development') was the only identity available, it was skipped and the build fell through to ad-hoc signing. Ad-hoc signing produces a new code hash on every rebuild, causing macOS TCC to revoke all granted permissions (Accessibility, Screen Recording, Microphone, etc.). Add a generic fallback that picks any valid codesigning identity before resorting to ad-hoc, so permissions persist across rebuilds. Co-authored-by: Claude <noreply@anthropic.com> * Fix test assertion for updated done-status error message (#9741) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve macOS build warnings (#9743) Co-authored-by: Claude <noreply@anthropic.com> * Trim redundant words from BOOTSTRAP.md first message (#9740) Co-authored-by: Claude <noreply@anthropic.com> * M1: Enrich SOUL.md template (#9737) * Enrich SOUL.md template with warmth and personality Co-Authored-By: Claude <noreply@anthropic.com> * Fix: use asterisk italic to avoid comment stripping The _ prefix is treated as a comment by stripCommentLines and gets removed from the system prompt. Using * for italic instead. --------- Co-authored-by: Claude <noreply@anthropic.com> * Warm up IDENTITY.md template with inviting field descriptions (#9738) Co-authored-by: Claude <noreply@anthropic.com> * Enrich USER.md template with coaching prompts and sign-off (#9739) Co-authored-by: Claude <noreply@anthropic.com> * Add memory persistence, group chat, and platform formatting guidance to system prompt (#9742) Co-authored-by: Claude <noreply@anthropic.com> * fix: propagate schedule source to task-runner conversations (#9745) When a schedule triggers a task via run_task:<id>, the task runner created conversations without source: 'schedule', defaulting to 'user'. This caused schedule-triggered task threads to appear in the regular Threads section instead of the Scheduled section in the macOS sidebar. Add optional source field to TaskRunOptions and pass 'schedule' from both the scheduler tick and the manual 'run now' handler. Co-authored-by: Claude <noreply@anthropic.com> * feat: add startup warning when VELLUM_DAEMON_NOAUTH is set (#9747) Co-authored-by: Claude <noreply@anthropic.com> * feat: add database index on conversations(updatedAt) (#9748) Co-authored-by: Claude <noreply@anthropic.com> * feat: add startup warning when DISABLE_HTTP_AUTH is set (#9749) Co-authored-by: Claude <noreply@anthropic.com> * chore: rename colliding migration files (026, 027) (#9750) Co-authored-by: Claude <noreply@anthropic.com> * fix: add max deferral limit to prevent indefinite job deferral (#9752) Co-authored-by: Claude <noreply@anthropic.com> * security: reduce TLS certificate validity from 10 years to 1 year (#9751) Co-authored-by: Claude <noreply@anthropic.com> * security: remove unsafe-inline from CSP script-src directive (#9753) Co-authored-by: Claude <noreply@anthropic.com> * docs: document why hooks-runner.test.ts is skipped (#9754) Co-authored-by: Claude <noreply@anthropic.com> * feat: add periodic background cleanup for gateway dedup caches (#9755) Co-authored-by: Claude <noreply@anthropic.com> * feat: add skill tool name collision detection (#9757) Co-authored-by: Claude <noreply@anthropic.com> * fix: add accessibility label to timezone picker clear button (#9758) Co-authored-by: Claude <noreply@anthropic.com> * feat: use local file path for recording playback and thumbnails (#9744) Pass the on-disk file path from daemon to client via IPC so recordings play directly from the local file instead of fetching via HTTP. The client generates thumbnails natively with AVAssetImageGenerator, eliminating the ffmpeg dependency and fixing the 3:4 portrait fallback. Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: record last_fix_push_time after re-request comments in blitz (#9759) Co-authored-by: Claude <noreply@anthropic.com> * fix: use original user text for guardian conversation title generation (#9760) Co-authored-by: Claude <noreply@anthropic.com> * fix: pass copy generator to remaining stale followup composer calls in mac path Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Aaron Levin <awlevin@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: siddseethepalli <siddseethepalli@gmail.com> Co-authored-by: Jason Zhou <jason@vellum.ai> Co-authored-by: Vellum Assistant <assistant@vellum.ai> * fix: add missing await on addMessage calls and catch unhandled rejection in sweep --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Jason Zhou <jason@vellum.ai> Co-authored-by: Vellum Assistant <assistant@vellum.ai> Co-authored-by: Aaron Levin <awlevin@users.noreply.github.com> Co-authored-by: siddseethepalli <siddseethepalli@gmail.com> * fix: improve auto-deny UX when user chats during pending confirmation (#9893) - Update decisionContext to instruct the agent to stop and respond to the user's message instead of immediately re-requesting the tool - Mark confirmation card as denied in the UI when user sends a follow-up Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: index user-uploaded attachments for asset_search (#9895) * fix: index user-uploaded attachments so asset_search can find them User attachments (e.g. zip files dropped into chat) were embedded inline in the message content JSON but never inserted into the attachments table. This meant asset_search returned nothing and asset_materialize couldn't locate them. Now persistUserMessage() calls uploadAttachment() + linkAttachmentToMessage() for each user attachment after persisting the message, with content-hash dedup to avoid duplicates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: validate user attachments before indexing Add validateAttachmentUpload() check before uploadAttachment() to ensure dangerous file extensions and unsupported MIME types are rejected, matching the validation that the HTTP upload route already enforces. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review feedback on PR #9871 (#9894) * fix: address review feedback on PR #9871 - Restore keep-latest logic for top-level images in media retry so context-too-large recovery can actually reduce payload size - Fix stripContextSummaryTags to find closing tag by indexOf instead of endsWith, preventing summary corruption when preserved images append trailing text blocks - Carry forward previously preserved images from existing summary message across multiple compaction cycles * fix: address Codex and Devin review feedback on PR #9894 - Use lastIndexOf for </context_summary> tag to avoid truncating summaries that legitimately mention the tag - Cap preserved image blocks at 5 to prevent unbounded accumulation across compaction cycles * fix: buffer existing cert/key before proactive TLS renewal attempt (#9911) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove nonce from CSP style-src to allow unsafe-inline styles (#9912) Co-authored-by: Claude <noreply@anthropic.com> * fix: map x86_64 to x64 in ONNX binary stripping for Intel Mac support (#9913) Co-authored-by: Claude <noreply@anthropic.com> * fix: skip local embedding backend after onnxruntime import failure (#9915) Co-authored-by: Claude <noreply@anthropic.com> * fix: scope stale-daemon kills to current workspace PID (#9917) Co-authored-by: Claude <noreply@anthropic.com> * fix: reject custom secret patterns that produce zero-length matches (#9918) Co-authored-by: Claude <noreply@anthropic.com> * fix: recreate FTS triggers after dropping them in clearAll (#9916) Co-authored-by: Claude <noreply@anthropic.com> * fix: align dedup check with DB unique index on (decision, channel) (#9922) Co-authored-by: Claude <noreply@anthropic.com> * fix: persist token to disk before updating in-memory auth state in rotation flow (#9923) Co-authored-by: Claude <noreply@anthropic.com> * fix: allow zero candidate budget and conditionally omit degradation notice (#9924) Co-authored-by: Claude <noreply@anthropic.com> * fix: change permission status icon from red X to muted circle (#9928) The red xmark.circle.fill icon looked like a clickable remove button. Replace with an unfilled circle in textMuted color so it clearly reads as a 'not yet configured' status indicator. Co-authored-by: Claude <noreply@anthropic.com> * fix: inject X-Forwarded-For in gateway proxy and trust it conditionally in runtime (#9926) Co-authored-by: Claude <noreply@anthropic.com> * Slack Channel (Socket Mode) — First-Class Channel Ingress/Egress (#9907) * M1: Gateway Slack config, credentials, and credential watcher (#9872) * feat: add Slack channel config, credentials, and credential watcher to gateway Co-Authored-By: Claude <noreply@anthropic.com> * fix: add slackChannelChanged handler to credential watcher callback in index.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: guard Slack credential watcher callback with env var override check Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * feat: add /deliver/slack route, schema, and transport hints (#9899) Co-authored-by: Claude <noreply@anthropic.com> * feat: add Slack Socket Mode adapter and event normalization (#9901) Co-authored-by: Claude <noreply@anthropic.com> * feat: add Slack channel config endpoints to runtime (#9900) Co-authored-by: Claude <noreply@anthropic.com> * feat: add Slack channel card to macOS Connect UI (#9903) Co-authored-by: Claude <noreply@anthropic.com> * test: add gateway and runtime tests for Slack channel (#9905) Co-authored-by: Claude <noreply@anthropic.com> * docs: update architecture docs for Slack channel (#9906) Co-authored-by: Claude <noreply@anthropic.com> * fix: check process.env directly for slackFromEnv guard (#9914) Co-authored-by: Claude <noreply@anthropic.com> * fix: add JSON body guard and fix OpenAPI schema chatId/to aliasing (#9919) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove non-functional guardian row from Slack card and fix xbot- typo (#9920) Co-authored-by: Claude <noreply@anthropic.com> * fix: return stored credential state in Slack config error responses (#9921) Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * feat: add locale-aware time-sent indicator on hover near copy icon (#9910) (#9927) Co-authored-by: Claude <noreply@anthropic.com> * fix: prevent multiple threads from appearing active in side nav (#9939) Rewrite the isSelected logic in threadItem() to use an exhaustive switch on windowState.selection. Previously, persistentThreadId and selection were checked independently, so when they pointed to different threads (e.g. after openConversationThread sets activeThreadId without updating selection), both threads evaluated as selected. Now, when selection is explicitly .thread(id) or .appEditing(_, threadId), only that thread is highlighted. The persistentThreadId fallback only applies when selection is .app or .none. Co-authored-by: Claude <noreply@anthropic.com> * feat: rename Wake Word tab to Voice and add PTT activation key picker (#9940) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add assistant-driven judgement principle to AGENTS.md (#9941) Co-authored-by: Claude <noreply@anthropic.com> * feat: add permission-aware PTT first-use flow (#9942) Co-authored-by: Claude <noreply@anthropic.com> * feat: add PTT key indicator to toolbar and menu bar tooltip (#9943) Co-authored-by: Claude <noreply@anthropic.com> * feat: move ElevenLabs TTS config into the Voice settings tab (#9944) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add deep-linking to specific settings tabs via IPC (#9945) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: always sync persistentThreadId on active thread change to prevent stale highlight (#9952) Co-authored-by: Claude <noreply@anthropic.com> * feat: add PTT state to system prompt and channel capabilities (#9953) Co-authored-by: Claude <noreply@anthropic.com> * fix: new thread button silently failing when active thread has assistant-only messages (#9955) The empty-thread-reuse guard in createThread() checked for the absence of user messages (!vm.messages.contains(where: { .role == .user })). This caused the guard to fire when the active thread had assistant-only content (e.g. greetings, notifications, restored sessions) but no user messages yet. The user would click "+" and nothing would happen. Tighten the guard to only reuse a thread when it is truly fresh: vm.messages.isEmpty (no messages at all), no sessionId (not a restored conversation), and not a private thread. Co-authored-by: Claude <noreply@anthropic.com> * fix: mark local embedding backend broken on embed-time init failures (#9956) Co-authored-by: Claude <noreply@anthropic.com> * fix: bypass PID liveness gate for custom socket transports and guard cleanup (#9957) Co-authored-by: Claude <noreply@anthropic.com> * fix: move FTS trigger recreation after base table DELETEs in clearAll (#9958) Co-authored-by: Claude <noreply@anthropic.com> * fix: guard scanText loop against zero-length regex matches (#9959) Co-authored-by: Claude <noreply@anthropic.com> * fix: compute degradation notice budget correctly without separator when no candidates (#9960) Co-authored-by: Claude <noreply@anthropic.com> * fix: reuse isPrivateAddress for trusted-peer detection in rate limiter (#9961) Co-authored-by: Claude <noreply@anthropic.com> * fix: add socket health check to detect alive-but-unresponsive daemons (#9962) Co-authored-by: Claude <noreply@anthropic.com> * fix: sort sidebar threads by createdAt instead of lastInteractedAt for stable ordering (#9964) Co-authored-by: Claude <noreply@anthropic.com> * feat: add IPC message for updating client settings (activation key) (#9963) Co-authored-by: Claude <noreply@anthropic.com> * Voice guardian timeout: route remaining copy through daemon (#9908) * voice: route guardian timeout copy through daemon composer * fix: address review feedback on guardian timeout copy PR * fix: preserve token revocation when disk write fails (#9965) Co-authored-by: Claude <noreply@anthropic.com> * feat: add assistant tool for changing PTT activation key (#9966) Co-authored-by: Claude <noreply@anthropic.com> * fix: ensure notification clicks navigate to the correct conversation thread (#9967) Three issues caused inconsistent notification-to-thread navigation: 1. openConversationThread set activeThreadId but never cleared windowState.selection — if the user was viewing a panel, app, or settings when clicking a notification, the UI stayed on that view instead of showing the chat thread. 2. ACTIVITY_COMPLETE notification handler ignored the sessionId in userInfo and just called showMainWindow() without deep-linking. 3. makeViewModel() passed an empty sessionId to activity notifications, making deep-linking impossible even if the handler extracted it. Fix: reset windowState.selection to nil in openConversationThread so the chat thread is always visible; extract sessionId in the ACTIVITY_COMPLETE handler; capture a weak viewModel reference to pass the real sessionId to activity notifications. Co-authored-by: Claude <noreply@anthropic.com> * fix: use pendingSettingsTab instead of notification for PTT indicator navigation (#9969) Co-authored-by: Claude <noreply@anthropic.com> * fix: only post activationKeyChanged when activation key is updated (#9970) Co-authored-by: Claude <noreply@anthropic.com> * fix: remove duplicate ElevenLabs config from Connect tab (#9971) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: differentiate mic vs speech recognition in PTT permission prompt (#9972) Co-authored-by: Claude <noreply@anthropic.com> * fix: validate PTT key in system prompt and propagate metadata for queued messages (#9973) Co-authored-by: Claude <noreply@anthropic.com> * feat: add bundled UPDATES.md template for release bulletins (#9975) Introduces a release-note source template following the existing template conventions (comment lines with _ prefix, freeform markdown). The template will be materialized to ~/.vellum/workspace/UPDATES.md and used to surface release update notes to the assistant. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add release-block formatter/parser helpers for update bulletin (#9976) Pure, side-effect-free functions for working with release markers in the update bulletin file: generating markers, detecting duplicates, appending new blocks (merge-safe), and extracting version IDs. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add bulletin checkpoint state helpers for active/completed releases (#9977) Adds read/write helpers for tracking active and completed release bulletins via memory checkpoints. Includes deduplication, sorting, and graceful degradation for corrupt checkpoint content. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * test: add ConfigWatcher test harness for workspace prompt file watching (#9978) Adds deterministic test coverage for ConfigWatcher workspace file watcher behavior by mocking fs.watch and platform paths. Verifies that prompt file changes (SOUL.md, IDENTITY.md, USER.md, LOOKS.md) trigger session eviction, config.json changes trigger config refresh, and unknown files are ignored. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: sort sidebar threads by lastInteractedAt instead of createdAt (#9979) Threads now move to the top of the sidebar when messages are sent or received, but NOT when the thread is clicked/selected. The lastInteractedAt timestamp is already updated on user message send and assistant message arrival but not on thread selection, making it the correct sort key for this behavior. Co-authored-by: Claude <noreply@anthropic.com> * fix: remove incorrect ctrl+shift mapping and duplicate settings broadcast (#9980) Co-authored-by: Claude <noreply@anthropic.com> * fix: restore indentation on showingTrustRules property (#9981) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add UPDATES.md template contract test (#9982) * fix: open general Privacy settings when both permissions denied (#9983) Co-authored-by: Claude <noreply@anthropic.com> * feat: allow file_read/file_edit/file_write for UPDATES.md (#9984) Add UPDATES.md to the WORKSPACE_PROMPT_FILES allowlist so the assistant can read, edit, and write it without prompting. Adds corresponding permission checker tests. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: load workspace UPDATES.md into system prompt (#9985) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: evict sessions on UPDATES.md file changes (#9986) * feat: add startup sync to materialize current release bulletin (#9987) PR 12 of the UPDATES.md bulletin rollout plan. Implements syncUpdateBulletinOnStartup() which reads the bundled UPDATES.md template, strips comment lines, and appends a release block to the workspace UPDATES.md for the current APP_VERSION. Skips completed releases and avoids duplicating existing markers. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add bulletin behavior instructions to system prompt (#9988) PR 04 of the UPDATES.md bulletin rollout plan. Enhances the updates section in the system prompt with judgment-based behavioral instructions for how the assistant should handle update notes — surface relevant updates naturally, apply assistant-relevant changes silently, and delete UPDATES.md when all updates have been actioned. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add deletion completion and merge semantics to bulletin sync (#9989) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: deny all pending confirmations in UI, not just the first one (#9904) Server-side denyAllPending() denies every pending confirmation, but the client only updated the first one via firstIndex. Use a for loop to mark all pending confirmations as denied. Co-authored-by: Claude <noreply@anthropic.com> * feat: allow rm UPDATES.md in workspace scope (#9990) Add a default allow rule so the assistant can delete UPDATES.md without permission friction, mirroring the existing bootstrap delete rule. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: write bearer token file early in daemon startup (#9974) Move the http-token resolution and file write to right after ensureDataDir(), before DB init, Qdrant, or any other slow steps. The CLI polls for this file during gateway startup with a 30s timeout, which was being exceeded when Qdrant init was slow. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add atomic write path for bulletin sync (#9992) Replaces direct writeFileSync calls with a temp-file + rename pattern to prevent partial/truncated UPDATES.md writes if the process crashes mid-write. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add UPDATES.md to prompt config section (#9993) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: pipe real createdAt through IPC contract for stable thread ordering (#9994) Co-authored-by: Claude <noreply@anthropic.com> * feat: add thread reuse decision contract and candidate context (M1) (#9995) Co-authored-by: Claude <noreply@anthropic.com> * Release v0.3.16 (#9996) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * feat: wire bulletin sync into daemon startup lifecycle (#9997) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: use pickup framing for inbound non-guardian voice openers (#9991) * fix: match inner corner radius to outer in drawer theme toggle (#9998) Co-authored-by: Claude <noreply@anthropic.com> * docs: add update bulletin architecture, README, and AGENTS guidance (#10000) Document the new update bulletin system that surfaces release notes to the assistant via the system prompt. Adds architecture docs describing the data flow, checkpoint keys, and key source files. Adds README guidance for release maintainers. Adds AGENTS.md section on release update hygiene. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove invalid @escaping from tuple return type in PermissionPromptOverlay (#9999) @escaping is only valid on function parameters, not in tuple return types. Closures in returned tuples are inherently escaping. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: make createdAt backward-compatible in IPC and HTTP session decoding (#10001) Co-authored-by: Claude <noreply@anthropic.com> * perf: batch guardian queries and add destination_conversation_id index in thread-candidates (#10002) Co-authored-by: Claude <noreply@anthropic.com> * fix: normalize conversationId and escape titles in thread candidate handling (#10005) Co-authored-by: Claude <noreply@anthropic.com> * fix: extract topBarView to resolve Swift type-checker timeout in MainWindowView (#10006) The coreLayoutView computed property exceeded the Swift compiler's type-checker complexity limit. Extract the top toolbar into a separate topBarView property to reduce nesting depth. Also fix latent .wakeWord enum case (should be .voice) that was masked by the type-checker timeout. Co-authored-by: Claude <noreply@anthropic.com> * feat: update afternoon wake-up greeting to 'Wake up, my friend' (#10007) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve Swift type-checker timeout in MainWindowView (#10008) Extract topBarView from coreLayoutView to reduce expression complexity that caused the compiler to bail. Also fix latent .wakeWord reference (should be .voice) that was hidden by the type-checker timeout. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove duplicate topBarView declaration in MainWindowView (#10009) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: remove name from all wake-up greetings so assistant is unnamed at hatch (#10010) Co-authored-by: Claude <noreply@anthropic.com> * fix: add package.json fallback for APP_VERSION in bulletin sync (#10018) * test: add atomic write failure path test for bulletin sync (#10019) Simulates a write failure by making the temp directory read-only, then verifies that original file content is preserved, no temp file leftovers remain, and the function throws. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: align empty-template policy across template, tests, and docs (#10020) Make the bundled UPDATES.md template comment-only so no bulletin is materialized for releases without explicit update notes. Relax the contract test to allow empty/comment-only templates. Update bulletin tests to swap in a test template with real content during setup and restore the original afterwards, plus add a new test verifying the comment-only skip path. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: prevent clicking a thread from reordering it to the top (#10013) When an LRU-evicted ViewModel is re-created on thread click, history loading triggers the messages Combine publisher which calls updateLastInteracted, bumping the thread to the top of the sidebar. Skip updateLastInteracted while history is loading or not yet loaded. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: include assistant name in inbound voice pickup intro (#10022) * fix: harden getDefaultRuleTemplates against partial config mocks (#10031) Use optional chaining and runtime type guards in getDefaultRuleTemplates() so partial config mocks (missing sandbox/skills branches) don't crash rule generation. Adds a test covering the partial-config path. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: prevent empty thread from getting stuck on loading spinner (#10029) trimForBackground() blindly reset isHistoryLoaded to false, even for threads with no session. When switching back, loadHistoryIfNeeded bails (no sessionId) but isHistoryLoaded stays false, leaving the UI stuck on a loading spinner. Only reset when there's a session to reload from. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * Migrate browser relay clients to gateway ingress (#9938) * Route browser relay and clients through gateway * Exclude browser relay websocket from auth rate limiter * Address bot feedback on readiness probe and token logging * Gate browser relay ingress behind runtime proxy flag * Harden browser relay upgrades to local/private peers * fix: remove ok:false from slack deliver error response for consistency (#10034) Co-authored-by: Claude <noreply@anthropic.com> * docs: clarify warning field is POST-only in Slack config docs (#10035) Co-authored-by: Claude <noreply@anthropic.com> * fix: isolate per-table failures in FTS reconciliation (#10039) Co-authored-by: Claude <noreply@anthropic.com> * fix: move buffered cert reads inside TLS renewal fallback (#10040) Co-authored-by: Claude <noreply@anthropic.com> * fix: reset global regex lastIndex before extractReleaseIds loop (#10041) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve symlink targets in bulletin atomic write (#10042) Co-authored-by: Claude <noreply@anthropic.com> * fix: preserve 0.0.0-dev fallback for local development (#10043) Co-authored-by: Claude <noreply@anthropic.com> * fix: default sandbox to enabled when config is missing (#10044) Co-authored-by: Claude <noreply@anthropic.com> * docs: document token revocation failure semantics (#10045) Co-authored-by: Claude <noreply@anthropic.com> * fix: wire up ConfigWatcher test assertions and remove unused locals (#10046) Co-authored-by: Claude <noreply@anthropic.com> * fix: match custom socket check with resolveSocketPath semantics (#10047) Co-authored-by: Claude <noreply@anthropic.com> * fix: use deterministic mock for bulletin write failure test (#10048) Co-authored-by: Claude <noreply@anthropic.com> * fix: harden daemon startup lock and dev command cleanup (#10049) Co-authored-by: Claude <noreply@anthropic.com> * Fix browser relay host-spoof bypass in peer guard (#10037) * fix: decouple bulletin tests from real template file (#10051) Co-authored-by: Claude <noreply@anthropic.com> * chore: skip approval prompt before final sweep in safe-blitz (#10056) Co-authored-by: Claude <noreply@anthropic.com> * feat: change default host_bash trust policy from ask to allow (#10053) (#10057) Co-authored-by: Claude <noreply@anthropic.com> * chore: remove defunct --auto flag from safe-blitz (#10069) Co-authored-by: Claude <noreply@anthropic.com> * fix: re-throw aggregate FTS reconciliation failures (#10070) Co-authored-by: Claude <noreply@anthropic.com> * fix: update sandbox default test to match new enabled-by-default behavior (#10072) Co-authored-by: Claude <noreply@anthropic.com> * fix: handle dangling symlinks in bulletin atomic write (#10071) Co-authored-by: Claude <noreply@anthropic.com> * docs: fix daemon-restart token recovery semantics (#10073) Co-authored-by: Claude <noreply@anthropic.com> * fix: avoid killing unrelated processes via stale PID files in dev startup (#10074) Co-authored-by: Claude <noreply@anthropic.com> * fix: resolve actor isolation warnings in activation key observer (#10081) Co-authored-by: Claude <noreply@anthropic.com> * docs: remove --auto references and add deprecated alias in safe-blitz (#10082) Co-authored-by: Claude <noreply@anthropic.com> * fix: mark live-streaming threads as history-loaded so assistant activity updates are not dropped (#10086) Co-authored-…
When the guardian approves an access request, starts outbound verification using the existing channel-guardian service. When denied, sends a refusal reply to the requester. Handles stale decisions and idempotency.
Closes #9436
Part of #9432