opencode/packages
Kit Langton ab31c41fee refactor(bus): one GlobalBus emit per event, with optional sync metadata
Today every sync.run produces TWO GlobalBus events:

  1. From bus.publish itself — the projection view:
     { payload: { id, type: "session.created", properties } }

  2. From sync/index.ts — the source-of-truth envelope:
     { payload: { type: "sync", syncEvent: { type: "session.created.1", id, seq, aggregateID, data } } }

These two emits live in different files and discriminate via
`payload.type` — either a real event type or the sentinel "sync".
That collision is the source of confusion in the codebase:
consumers have to know which shape they're looking at.

The duality dates back to:
  - d88912abf0 (Dec 2025): Dax added GlobalBus.emit inside bus.publish.
  - b22add292c (#22347, Apr 2026): James consolidated sync events onto
    GlobalBus by ADDING a second emit with the source envelope.

#22347's stated intent was "consolidate events into a single stream".
Two emits with different shapes on one channel isn't quite that. One
emit per event with a unified shape is.

This PR collapses the two emits into one:

  { payload: { id, type: "session.created", properties, sync?: { name, seq, aggregateID, data } } }

- bus.publish accepts an optional `sync?: SyncMetadata` option.
- sync/index.ts passes it; its own GlobalBus.emit is removed.
- BusEvent.effectPayloads() adds an optional `sync` field to every
  event schema.
- SyncEvent.effectPayloads() is no longer included in the wire schema
  (it's now subsumed by the optional field).
- Consumers migrate from `payload.type === "sync"` filtering to
  `payload.sync != null` filtering. control-plane/workspace.ts
  reconstructs SerializedEvent from { id, sync.* } for replay.

Note: this is a BREAKING SDK wire-format change. External consumers
that read `payload.type === "sync"` or `payload.syncEvent` need to
migrate. Internal opencode consumers all migrated in this PR.

Side observation: the runtime emit was using `payload.syncEvent`
(nested) while the SDK schema declared the fields at top level under
`type: "sync"` — a silent schema drift. Collapsing to one emit also
fixes that drift by definition.

Verified:
  - bun typecheck — clean
  - bun run test test/sync/index.test.ts test/bus/bus-effect.test.ts
    test/server/httpapi-event.test.ts
    test/server/httpapi-event-diagnostics.test.ts — 29/29 green
  - bun run test test/server/httpapi-sdk.test.ts -t "streams sync-backed" — green
2026-05-18 13:23:30 -04:00
..
app refactor(bus): one GlobalBus emit per event, with optional sync metadata 2026-05-18 13:23:30 -04:00
console sync 2026-05-17 23:43:23 -04:00
containers chore: Upgrade Bun to the final non-rust version (#27648) 2026-05-18 09:24:26 +10:00
core Load models.dev snapshot from build global (#28077) 2026-05-17 21:59:44 -04:00
desktop feat(desktop): add notification permission for renderer (#28119) 2026-05-18 15:43:28 +08:00
docs fix(web): favicon rename again 2026-01-21 14:36:21 -06:00
enterprise sync 2026-05-17 23:43:23 -04:00
extensions/zed sync release versions for v1.15.4 2026-05-17 17:44:50 +00:00
function sync 2026-05-17 23:43:23 -04:00
http-recorder sync release versions for v1.15.4 2026-05-17 17:44:50 +00:00
identity chore: identity cleanup, vscode ext images 2025-12-08 06:47:57 -06:00
llm sync release versions for v1.15.4 2026-05-17 17:44:50 +00:00
opencode refactor(bus): one GlobalBus emit per event, with optional sync metadata 2026-05-18 13:23:30 -04:00
plugin upgrade opentui to 0.2.14 (#28090) 2026-05-18 13:44:08 +02:00
script refactor: replace bun semver with npm semver package 2026-03-09 15:29:55 -04:00
sdk refactor(bus): one GlobalBus emit per event, with optional sync metadata 2026-05-18 13:23:30 -04:00
slack sync release versions for v1.15.4 2026-05-17 17:44:50 +00:00
storybook Remove unused app ID schema (#27105) 2026-05-12 13:09:23 -04:00
ui fix(ui): fix question dock overflow and message part flex layout (#28142) 2026-05-18 17:55:27 +08:00
web dialog prompt submit keybind + opentui event sink (#27945) 2026-05-18 00:23:19 +02:00