Skip to content

fix(contacts): show Assistant badge for assistant-type contacts (LUM-1009)#26239

Merged
vex-assistant-bot[bot] merged 2 commits into
mainfrom
devin/1776464043-lum-1009-contact-type-badge
Apr 17, 2026
Merged

fix(contacts): show Assistant badge for assistant-type contacts (LUM-1009)#26239
vex-assistant-bot[bot] merged 2 commits into
mainfrom
devin/1776464043-lum-1009-contact-type-badge

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented Apr 17, 2026

ContactTypeBadge was switching on role, which for trusted contacts is always "contact" regardless of whether the contact is a human or an AI assistant. The "assistant" case was therefore unreachable and assistant-type contacts rendered as "Human". This PR models the badge's input as a closed Kind enum and adds a convenience initializer that derives the variant from a contact's role and contactType together — role == "guardian" wins, otherwise contactType == "assistant" selects the assistant variant, otherwise human. Call sites are updated to pass the enum (system Guardian/Assistant slots) or derive the kind from the payload (list rows, detail header).

Why this is the right shape

  • Human/assistant is the contactType distinction (assistant/src/contacts/types.ts); role only separates guardian from other contacts. The old API conflated them.
  • A String? parameter let invalid values ("assistant" passed as a role) compile and silently fall through. A closed enum makes the variant set exhaustive and checked at the call site, following the Swift API Design Guidelines and the "make invalid states unrepresentable" guidance from WWDC19 — Modern Swift API Design.
  • The guardian branch is unchanged; the fix is purely display-layer with no data-model changes.

Alternatives considered and rejected

  • Add contactType: String? alongside role: String? and check both in the switch. Rejected — still stringly-typed, same foot-gun for the next caller.
  • Drop role and take only contactType. Rejected — loses the guardian distinction, which is a role, not a contactType.
  • Have the badge read ContactPayload directly. Rejected — two call sites (the system "Your Assistant" slot and the guardian header in ContactsContainerView) don't have a ContactPayload to hand in.

Root cause analysis

  1. The badge was introduced as a thin wrapper keyed on a single string before the assistant/human distinction was exposed as a trusted contact in the UI.
  2. contactType was added later on the data model but the badge was never updated to read it; a stringly-typed API hid the drift.
  3. Warning sign we missed: the "assistant" case in the badge was unreachable from any real ContactPayload.role value — an enum would have surfaced this at compile time.
  4. Prevention: prefer enums over String? for closed display-variant sets; derive variants at the call site from the domain model rather than forwarding raw role strings.

Review & Testing Checklist for Human

  • macOS CI is not run for this repo — please verify the Xcode build locally before merging.
  • Visually confirm in the macOS client:
    • A trusted-contact whose contactType == "assistant" now renders with the "Assistant" label and sparkles icon (previously "Human" with a user icon).
    • Guardian rows (sidebar + detail header) still render as "Guardian" with the shield icon.
    • The system "Your Assistant" row and header still render as "Assistant".
    • Regular human contacts still render as "Human".
  • Confirm no other call sites of ContactTypeBadge exist outside Features/Contacts/ that would need updating (grep is clean on this branch).

Link to Devin session: https://app.devin.ai/sessions/36392abaa0224828ae0be4c00218e6c8
Requested by: @ashleeradka


Open with Devin

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@linear
Copy link
Copy Markdown

linear Bot commented Apr 17, 2026

LUM-1009 ContactTypeBadge shows 'Human' for assistant-type contacts

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab9590e5fb

ℹ️ 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".

contactListRow(
name: "You",
role: guardian.role,
badgeKind: ContactTypeBadge.Kind(role: guardian.role, contactType: guardian.contactType),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use a valid initializer for ContactTypeBadge.Kind

ContactTypeBadge.Kind is an enum with only cases, but this call constructs it with Kind(role:contactType:); that initializer is defined on ContactTypeBadge, not on Kind. This introduces a compile-time error in the macOS client (and the same pattern appears again in this file at line 86), so the contacts feature cannot be built from this commit.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 9523949. The init(role:contactType:) now lives on ContactTypeBadge.Kind (where the mapping actually belongs), so ContactTypeBadge.Kind(role:contactType:) resolves, and the call site in ContactDetailView was updated to ContactTypeBadge(kind: .init(role:contactType:)). Resolved.

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +23 to +31
init(role: String?, contactType: String?) {
if role == "guardian" {
self.kind = .guardian
} else if contactType == "assistant" {
self.kind = .assistant
} else {
self.kind = .human
}
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Behavioral change: assistant badge now keyed on contactType instead of role

The old ContactTypeBadge matched role == "assistant" to show the Assistant badge. The new init(role:contactType:) at clients/macos/vellum-assistant/Features/Contacts/ContactTypeBadge.swift:23-31 instead checks contactType == "assistant". This means any contact where role == "assistant" but contactType is nil or something other than "assistant" will now display as "Human" instead of "Assistant". Conversely, contacts where role is not "assistant" but contactType == "assistant" will now show the Assistant badge. This appears intentional (the docstring describes this logic), but the reviewer should confirm this matches the backend data model — specifically whether there are existing contacts where role and contactType disagree on the assistant classification.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional and matches the backend. role is typed as 'guardian' | 'contact' in the schema (<ref_snippet file="/home/ubuntu/repos/vellum-assistant/assistant/src/memory/schema/contacts.ts" lines="11-11" />, default "contact") and contactType is typed as 'human' | 'assistant' (<ref_snippet file="/home/ubuntu/repos/vellum-assistant/assistant/src/memory/schema/contacts.ts" lines="14-14" />, default "human") — they're orthogonal. role == "assistant" was never a valid backend value; the old ContactTypeBadge case matching it was unreachable, which is the bug this PR fixes. The assistant/human distinction is contactType's job, so keying on it is the correct fix. Resolved.

Copy link
Copy Markdown
Contributor

@vex-assistant-bot vex-assistant-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vex ✦ Review: Approved

Clean type-safe fix. The root cause analysis is correct:

  • role is guardian | contact, never assistant
  • contactType is human | assistant — this is the right field to key on
  • Old code matched role == "assistant" which was unreachable for trusted-contact-assistants

The closed Kind enum with init(role:, contactType:) is the right approach — eliminates the stringly-typed matching entirely. All 4 call sites updated correctly. Compile error from Codex review addressed in follow-up commit.

LGTM ✅

@vex-assistant-bot
Copy link
Copy Markdown
Contributor

@devin-ai review this PR

@vex-assistant-bot
Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

ℹ️ 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".

@vex-assistant-bot vex-assistant-bot Bot merged commit fc053db into main Apr 17, 2026
6 checks passed
@vex-assistant-bot vex-assistant-bot Bot deleted the devin/1776464043-lum-1009-contact-type-badge branch April 17, 2026 23:40
siddseethepalli pushed a commit that referenced this pull request Apr 17, 2026
…1009) (#26239)

* fix(contacts): show Assistant badge for assistant-type contacts (LUM-1009)

* Move role/contactType derivation onto Kind for valid initializer

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
siddseethepalli added a commit that referenced this pull request Apr 17, 2026
…ation gaps (#26271)

* Fix Chrome extension allowlist ID and clarify README dev setup (#26259)

Update the canonical allowlist to use the correct published CWS
extension ID (hphbdmpffeigpcdjkckleobjmhhokpne). Restructure the
Chrome extension README to clearly explain the allowlist merge
strategy, separate the macOS app (automatic) path from the manual
native messaging setup, and show how dev + prod extensions work
side-by-side.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(clients): enable non-contiguous glyph layout for NSTextView-backed code views (#26242)

TextKit 1 defaults NSLayoutManager.allowsNonContiguousLayout to false,
which forces full-document glyph layout from character 0 on the main
thread whenever a glyph range is queried. Attaching an NSTextView to
its scroll view (setDocumentView: -> _setSuperview: ->
setNeedsDisplayInRect: -> _glyphRangeForBoundingRect:) triggers that
query during makeNSView, producing multi-second hangs on large code
blocks.

Opt into non-contiguous layout on every TextKit 1 stack we build via
NSViewRepresentable so glyph generation is confined to the requested
bounding rect.

Also replace NSLayoutManager.ensureLayout(for:) in the code-view
sizeThatFits paths with direct lineCount * fixedLineHeight math: the
text container is unbounded horizontally (no wrapping) and paragraph
style pins minimumLineHeight == maximumLineHeight, so the geometry is
exact and avoids a second O(glyph count) main-thread path.

Fixes VELLUM-ASSISTANT-MACOS-J2.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ashlee@vellum.ai <ashlee@vellum.ai>

* fix(contacts): show Assistant badge for assistant-type contacts (LUM-1009) (#26239)

* fix(contacts): show Assistant badge for assistant-type contacts (LUM-1009)

* Move role/contactType derivation onto Kind for valid initializer

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(llm-callsite): UI override state divergence, null-as-delete, migration gaps

- deepMergeOverwrite: null on scalar/null targets assigns null (preserves
  nullable config fields like activeHoursStart); null on object targets
  still deletes (call-site clearing). Fixes regression where PATCH with
  null for nullable fields was deleted then re-defaulted.
- InferenceServiceCard: override confirmation dialog only fires when the
  resolved provider ID actually changes, not on mode-only toggles where
  both old and new resolve to the same provider.
- CallSiteOverridesSheet: per-row Save uses replaceCallSiteOverride
  (clear-then-set) so stale daemon-side leaves are removed. The
  partial-update setCallSiteOverride would retain fields the draft nil'd.
- CallSiteOverrideRow: merge consecutive .padding modifiers into single
  EdgeInsets call per macOS AGENTS.md layout rule.
- SettingsStore: add replaceCallSiteOverride for full-entry replacement.

---------

Co-authored-by: Noa Flaherty <noa@vellum.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ashlee@vellum.ai <ashlee@vellum.ai>
siddseethepalli pushed a commit that referenced this pull request Apr 17, 2026
…1009) (#26239)

* fix(contacts): show Assistant badge for assistant-type contacts (LUM-1009)

* Move role/contactType derivation onto Kind for valid initializer

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
siddseethepalli added a commit that referenced this pull request Apr 18, 2026
…es} (#26159)

* config(llm): add unified llm schema with call-site enum and profile refines (#26089)

* config(llm): add unified llm schema with call-site enum and profile refines

* fix(llm-schema): replace deepPartialObject helper with explicit .partial().extend()

Zod 4's readonly shape typing tripped TS2542 in the LSP for the generic walker.
Inline the one-level expansion for ContextWindowSchema and switch the superRefine
issue code to the string literal (Zod 4 deprecated ZodIssueCode).

* config(llm): add resolveCallSiteConfig resolver with deep merge (#26094)

* config(llm): add resolveCallSiteConfig resolver with deep merge

* fix(llm-resolver): deep-clone nested objects so resolved configs are isolated snapshots

Codex flagged that the merge helper aliased nested objects from llm.default
when no override touched them, so a caller mutating the returned config
would silently corrupt the source. Recurse into plain-object sources
unconditionally and add a regression test.

* config(llm): add llm field to AssistantConfigSchema (no behavior change) (#26095)

* config(llm): add llm field to AssistantConfigSchema (no behavior change)

* fix(llm-schema): add field-level defaults so partial llm configs don't trigger full config reset

Codex flagged that requiring all LLMConfigBase fields meant the loader's
leaf-deletion recovery couldn't repair partial/invalid llm blocks — falling
through to cloneDefaultConfig() and discarding the user's other valid
settings. Add .default(...) to every leaf so LLMSchema.parse({}) returns a
fully-defaulted object, matching the pattern used by sibling config schemas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* providers: accept callSite in per-call config; resolve via resolveCallSiteConfig (#26102)

* workspace: migrate scattered LLM config keys into unified llm structure (#26101)

* workspace: migrate scattered LLM config keys into unified llm structure

* fix(migration): preserve existing llm subtree; map notification intent to both call sites

Codex flagged two issues:
- The migration assignment replaced config.llm wholesale, destroying any
  pre-existing llm.callSites/profiles when llm.default was absent. Now
  merges into existing config.llm, preserving non-conflicting entries.
- notifications.decisionModelIntent drives both notification classification
  and preference extraction, but the migration only seeded
  notificationDecision. Now seeds both call sites.

* memory: route extraction/consolidation/retrieval through call-site IDs (#26106)

* memory: route narrative/pattern/summarization/starters through call-site IDs (#26107)

* notifications: route decision and preference extraction through call-site IDs (#26109)

* calls+watcher: route guardian copy and watch handlers through call-site IDs (#26105)

* utility: route classifier and analyzer LLM calls through call-site IDs (#26111)

* macos(settings): migrate InferenceServiceCard reads/writes to llm.default.* (#26113)

* workspace+conversation: route commit message and title through call-site IDs (#26112)

* ui: route identity intro and empty-state greeting through call-site IDs (#26108)

* daemon: thread callSite through processMessage options and adapter callbacks (#26115)

* daemon: thread callSite through processMessage options and adapter callbacks

* fix(callsite-threading): complete interface contract and server.ts symmetry

Devin flagged two gaps in PR #26115:
- ProcessConversationContext interface missing callSite in its
  runAgentLoop options type (works via structural typing but contract
  was incomplete; mocks would silently drop the field).
- DaemonServer.persistAndProcessMessage didn't thread callSite to
  conversation.runAgentLoop, while DaemonServer.processMessage did.
  Aligned.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(callsite): don't default unspecified callers to 'mainAgent'

Codex flagged that defaulting to mainAgent for every turn routes them
through the new RetryProvider call-site resolver, which reads from
llm.default — but config-model.setModel still writes to services.inference
without syncing llm.default. Result: stale/incompatible model IDs after a
model switch.

Defer the cutover. agent-loop turns now keep using the legacy modelIntent
path (turnCallSite = options?.callSite, no fallback). PRs 7-11 still
explicitly pass callSite and route through the new resolver as intended.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* heartbeat: pass callSite: 'heartbeatAgent' instead of speed kwarg (#26125)

* filing: pass callSite: 'filingAgent' instead of speed kwarg (#26124)

* runtime/analyze-conversation: route through callSite: 'analyzeConversation' (#26126)

* subagent: pass callSite: 'subagentSpawn' when spawning isolated agents (#26122)

* calls: route the call agent loop through callSite: 'callAgent' (#26123)

* macos(settings): add SettingsStore APIs for per-call-site overrides (#26128)

* macos(settings): add SettingsStore APIs for per-call-site overrides

* fix(callsite-overrides): harden setCallSiteOverrides against dup-id crash and batch divergence

Devin and Codex flagged two issues:
- Dictionary(uniqueKeysWithValues:) crashes if callers pass duplicate
  CallSiteOverride.id values (external input — must be tolerant). Switch
  to Dictionary(_:uniquingKeysWith:) with last-write-wins.
- Batch updates locally cleared entries omitted from the input but only
  PATCHed entries that were present, so omitted entries appeared cleared
  in the UI but reappeared on next sync. Now the PATCH payload includes
  NSNull clears for every catalog entry not in the batch, aligning remote
  with local.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(callsite-overrides): null entire entry on clear so non-UI leaves get cleared too

Codex P2 (PR #26128 cycle 2): clearCallSiteOverride only nulled
provider/model/profile, but call-site config supports additional leaves
(maxTokens, effort, speed, thinking, contextWindow). If those were set
via manual edits, the UI would report cleared while the daemon kept
applying hidden overrides.

Switch the PATCH payload from { provider: null, model: null, profile: null }
to a single null on the entry itself. The Zod fragment treats null as
absent, so the resolver falls back to llm.default. Same fix applies to the
omitted-catalog-entry clears in setCallSiteOverrides batch.

Tests updated to assert the new shape.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* macos(settings): confirm default-provider switch when call-site overrides exist (#26133)

* macos(settings): show 'N call-site overrides' badge with read-only list sheet (#26135)

* macos(settings): show 'N call-site overrides' badge with read-only list sheet

* fix(comments): drop PR-number breadcrumbs in callsite override files

Devin flagged that comments referencing PR 22/23/24 violate clients/AGENTS.md
'Comment Quality' rule (no breadcrumbs). Replaced with timeless descriptions
of code intent.

* macos(settings): make per-task override sheet editable with provider/model pickers (#26136)

* macos(settings): make per-task override sheet editable with provider/model pickers

* fix(callsite-sheet): preserve external updates and seed override from active default provider

Codex flagged two P1s:
- syncDraftsFromStore compared drafts against the NEW persisted value to
  decide 'touched', so external store updates were treated as user edits
  and got overwritten by Save All. Track the previously-persisted value
  in lastSyncedFromStore and consider a row touched only when the draft
  differs from that baseline.
- Toggling 'Override default' on initialized provider from
  providerIds.first instead of the user's actual default provider, which
  could pin the wrong provider on save. Pass the user's default provider
  into CallSiteOverrideRow and seed from it.

* fix(callsite-sheet): use entry-level null path for cleared rows in saveAll/resetAll

Devin flagged that saveAll() and resetAll() were passing all-nil entries
to setCallSiteOverrides, which routed them through the field-level null
path (provider/model/profile = null). That left advanced leaves
(maxTokens, effort, temperature, contextWindow) untouched on the daemon.

Fix:
- saveAll(): filter to entries with hasOverride == true; toggled-off rows
  fall through to the entry-level null path.
- resetAll(): pass an empty list so every catalog entry hits the
  entry-level null path.

* config(llm): remove deprecated scattered LLM keys (#26140)

* fix(config-loader): treat JSON null as key deletion in deepMergeOverwrite (#26153)

* fix(agent-loop): default user-initiated turns to callSite: 'mainAgent' (#26154)

* fix(meet-join): migrate consent-monitor + session-manager to callSite contract (#26155)

* fix(macos): atomic provider+model save via single PATCH (#26156)

* fix(cleanup): remove dead code, refresh comments, add migration test, update docs (#26157)

* fix(r2): catalog test count, skill self-knowledge doc, AGENTS.md, loader docstring (#26158)

* fix(llm-callsite): refresh stale docstring, restore overflow budget, restore SettingsStore fallback (#26252)

* fix(llm-callsite): route provider transport and field precedence through callSite (#26254)

* fix(llm-callsite): pass CI + address subagent/thinking/temperature review comments (#26258)

* test(extension-id-guard): allow CWS URL matches; mirrors main PR #26263 (#26270)

* fix(llm-callsite): UI override state divergence, null-as-delete, migration gaps (#26271)

* Fix Chrome extension allowlist ID and clarify README dev setup (#26259)

Update the canonical allowlist to use the correct published CWS
extension ID (hphbdmpffeigpcdjkckleobjmhhokpne). Restructure the
Chrome extension README to clearly explain the allowlist merge
strategy, separate the macOS app (automatic) path from the manual
native messaging setup, and show how dev + prod extensions work
side-by-side.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(clients): enable non-contiguous glyph layout for NSTextView-backed code views (#26242)

TextKit 1 defaults NSLayoutManager.allowsNonContiguousLayout to false,
which forces full-document glyph layout from character 0 on the main
thread whenever a glyph range is queried. Attaching an NSTextView to
its scroll view (setDocumentView: -> _setSuperview: ->
setNeedsDisplayInRect: -> _glyphRangeForBoundingRect:) triggers that
query during makeNSView, producing multi-second hangs on large code
blocks.

Opt into non-contiguous layout on every TextKit 1 stack we build via
NSViewRepresentable so glyph generation is confined to the requested
bounding rect.

Also replace NSLayoutManager.ensureLayout(for:) in the code-view
sizeThatFits paths with direct lineCount * fixedLineHeight math: the
text container is unbounded horizontally (no wrapping) and paragraph
style pins minimumLineHeight == maximumLineHeight, so the geometry is
exact and avoids a second O(glyph count) main-thread path.

Fixes VELLUM-ASSISTANT-MACOS-J2.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ashlee@vellum.ai <ashlee@vellum.ai>

* fix(contacts): show Assistant badge for assistant-type contacts (LUM-1009) (#26239)

* fix(contacts): show Assistant badge for assistant-type contacts (LUM-1009)

* Move role/contactType derivation onto Kind for valid initializer

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(llm-callsite): UI override state divergence, null-as-delete, migration gaps

- deepMergeOverwrite: null on scalar/null targets assigns null (preserves
  nullable config fields like activeHoursStart); null on object targets
  still deletes (call-site clearing). Fixes regression where PATCH with
  null for nullable fields was deleted then re-defaulted.
- InferenceServiceCard: override confirmation dialog only fires when the
  resolved provider ID actually changes, not on mode-only toggles where
  both old and new resolve to the same provider.
- CallSiteOverridesSheet: per-row Save uses replaceCallSiteOverride
  (clear-then-set) so stale daemon-side leaves are removed. The
  partial-update setCallSiteOverride would retain fields the draft nil'd.
- CallSiteOverrideRow: merge consecutive .padding modifiers into single
  EdgeInsets call per macOS AGENTS.md layout rule.
- SettingsStore: add replaceCallSiteOverride for full-entry replacement.

---------

Co-authored-by: Noa Flaherty <noa@vellum.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ashlee@vellum.ai <ashlee@vellum.ai>

* fix(llm-callsite): seed latency-optimized defaults and fix guardian provider routing (#26275)

* fix(meet-bot): address review feedback — Docker build, scraper races, audio capture, storage writer (#26264)

* fix(meet): chat concurrency, dispose teardown, and wake adapter fidelity (#26265)

* fix: heartbeat dual-emit, analysis dedup, test hermiticity, credential executor discovery (#26266)

* fix: model default fallback, empty-response nudge scan (#26268)

- Update FALLBACK_DEFAULT_MODEL to claude-opus-4-7 + test
- Fix resolveModel to check Anthropic catalog (not just current default)
  so stale persisted defaults (e.g. claude-opus-4-6) don't get sent
  to non-Anthropic providers
- Fix priorAssistantHadVisibleText backward scan to check ALL prior
  assistant messages, not just the most recent one

Addresses review feedback from PRs #26247, #26164.

* fix(meet): TTS stream races, barge-in tracking, ffmpeg error classification (#26267)

* Fix extension-id-sync-guard test after canonical ID update (#26263)

The guard test asserts that canonical extension IDs appear only in the
allowlist config file. After updating the canonical ID to match the
published CWS extension, it now collides with CWS URLs in README and
browser-execution.ts. Fix by stripping CWS URLs before checking for
bare ID occurrences, and ignore .codex-worktrees (repo copies).
Also remove hardcoded CWS ID from README in favor of reading from
the canonical config.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(llm-callsite): seed latency-optimized defaults, fix guardian provider routing, clean stale comments

- Add LATENCY_OPTIMIZED_CALLSITE_DEFAULTS to schema for new installs
- Create migration 040 to seed latency-optimized call-site entries for existing workspaces
- Fix guardian-action-generators to use getConfiguredProvider() instead of bypassing call-site resolution
- Restore commitMessage maxTokens: 120 and temperature: 0.2 via call-site defaults
- Remove stale PR-reference comments from analyze-conversation.ts and voice-session-bridge.ts

Addresses consolidated review feedback from PRs #26101-#26140.

---------

Co-authored-by: Noa Flaherty <noa@vellum.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(retry): stop forwarding contextWindow/provider to provider request body (#26280)

* chore(skills): regenerate catalog.json

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Noa Flaherty <noa@vellum.ai>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ashlee@vellum.ai <ashlee@vellum.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant