Skip to content

fix(macos): paginate sidebar conversation list on cold launch#31924

Merged
siddseethepalli merged 1 commit into
mainfrom
do/macos-scheduled-pagination
May 24, 2026
Merged

fix(macos): paginate sidebar conversation list on cold launch#31924
siddseethepalli merged 1 commit into
mainfrom
do/macos-scheduled-pagination

Conversation

@siddseethepalli
Copy link
Copy Markdown
Contributor

Summary

  • ConversationRestorer single-shot fetched 50 rows and never paginated, so users with hundreds of scheduled/background conversations saw only a small recent-activity slice in the sidebar (e.g. 14 of 1,856 in one report).
  • Add fetchAllConversationPages helper that loops until hasMore: false and apply it to both the foreground and background fetches. Page size of 200 with a 50-page cap (10k total) matches the existing loadAllRemainingConversations pattern.
  • macOS analogue of LUM-1618 (fixed for web in fix(web): paginate conversation list + preserve displayOrder (LUM-1618, LUM-1619) #31472).

Original prompt

User noticed the macOS sidebar's "Scheduled" section showed only 14 conversations despite the SQLite DB holding ~1,856 active scheduled conversations across 26+ cron jobs (Location Pulse: 912, Velissa NOW Refresh: 478, Oura Pulse: 259, etc.). Tracing the data path showed the macOS client made a single GET ?conversationType=background&limit=50 and ignored the daemon's hasMore flag — the same bug the web client hit and fixed in #31472. Wanted the parallel fix shipped to macOS.

…runcate

ConversationRestorer single-shot fetched 50 rows from
?conversationType=background and never paginated, so users with many
scheduled conversations saw a small recent-activity slice (e.g. 14 of
1856 in one report). This is the macOS analogue of LUM-1618 which was
fixed for web in #31472.

Add fetchAllConversationPages helper that loops until hasMore is
false (with a 50-page safety cap), used for both the foreground and
background fetches in fetchConversationList. Page size is 200 to match
ConversationListStore.loadAllRemainingConversations and reduce cold-
launch round-trips on macOS, which talks to the daemon over loopback.
@siddseethepalli siddseethepalli merged commit 470caa9 into main May 24, 2026
@siddseethepalli siddseethepalli deleted the do/macos-scheduled-pagination branch May 24, 2026 17:00
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: 3bdbd2b73c

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

dvargasfuertes pushed a commit that referenced this pull request May 25, 2026
…l sync fan-out (#32002)

* fix(daemon+web): per-conversation seen-state event replaces list-level sync fan-out

Conversation switches that landed on an unseen conversation were
triggering a full sidebar drain — ~14 paginated /conversations
requests (limit=50, offsets 0..350 × foreground + background) on
every switch, even when the conversation list itself didn't change
shape.

Root cause: `handleRecordSeen` and `handleMarkUnread` published
`conversation_list_invalidated` + a `sync_changed` carrying the
`conversationsList` tag. Web consumed the tag via
`useAssistantSyncStream` and `webSyncRouter` and invoked the
debounced full-list refetch (`fetchConversationList` drains
`hasMore=false`, twice per fetch). Seen state is per-conversation
attention metadata, not list-shaped, so the list-level fan-out was
the wrong primitive.

This swaps both routes onto a new `publishConversationSeenChanged`
that emits a single typed `conversation_seen_changed` event
carrying the canonical post-mutation state (hasUnseen,
latestAssistantMessageAt, lastSeenAssistantMessageAt as epoch ms).
The web's `useAssistantSyncStream` (chat-layout scope, always
mounted) patches the cached conversation row in-place via the new
`applyConversationSeenStateLocal` helper. No list invalidation, no
refetch, no drain.

Originating client's echo is a harmless idempotent re-patch — its
optimistic update (`markConversationSeenLocal`) already wrote the
same state. Sibling tabs/devices receive the typed event and patch.
macOS client doesn't yet handle this event; its sidebar will catch
up on the next unrelated list refresh (acceptable for seen state,
which is purely visual).

Tests:
- New daemon test `conversation-seen-changed-publish.test.ts` covers
  both routes' publish behavior, no-state-change skip, and asserts
  `publishConversationListAndMetadataChanged` is NOT called.
- New web tests for the parser (5 cases incl. timestamp narrowing),
  the cache patcher (4 cases incl. defensive null preservation), and
  the sync-stream handler (2 cases asserting no list invalidation).

Refs: LUM-1618 (PR #31472 introduced the drain pagination loop on web),
PR #31924 (macOS analog using fetchAllConversationPages).

* fix: address CI failures from PR #32002

Three CI failures all caused by the seen-state PR; one design
issue surfaced by the cross-domain import rule, two test-shape
issues from the behavior change.

1) **Lint (web) — cross-domain import**: the new import of
   `applyConversationSeenStateLocal` (in `domains/conversations/`)
   from `domains/chat/hooks/use-assistant-sync-stream.ts` tripped
   `local/no-cross-domain-imports`. The hook was already a
   cross-cutting concern — it routes assistant-global SSE events
   into caches across avatar, identity, conversations, sounds,
   schedules, feature flags, and home-feed — mounted at
   RootLayout and described as 'always mounted on every
   authenticated route' in its own header. Per the conventions
   doc, foundational/cross-cutting concerns 'always top-level,
   even if currently consumed by one domain'. Moved the hook
   (and its test) to `src/hooks/`. Updated the two consumers
   (`root-layout.tsx` import, dynamic import in the test).

2) **Type Check (assistant)**: `mockGetAttentionState` in
   `conversation-seen-changed-publish.test.ts` inferred every
   nullable field of its return shape as the literal type of its
   default implementation (e.g. `lastSeenAssistantMessageId:
   null` → typed as `null`). A `.mockImplementationOnce` that
   returns the same row with non-null fields (the
   already-seen-conversation path) is then incompatible. Added
   an explicit `MockAttentionRow` type mirroring the store's
   `AttentionState` shape and applied it as the mock's return
   annotation so overrides accept the full `string | null`
   union.

3) **Test (assistant) — `conversation-sync-tags.test.ts`**: two
   cases (`record seen`, `mark unread`) still asserted the old
   list-level fan-out — exactly the behavior this PR removed.
   Rewrote both to assert the new contract: a single
   `conversation_seen_changed` typed event carrying the
   canonical post-mutation state, with explicit negative
   assertions that `sync_changed` and
   `conversation_list_invalidated` are NOT emitted. Mirrors the
   shape used by the new `conversation-seen-changed-publish`
   regression suite, but exercised against the real
   `assistantEventHub` rather than mocks.

* refactor(seen-signal): drop list umbrella for content-only reasons; GET-and-patch on web

Replaces the typed `conversation_seen_changed` event (PR #32002 Phase 1-2)
with a per-conversation sync-tag + GET-and-patch loop on web. macOS keeps
the legacy `conversation_list_invalidated` broadcast (now scoped to
`targetInterfaceId: "macos"`) until the Electron cutover.

## Hub (`assistant-event-hub.ts`)

- New `targetInterfaceId?: InterfaceId` option on `publish()` and
  `broadcastMessage()`, composing with `targetClientId`/`targetCapability`/
  `excludeClientId`.
- Inline title-update `conversation_list_invalidated` emission is now
  macOS-scoped with an electron-cutover TODO.

## Sync helpers (`resource-sync-events.ts`)

- New `SHAPE_CHANGING_REASONS = {"created", "deleted", "reordered"}`.
- `publishConversationListAndMetadataChanged` only prepends the
  `conversationsList` umbrella tag for shape-changing reasons.
  Content-only reasons (`seen_changed`, `renamed`) emit per-conversation
  metadata tags only.
- New `broadcastConversationListInvalidatedToMacos()` helper centralizes
  the macOS-only legacy broadcast. Same electron-cutover TODO.
- Removed the typed `publishConversationSeenChanged` publisher and the
  `ConversationSeenChanged` message type.

## Web

- New `fetchConversationDetail(assistantId, conversationId)` +
  `CONVERSATION_NOT_FOUND` sentinel in `chat/api/conversations.ts`.
- New `refreshConversationRow(queryClient, assistantId, conversationId)`
  in `conversation-queries.ts` — replaces / appends / removes the row
  in the cached chat context. One GET per row instead of the legacy
  ~14-request paginated drain (`limit=50&offset=0..N` × foreground +
  background) at a few hundred conversations.
- `useAssistantSyncStream` metadata-tag branch now calls
  `refreshConversationRow` (wrapped in `Sentry.captureException`) instead
  of debounced `invalidateQueries`.
- Dropped the FE `handleConversationListInvalidated` handler; the
  dispatcher case is a documented no-op until the electron cutover.
- Removed `applyConversationSeenStateLocal` + the typed-event parser /
  GLOBAL_STREAM_EVENT_TYPES entries.

## Tests

- Rewrote `conversation-sync-tags.test.ts`: shape-changing reasons emit
  `sync_changed` with the umbrella tag; content-only reasons emit just
  the per-conversation metadata tag. Legacy broadcast asserted
  invisible to process subscribers.
- Rewrote `useAssistantSyncStream` metadata-tag tests via `mock.module`
  on `fetchConversationDetail`: GET fires for the right id, cache is
  patched, untouched rows preserved, 404 removes the row, no list-level
  invalidation fires.
- Removed orphaned tests for the torn-out typed event.

Resolves Vargas's PR #32002 architectural review: keep
`publishConversationListAndMetadataChanged`, carry the `conversationId`
through, single emit path per signal.

---------

Co-authored-by: vellum-apollo-bot[bot] <242025090+vellum-apollo-bot[bot]@users.noreply.github.com>
siddseethepalli added a commit that referenced this pull request May 26, 2026
…count (#32103)

After the cold-start drain loop, ConversationRestorer set serverOffset to
foreground.conversations.count. The conversations endpoint appends injected
pinned rows on page 1 (offset 0), so the accumulated count overshoots the
server's DB cursor and the next loadMoreConversations() skips that many rows.
Carry the server-provided nextOffset (DB pagination cursor) through the
synthesized fetchAllConversationPages response and use it for serverOffset,
mirroring the appendConversations pagination path.

Addresses Codex review on #31924.

Co-authored-by: Vellum Assistant <assistant@vellum.ai>
@siddseethepalli
Copy link
Copy Markdown
Contributor Author

Addressed in #32103

ashleeradka added a commit that referenced this pull request May 26, 2026
…okup (#32095)

* perf(macos): replace O(n²) conversation merge with O(1) dictionary lookup

Replace linear-scan firstIndex(where:) with a pre-built [String: Int]
dictionary in handleConversationListResponse and appendConversations.

With ~1800 conversations (post-pagination PR #31924), the old O(n²)
pattern performed ~3.24M string comparisons on @mainactor, blocking the
main thread for ~1.6s and triggering AppHang reports (LUM-1901).

The dictionary reduces this to ~3600 hash lookups — effectively O(n).

Also removes dead code: a redundant snapshot.first(where:) that
searched for a conversation already proven absent by the preceding
firstIndex check.

Closes LUM-1901

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

* fix: keep dictionary in sync when appending new conversations

The old firstIndex(where:) searched the mutated snapshot (including
just-appended items), so duplicate IDs in a single response would match
and update in-place. The dictionary must be kept in sync after each
append to preserve this behavior.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
noanflaherty added a commit that referenced this pull request May 26, 2026
* fix(macos/billing): tolerate tiered Pro plan in catalog decode (#32114)

PlanCatalogEntry.price_cents was a required Int, but the platform's
/v1/organizations/billing/plans/ Pro entry no longer emits price_cents
(it moved to base_price_cents + machine_tiers/storage_tiers). The whole
catalog decode hard-failed on the Pro entry, was swallowed by try? in
SettingsBillingTab.loadSummary, and surfaced as a perpetual 'Unable to
load plan information.' on the Plan card.

Make price_cents optional (PlanCard only reads id/name/included_features,
so no display impact) and update the wire-protocol test to the real
tiered Pro payload so this drift can't recur silently.

* fix(skills): remove broken document-writer skill, enhance document-editor (#32151)

* fix(skills): remove broken document-writer skill, enhance document-editor

The managed document-writer skill had no TOOLS.json and a broken include
("document" instead of "document-editor"), so skill_execute could never
find document_create in the registry — causing "Unknown tool" errors on
staging.

Delete document-writer and fold its useful anti-pattern guidance into the
bundled document-editor skill which already owns the TOOLS.json manifest.

Closes JARVIS-961

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

* fix(skills): remove document-writer from catalog.json

Stale catalog entry would cause autoInstallFromCatalog to try
reinstalling the deleted skill.

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

* chore: regenerate catalog.json with correct meet-join timestamp

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(assistant): include messageId on canned-response message_complete events (LUM-1902) (#32143)

* fix(assistant): include messageId on canned-greeting message_complete (LUM-1902)

The canned first-greeting path persisted the assistant row via addMessage()
but discarded the returned id, so the message_complete broadcast lacked
messageId. The macOS client filter at ChatActionHandler.swift:507 treats
message_complete events lacking messageId as aux-style notifications while
a turn is in flight and early-returns, so isSending never cleared and the
3-dot loading indicator stayed visible until the 60s watchdog kicked in.

Capture the persisted assistant row id and pass it through. Architectural
follow-up (other emission sites with the same bug shape, macOS filter
cleanup) tracked in LUM-1904.

* fix(assistant): apply messageId fix to all canned-response paths (LUM-1902)

Same bug shape as the canned greeting: the assistant row is persisted via
addMessage() but the returned id is discarded, so the message_complete
broadcast goes out without messageId and the macOS guard at
ChatActionHandler.swift:507 drops the event whenever the streaming-buffer
50ms flush has fired between the delta and the complete — leaving the user
stuck for the full 60s watchdog.

Patches the same five paths #31994 will eventually subsume:

  - inline approval reply (conversation-routes.ts:422)
  - canned first greeting (conversation-routes.ts:1451)
  - slash command output (:1774)
  - /compact (:1855)
  - /clean (:1935)

Centralized into a single `emitCannedMessageComplete` helper so the
temporary fix is one helper + five one-line callers, easy to grep and
inline-then-delete when #31994 lands. Helper carries the full LUM-1902
context comment so individual call sites stay tidy.

The wake-target adapter (wake-target-adapter.ts:130) has the same bug
shape but isn't a quick fix — AgentEvent.message_complete carries no
messageId at the point of relay, so it needs the pre-allocated anchor
treatment matching #31994's approach. Tracked in LUM-1904.

* chore(assistant): scrub internal ticket reference from helper comment

Linear ticket ids are internal references and don't help open-source
contributors reading this file. The PR reference (#31994) stays since
it's discoverable from the repo.

---------

Co-authored-by: Claude <noreply@anthropic.com>

* perf(macos): replace O(n²) conversation merge with O(1) dictionary lookup (#32095)

* perf(macos): replace O(n²) conversation merge with O(1) dictionary lookup

Replace linear-scan firstIndex(where:) with a pre-built [String: Int]
dictionary in handleConversationListResponse and appendConversations.

With ~1800 conversations (post-pagination PR #31924), the old O(n²)
pattern performed ~3.24M string comparisons on @mainactor, blocking the
main thread for ~1.6s and triggering AppHang reports (LUM-1901).

The dictionary reduces this to ~3600 hash lookups — effectively O(n).

Also removes dead code: a redundant snapshot.first(where:) that
searched for a conversation already proven absent by the preceding
firstIndex check.

Closes LUM-1901

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

* fix: keep dictionary in sync when appending new conversations

The old firstIndex(where:) searched the mutated snapshot (including
just-appended items), so duplicate IDs in a single response would match
and update in-place. The dictionary must be kept in sync after each
append to preserve this behavior.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

---------

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

* fix(assistant): clear processing flag if /clean user persist fails (#32115)

Cherry-pick of a535818 — wraps the user-message persist inside the
outer try/finally so a throw from addMessage still clears processing
and drains the queue.

Co-authored-by: siddseethepalli <siddseethepalli@gmail.com>
Co-authored-by: Vellum Assistant <assistant@vellum.ai>

* fix(assistant): clear processing flag if /compact initial persist fails (#32128)

The /compact slash branch set conversation.processing = true and then
awaited the initial user-message addMessage OUTSIDE any guard. The
fire-and-forget compaction IIFE owns the try/finally that resets the
flag, but a throw from that initial persist (transient SQLite/disk
error) never reaches it, leaving the conversation stuck in queued mode
indefinitely. This is the same class of bug fixed for /clean in #32115.

An outer try/finally (as used by /clean) is wrong here because compact
returns 202 immediately and runs async, so it would clear the flag
before compaction finished. Instead, guard just the synchronous pre-202
persist: on failure reset processing, drain the queue, and rethrow so
the caller still surfaces the error.

Co-authored-by: Vellum Assistant <assistant@vellum.ai>

---------

Co-authored-by: Carson Shaar <carson.s.shaar@gmail.com>
Co-authored-by: Alex Nork <48630278+alex-nork@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ashlee Radka <ashlee@vellum.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: siddseethepalli <siddseethepalli@gmail.com>
Co-authored-by: Vellum Assistant <assistant@vellum.ai>
vellum-automation Bot added a commit that referenced this pull request May 26, 2026
* Release v0.8.5

* Cherry-pick fixes onto release/v0.8.5 (#32159)

* fix(macos/billing): tolerate tiered Pro plan in catalog decode (#32114)

PlanCatalogEntry.price_cents was a required Int, but the platform's
/v1/organizations/billing/plans/ Pro entry no longer emits price_cents
(it moved to base_price_cents + machine_tiers/storage_tiers). The whole
catalog decode hard-failed on the Pro entry, was swallowed by try? in
SettingsBillingTab.loadSummary, and surfaced as a perpetual 'Unable to
load plan information.' on the Plan card.

Make price_cents optional (PlanCard only reads id/name/included_features,
so no display impact) and update the wire-protocol test to the real
tiered Pro payload so this drift can't recur silently.

* fix(skills): remove broken document-writer skill, enhance document-editor (#32151)

* fix(skills): remove broken document-writer skill, enhance document-editor

The managed document-writer skill had no TOOLS.json and a broken include
("document" instead of "document-editor"), so skill_execute could never
find document_create in the registry — causing "Unknown tool" errors on
staging.

Delete document-writer and fold its useful anti-pattern guidance into the
bundled document-editor skill which already owns the TOOLS.json manifest.

Closes JARVIS-961

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

* fix(skills): remove document-writer from catalog.json

Stale catalog entry would cause autoInstallFromCatalog to try
reinstalling the deleted skill.

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

* chore: regenerate catalog.json with correct meet-join timestamp

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(assistant): include messageId on canned-response message_complete events (LUM-1902) (#32143)

* fix(assistant): include messageId on canned-greeting message_complete (LUM-1902)

The canned first-greeting path persisted the assistant row via addMessage()
but discarded the returned id, so the message_complete broadcast lacked
messageId. The macOS client filter at ChatActionHandler.swift:507 treats
message_complete events lacking messageId as aux-style notifications while
a turn is in flight and early-returns, so isSending never cleared and the
3-dot loading indicator stayed visible until the 60s watchdog kicked in.

Capture the persisted assistant row id and pass it through. Architectural
follow-up (other emission sites with the same bug shape, macOS filter
cleanup) tracked in LUM-1904.

* fix(assistant): apply messageId fix to all canned-response paths (LUM-1902)

Same bug shape as the canned greeting: the assistant row is persisted via
addMessage() but the returned id is discarded, so the message_complete
broadcast goes out without messageId and the macOS guard at
ChatActionHandler.swift:507 drops the event whenever the streaming-buffer
50ms flush has fired between the delta and the complete — leaving the user
stuck for the full 60s watchdog.

Patches the same five paths #31994 will eventually subsume:

  - inline approval reply (conversation-routes.ts:422)
  - canned first greeting (conversation-routes.ts:1451)
  - slash command output (:1774)
  - /compact (:1855)
  - /clean (:1935)

Centralized into a single `emitCannedMessageComplete` helper so the
temporary fix is one helper + five one-line callers, easy to grep and
inline-then-delete when #31994 lands. Helper carries the full LUM-1902
context comment so individual call sites stay tidy.

The wake-target adapter (wake-target-adapter.ts:130) has the same bug
shape but isn't a quick fix — AgentEvent.message_complete carries no
messageId at the point of relay, so it needs the pre-allocated anchor
treatment matching #31994's approach. Tracked in LUM-1904.

* chore(assistant): scrub internal ticket reference from helper comment

Linear ticket ids are internal references and don't help open-source
contributors reading this file. The PR reference (#31994) stays since
it's discoverable from the repo.

---------

Co-authored-by: Claude <noreply@anthropic.com>

* perf(macos): replace O(n²) conversation merge with O(1) dictionary lookup (#32095)

* perf(macos): replace O(n²) conversation merge with O(1) dictionary lookup

Replace linear-scan firstIndex(where:) with a pre-built [String: Int]
dictionary in handleConversationListResponse and appendConversations.

With ~1800 conversations (post-pagination PR #31924), the old O(n²)
pattern performed ~3.24M string comparisons on @mainactor, blocking the
main thread for ~1.6s and triggering AppHang reports (LUM-1901).

The dictionary reduces this to ~3600 hash lookups — effectively O(n).

Also removes dead code: a redundant snapshot.first(where:) that
searched for a conversation already proven absent by the preceding
firstIndex check.

Closes LUM-1901

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

* fix: keep dictionary in sync when appending new conversations

The old firstIndex(where:) searched the mutated snapshot (including
just-appended items), so duplicate IDs in a single response would match
and update in-place. The dictionary must be kept in sync after each
append to preserve this behavior.

Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>

---------

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

* fix(assistant): clear processing flag if /clean user persist fails (#32115)

Cherry-pick of a535818 — wraps the user-message persist inside the
outer try/finally so a throw from addMessage still clears processing
and drains the queue.

Co-authored-by: siddseethepalli <siddseethepalli@gmail.com>
Co-authored-by: Vellum Assistant <assistant@vellum.ai>

* fix(assistant): clear processing flag if /compact initial persist fails (#32128)

The /compact slash branch set conversation.processing = true and then
awaited the initial user-message addMessage OUTSIDE any guard. The
fire-and-forget compaction IIFE owns the try/finally that resets the
flag, but a throw from that initial persist (transient SQLite/disk
error) never reaches it, leaving the conversation stuck in queued mode
indefinitely. This is the same class of bug fixed for /clean in #32115.

An outer try/finally (as used by /clean) is wrong here because compact
returns 202 immediately and runs async, so it would clear the flag
before compaction finished. Instead, guard just the synchronous pre-202
persist: on failure reset processing, drain the queue, and rethrow so
the caller still surfaces the error.

Co-authored-by: Vellum Assistant <assistant@vellum.ai>

---------

Co-authored-by: Carson Shaar <carson.s.shaar@gmail.com>
Co-authored-by: Alex Nork <48630278+alex-nork@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ashlee Radka <ashlee@vellum.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: siddseethepalli <siddseethepalli@gmail.com>
Co-authored-by: Vellum Assistant <assistant@vellum.ai>

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Noa Flaherty <noa@vellum.ai>
Co-authored-by: Carson Shaar <carson.s.shaar@gmail.com>
Co-authored-by: Alex Nork <48630278+alex-nork@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ashlee Radka <ashlee@vellum.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: siddseethepalli <siddseethepalli@gmail.com>
Co-authored-by: Vellum Assistant <assistant@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