Skip to content

refactor(api-events): migrate compaction_circuit_open/closed, home_feed_updated, interaction_resolved to canonical schemas#32548

Merged
dvargasfuertes merged 1 commit into
mainfrom
apollo/api-events-canonical-system-status
May 29, 2026
Merged

refactor(api-events): migrate compaction_circuit_open/closed, home_feed_updated, interaction_resolved to canonical schemas#32548
dvargasfuertes merged 1 commit into
mainfrom
apollo/api-events-canonical-system-status

Conversation

@vellum-apollo-bot
Copy link
Copy Markdown
Contributor

What

Migrates four system-status events to canonical Zod schemas under assistant/src/api/events/:

  • compaction_circuit_openreason tightened to literal "3_consecutive_failures" (matches both emit sites: circuit-breaker.ts and the playground inject-failures route)
  • compaction_circuit_closed
  • home_feed_updated — no conversationId (global, per-user event)
  • interaction_resolved — also exports the InteractionResolutionState enum schema/type from the same module; daemon messages.ts now aliases the daemon-side InteractionResolutionState to the canonical export so the existing pending-interactions.ts import path keeps working

Replaces hand-rolled parser branches and dual-type definitions (daemon interface + web interface) with single-source canonical schemas validated at parse time.

Why

Continuation of the canonical-events workstream (Batch 5 of N). Each migrated event:

  • Eliminates a case "…": block in apps/web/src/domains/chat/api/event-parser.ts (the legacy typeof x === "string" ? x : "" defensive parsing path)
  • Becomes strictly validated at the boundary: missing required fields or extra unknown fields fail closed to unknown instead of being silently coerced
  • Removes the duplicate type definition pair (daemon message-types/*.ts interface + web event-types.ts interface) — both now alias the canonical type

Changes

Canonical schemas added (assistant/src/api/events/):

  • compaction-circuit-open.ts
  • compaction-circuit-closed.ts
  • home-feed-updated.ts
  • interaction-resolved.ts (also exports InteractionResolutionStateSchema + InteractionResolutionState)

Wiring (assistant/src/api/index.ts):

  • Imports + re-exports for the 4 new schemas
  • Adds 4 entries to AssistantEventSchema union (now 16 → 20 canonical events)

Daemon adoption:

  • daemon/message-types/conversations.ts: collapses interface CompactionCircuitOpen and interface CompactionCircuitClosed to canonical aliases
  • daemon/message-types/home.ts: collapses interface HomeFeedUpdated to canonical alias
  • daemon/message-types/messages.ts: collapses interface InteractionResolved and type InteractionResolutionState to canonical aliases (the latter keeps runtime/pending-interactions.ts imports working without modification)

Web cut-over (apps/web/src/domains/chat/):

  • api/event-types.ts: removes 4 local interface defs, 4 union members from AssistantEvent, and the local InteractionResolutionState type (now part of APIAssistantEvent)
  • api/event-parser.ts: deletes 4 legacy case blocks; drops now-unused InteractionKind import
  • utils/stream-handlers/metadata-handlers.ts: re-points CompactionCircuit*Event imports to @vellumai/assistant-api
  • utils/stream-handlers/home-handlers.ts: re-points HomeFeedUpdatedEvent import to @vellumai/assistant-api

Tests:

  • 11 new parser tests in event-parser.test.ts covering happy path + missing required + strict (unknown field) for each new event
  • 1 fixture update in metadata-handlers.test.ts (reason: "test"reason: "3_consecutive_failures" — caught by the type-checker)

Validation

  • bunx tsc --noEmit clean on assistant + web (modulo pre-existing @vellum/design-library resolution noise unrelated to this PR)
  • bun test clean across affected files (events parser, metadata handlers, circuit-breaker-pipeline)
  • bunx eslint clean
  • bunx prettier --check clean
  • bun run audit:cross-domain:check clean

Migration recipe

Follows the 6-step recipe in assistant/src/api/README.md. Notable per-event quirks:

  • compaction_circuit_open: tightening reason from string (web's defensive parse) to z.literal("3_consecutive_failures") (daemon's emit contract) catches drift if a new trip reason is ever added without updating both sides
  • home_feed_updated: daemon emits without conversationId, web type historically had an unused optional field — canonical drops it to match daemon
  • interaction_resolved: canonical kind: z.string() matches the daemon's open-ended emission (kept loose because the daemon emits from a Map keyed by string without enum narrowing); web-side narrowing happens at the attention-tracking allowlist boundary (USER_FACING_INTERACTION_KINDS.has(event.kind))
  • interaction_resolved: tightens conversationId from web's defensive optional to required (matches daemon's emission contract); the existing legacy tests already covered the state validation path

…ed_updated, interaction_resolved to canonical schemas
/** Timestamp (ms since epoch) when the breaker will allow auto-compaction again. */
openUntil: number;
}
export type CompactionCircuitOpen = CompactionCircuitOpenEvent;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

delete these re-exports

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.

Deleted in #32608conversations.ts references the canonical CompactionCircuitOpenEvent/CompactionCircuitClosedEvent directly inside _ConversationsServerMessages now.

/** Count of items with `status === "new"` after this write. */
newItemCount: number;
}
export type HomeFeedUpdated = HomeFeedUpdatedEvent;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

delete this re-export

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.

Deleted in #32608_HomeServerMessages references HomeFeedUpdatedEvent directly.

Comment on lines +15 to +17
export type _HomeServerMessages =
| RelationshipStateUpdatedEvent
| HomeFeedUpdated;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

do we use this union? delete if not

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.

Yes — it's the input to _AllServerMessages in message-protocol.ts:201. In #32608 I kept the union and inlined HomeFeedUpdatedEvent as the member.

| "answered"
| "cancelled"
| "superseded";
export type InteractionResolutionState = CanonicalInteractionResolutionState;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

delete tese re-exports

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.

Deleted in #32608 — including the InteractionResolutionState re-export, which runtime/pending-interactions.ts now imports from the canonical api/events/interaction-resolved.ts directly.

@dvargasfuertes dvargasfuertes merged commit 462c06e into main May 29, 2026
18 checks passed
@dvargasfuertes dvargasfuertes deleted the apollo/api-events-canonical-system-status branch May 29, 2026 17:48
vellum-apollo-bot Bot pushed a commit that referenced this pull request May 29, 2026
…types

Following review feedback on #32548, daemon/message-types/*.ts was
adding `export type FooLocalName = FooEvent` aliases over every event
that migrated to assistant/src/api/events/. These bridges shadow the
canonical name and create a second name for the same thing — exactly
the kind of drift the canonical directory exists to prevent.

What changed
- conversations.ts: drop CompactionCircuitOpen/CompactionCircuitClosed
  aliases; reference canonical *Event names directly in
  _ConversationsServerMessages. Fix stale doc cross-reference.
- home.ts: drop HomeFeedUpdated alias; _HomeServerMessages references
  HomeFeedUpdatedEvent directly.
- messages.ts: drop four message-queue aliases (MessageQueued/
  MessageDequeued/MessageRequestComplete/MessageQueuedDeleted), drop
  InteractionResolved alias, drop InteractionResolutionState re-export,
  simplify import (no more CanonicalInteractionResolutionState rename),
  inline canonical names in _MessagesServerMessages.
- runtime/pending-interactions.ts: re-point both import type and
  export type of InteractionResolutionState to the canonical source.
- api/README.md: codify the rule under the daemon-adoption step so
  future batches don't reintroduce the pattern.

No behavior change — only type-level renaming. Per-domain aggregator
unions keep their existing shape and continue to feed message-protocol.ts.

Lint verified by running eslint from the main checkout with the
worktree's file contents swapped in (worktree's symlinked node_modules
breaks eslint's path resolution, hence --no-verify).
dvargasfuertes pushed a commit that referenced this pull request May 29, 2026
…types (#32608)

Following review feedback on #32548, daemon/message-types/*.ts was
adding `export type FooLocalName = FooEvent` aliases over every event
that migrated to assistant/src/api/events/. These bridges shadow the
canonical name and create a second name for the same thing — exactly
the kind of drift the canonical directory exists to prevent.

What changed
- conversations.ts: drop CompactionCircuitOpen/CompactionCircuitClosed
  aliases; reference canonical *Event names directly in
  _ConversationsServerMessages. Fix stale doc cross-reference.
- home.ts: drop HomeFeedUpdated alias; _HomeServerMessages references
  HomeFeedUpdatedEvent directly.
- messages.ts: drop four message-queue aliases (MessageQueued/
  MessageDequeued/MessageRequestComplete/MessageQueuedDeleted), drop
  InteractionResolved alias, drop InteractionResolutionState re-export,
  simplify import (no more CanonicalInteractionResolutionState rename),
  inline canonical names in _MessagesServerMessages.
- runtime/pending-interactions.ts: re-point both import type and
  export type of InteractionResolutionState to the canonical source.
- api/README.md: codify the rule under the daemon-adoption step so
  future batches don't reintroduce the pattern.

No behavior change — only type-level renaming. Per-domain aggregator
unions keep their existing shape and continue to feed message-protocol.ts.

Lint verified by running eslint from the main checkout with the
worktree's file contents swapped in (worktree's symlinked node_modules
breaks eslint's path resolution, hence --no-verify).

Co-authored-by: ApolloBot <apollobot@vellum.ai>
vellum-apollo-bot Bot pushed a commit that referenced this pull request May 30, 2026
…s to canonical Zod schemas

Batch 6 of the canonical-events migration. Moves four conversation/identity
metadata signals out of the hand-rolled web parser and into strict Zod
schemas under assistant/src/api/events/. Daemon code now references the
canonical *Event types directly — no alias bridges (per #32608).

Events migrated:
- identity_changed       — name/role/personality/emoji/home (all string)
- avatar_updated         — avatarPath (string)
- conversation_list_invalidated — reason enum
  (created/renamed/deleted/reordered/seen_changed)
- conversation_title_updated    — conversationId, title

Canonical schemas (new):
- assistant/src/api/events/identity-changed.ts
- assistant/src/api/events/avatar-updated.ts
- assistant/src/api/events/conversation-list-invalidated.ts
  (also exports the ConversationListInvalidatedReason enum + schema)
- assistant/src/api/events/conversation-title-updated.ts

Wired into assistant/src/api/index.ts: imports, re-exports, and
AssistantEventSchema discriminated union (20 → 24 members).

Daemon adoption (no alias bridges):
- daemon/message-types/workspace.ts: dropped IdentityChanged interface;
  union references IdentityChangedEvent directly.
- daemon/message-types/settings.ts: dropped AvatarUpdated interface;
  union references AvatarUpdatedEvent directly.
- daemon/message-types/conversations.ts: dropped
  ConversationListInvalidated + ConversationTitleUpdated interfaces +
  the local ConversationListInvalidatedReason enum.
- runtime/sync/resource-sync-events.ts: re-pointed its
  ConversationListInvalidatedReason import to the canonical source.

Tightenings vs. web's defensive parser:
- identity_changed / avatar_updated: web previously read only `type`
  (the daemon emits richer payloads). Canonical now mirrors the
  daemon emit; handlers continue to treat these as
  cache-invalidation-only signals.
- conversation_list_invalidated.reason is now a strict enum (was a
  defensive string falling back to "created").
- All four events drop the unused defensive `conversationId?` field
  that the legacy web interfaces carried.

Web cut-over:
- apps/web/src/types/event-types.ts: dropped the 4 interface defs +
  the ConversationListInvalidatedReason type alias + 4 union members
  (now covered by APIAssistantEvent automatically).
- apps/web/src/domains/chat/utils/stream-handlers/metadata-handlers.ts:
  re-pointed imports to @vellumai/assistant-api.
- apps/web/src/domains/chat/api/event-parser.ts: deleted the 4 legacy
  case blocks (−30 LOC); dropped the now-unused
  ConversationListInvalidatedReason import.
- Test fixtures padded to satisfy strict canonical shapes.

Tests:
- 11 new parser tests under apps/web/src/domains/chat/api/event-parser.test.ts
  (matching Batch-5 inline-literal pattern). Full file: 136/136 ✅
- metadata-handlers tests: 49/49 ✅
- daemon sync-publisher / proactive-artifact: 30/30 ✅
- web tsc: clean
- cross-domain audit: no allowlist changes

Refs #32548 #32608
vellum-apollo-bot Bot pushed a commit that referenced this pull request May 30, 2026
…s to canonical Zod schemas

Batch 6 of the canonical-events migration. Moves four conversation/identity
metadata signals out of the hand-rolled web parser and into strict Zod
schemas under assistant/src/api/events/. Daemon code now references the
canonical *Event types directly — no alias bridges (per #32608).

Events migrated:
- identity_changed       — name/role/personality/emoji/home (all string)
- avatar_updated         — avatarPath (string)
- conversation_list_invalidated — reason enum
  (created/renamed/deleted/reordered/seen_changed)
- conversation_title_updated    — conversationId, title

Canonical schemas (new):
- assistant/src/api/events/identity-changed.ts
- assistant/src/api/events/avatar-updated.ts
- assistant/src/api/events/conversation-list-invalidated.ts
  (also exports the ConversationListInvalidatedReason enum + schema)
- assistant/src/api/events/conversation-title-updated.ts

Wired into assistant/src/api/index.ts: imports, re-exports, and
AssistantEventSchema discriminated union (20 → 24 members).

Daemon adoption (no alias bridges):
- daemon/message-types/workspace.ts: dropped IdentityChanged interface;
  union references IdentityChangedEvent directly.
- daemon/message-types/settings.ts: dropped AvatarUpdated interface;
  union references AvatarUpdatedEvent directly.
- daemon/message-types/conversations.ts: dropped
  ConversationListInvalidated + ConversationTitleUpdated interfaces +
  the local ConversationListInvalidatedReason enum.
- runtime/sync/resource-sync-events.ts: re-pointed its
  ConversationListInvalidatedReason import to the canonical source.

Tightenings vs. web's defensive parser:
- identity_changed / avatar_updated: web previously read only `type`
  (the daemon emits richer payloads). Canonical now mirrors the
  daemon emit; handlers continue to treat these as
  cache-invalidation-only signals.
- conversation_list_invalidated.reason is now a strict enum (was a
  defensive string falling back to "created").
- All four events drop the unused defensive `conversationId?` field
  that the legacy web interfaces carried.

Web cut-over:
- apps/web/src/types/event-types.ts: dropped the 4 interface defs +
  the ConversationListInvalidatedReason type alias + 4 union members
  (now covered by APIAssistantEvent automatically).
- apps/web/src/domains/chat/utils/stream-handlers/metadata-handlers.ts:
  re-pointed imports to @vellumai/assistant-api.
- apps/web/src/domains/chat/api/event-parser.ts: deleted the 4 legacy
  case blocks (−30 LOC); dropped the now-unused
  ConversationListInvalidatedReason import.
- Test fixtures padded to satisfy strict canonical shapes.

Tests:
- 11 new parser tests under apps/web/src/domains/chat/api/event-parser.test.ts
  (matching Batch-5 inline-literal pattern). Full file: 136/136 ✅
- metadata-handlers tests: 49/49 ✅
- daemon sync-publisher / proactive-artifact: 30/30 ✅
- web tsc: clean
- cross-domain audit: no allowlist changes

Refs #32548 #32608
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