refactor(api-events): migrate compaction_circuit_open/closed, home_feed_updated, interaction_resolved to canonical schemas#32548
Conversation
…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; |
There was a problem hiding this comment.
delete these re-exports
There was a problem hiding this comment.
Deleted in #32608 — conversations.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; |
There was a problem hiding this comment.
delete this re-export
There was a problem hiding this comment.
Deleted in #32608 — _HomeServerMessages references HomeFeedUpdatedEvent directly.
| export type _HomeServerMessages = | ||
| | RelationshipStateUpdatedEvent | ||
| | HomeFeedUpdated; |
There was a problem hiding this comment.
do we use this union? delete if not
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
delete tese re-exports
There was a problem hiding this comment.
Deleted in #32608 — including the InteractionResolutionState re-export, which runtime/pending-interactions.ts now imports from the canonical api/events/interaction-resolved.ts directly.
…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).
…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>
…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
…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
What
Migrates four system-status events to canonical Zod schemas under
assistant/src/api/events/:compaction_circuit_open—reasontightened to literal"3_consecutive_failures"(matches both emit sites:circuit-breaker.tsand the playgroundinject-failuresroute)compaction_circuit_closedhome_feed_updated— noconversationId(global, per-user event)interaction_resolved— also exports theInteractionResolutionStateenum schema/type from the same module; daemonmessages.tsnow aliases the daemon-sideInteractionResolutionStateto the canonical export so the existingpending-interactions.tsimport path keeps workingReplaces hand-rolled parser branches and dual-type definitions (daemon
interface+ webinterface) with single-source canonical schemas validated at parse time.Why
Continuation of the canonical-events workstream (Batch 5 of N). Each migrated event:
case "…":block inapps/web/src/domains/chat/api/event-parser.ts(the legacytypeof x === "string" ? x : ""defensive parsing path)unknowninstead of being silently coercedmessage-types/*.tsinterface + webevent-types.tsinterface) — both now alias the canonical typeChanges
Canonical schemas added (
assistant/src/api/events/):compaction-circuit-open.tscompaction-circuit-closed.tshome-feed-updated.tsinteraction-resolved.ts(also exportsInteractionResolutionStateSchema+InteractionResolutionState)Wiring (
assistant/src/api/index.ts):AssistantEventSchemaunion (now 16 → 20 canonical events)Daemon adoption:
daemon/message-types/conversations.ts: collapsesinterface CompactionCircuitOpenandinterface CompactionCircuitClosedto canonical aliasesdaemon/message-types/home.ts: collapsesinterface HomeFeedUpdatedto canonical aliasdaemon/message-types/messages.ts: collapsesinterface InteractionResolvedandtype InteractionResolutionStateto canonical aliases (the latter keepsruntime/pending-interactions.tsimports working without modification)Web cut-over (
apps/web/src/domains/chat/):api/event-types.ts: removes 4 local interface defs, 4 union members fromAssistantEvent, and the localInteractionResolutionStatetype (now part ofAPIAssistantEvent)api/event-parser.ts: deletes 4 legacycaseblocks; drops now-unusedInteractionKindimportutils/stream-handlers/metadata-handlers.ts: re-pointsCompactionCircuit*Eventimports to@vellumai/assistant-apiutils/stream-handlers/home-handlers.ts: re-pointsHomeFeedUpdatedEventimport to@vellumai/assistant-apiTests:
event-parser.test.tscovering happy path + missing required + strict (unknown field) for each new eventmetadata-handlers.test.ts(reason: "test"→reason: "3_consecutive_failures"— caught by the type-checker)Validation
bunx tsc --noEmitclean on assistant + web (modulo pre-existing@vellum/design-libraryresolution noise unrelated to this PR)bun testclean across affected files (events parser, metadata handlers, circuit-breaker-pipeline)bunx eslintcleanbunx prettier --checkcleanbun run audit:cross-domain:checkcleanMigration recipe
Follows the 6-step recipe in
assistant/src/api/README.md. Notable per-event quirks:compaction_circuit_open: tighteningreasonfromstring(web's defensive parse) toz.literal("3_consecutive_failures")(daemon's emit contract) catches drift if a new trip reason is ever added without updating both sideshome_feed_updated: daemon emits withoutconversationId, web type historically had an unused optional field — canonical drops it to match daemoninteraction_resolved: canonicalkind: z.string()matches the daemon's open-ended emission (kept loose because the daemon emits from aMapkeyed by string without enum narrowing); web-side narrowing happens at the attention-tracking allowlist boundary (USER_FACING_INTERACTION_KINDS.has(event.kind))interaction_resolved: tightensconversationIdfrom web's defensive optional to required (matches daemon's emission contract); the existing legacy tests already covered thestatevalidation path