@canonmsg/backend-contracts 1.9.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/dist/agentBehaviorPolicy.d.ts +45 -86
  2. package/dist/agentBehaviorPolicy.js +0 -53
  3. package/dist/cjs/agentBehaviorPolicy.js +1 -54
  4. package/dist/cjs/contactRequest.js +0 -30
  5. package/dist/cjs/diffRedaction.js +73 -0
  6. package/dist/cjs/index.js +1 -8
  7. package/dist/cjs/media.js +0 -13
  8. package/dist/cjs/message.js +44 -65
  9. package/dist/cjs/runtimeCardFields.js +1 -58
  10. package/dist/cjs/turnProtocol.js +1 -41
  11. package/dist/contactRequest.d.ts +23 -42
  12. package/dist/contactRequest.js +0 -29
  13. package/dist/diffRedaction.d.ts +6 -0
  14. package/dist/diffRedaction.js +68 -0
  15. package/dist/index.d.ts +1 -8
  16. package/dist/index.js +1 -8
  17. package/dist/media.d.ts +11 -24
  18. package/dist/media.js +0 -12
  19. package/dist/message.d.ts +79 -132
  20. package/dist/message.js +45 -65
  21. package/dist/runtimeCardFields.d.ts +39 -112
  22. package/dist/runtimeCardFields.js +0 -57
  23. package/dist/turnProtocol.d.ts +24 -91
  24. package/dist/turnProtocol.js +0 -40
  25. package/package.json +5 -8
  26. package/dist/bridgeLocal.d.ts +0 -1871
  27. package/dist/bridgeLocal.js +0 -744
  28. package/dist/cjs/bridgeLocal.js +0 -747
  29. package/dist/cjs/controlSignals.js +0 -44
  30. package/dist/cjs/endpointRegistry.js +0 -264
  31. package/dist/cjs/hitl.js +0 -171
  32. package/dist/cjs/inboundFrame.js +0 -92
  33. package/dist/cjs/restEndpoints.js +0 -237
  34. package/dist/cjs/sendContextual.js +0 -92
  35. package/dist/cjs/sseEvents.js +0 -281
  36. package/dist/controlSignals.d.ts +0 -55
  37. package/dist/controlSignals.js +0 -41
  38. package/dist/endpointRegistry.d.ts +0 -13
  39. package/dist/endpointRegistry.js +0 -261
  40. package/dist/hitl.d.ts +0 -303
  41. package/dist/hitl.js +0 -168
  42. package/dist/inboundFrame.d.ts +0 -229
  43. package/dist/inboundFrame.js +0 -89
  44. package/dist/restEndpoints.d.ts +0 -531
  45. package/dist/restEndpoints.js +0 -234
  46. package/dist/schemas/agent-behavior-settings-record.schema.json +0 -48
  47. package/dist/schemas/bridge-connection-state-notification.schema.json +0 -19
  48. package/dist/schemas/bridge-get-attachment-params.schema.json +0 -24
  49. package/dist/schemas/bridge-get-attachment-result.schema.json +0 -20
  50. package/dist/schemas/bridge-hello-params.schema.json +0 -42
  51. package/dist/schemas/bridge-hello-result.schema.json +0 -105
  52. package/dist/schemas/bridge-hitl-state.schema.json +0 -38
  53. package/dist/schemas/bridge-ok-result.schema.json +0 -15
  54. package/dist/schemas/bridge-publish-streaming-event-params.schema.json +0 -138
  55. package/dist/schemas/bridge-ready-notification.schema.json +0 -79
  56. package/dist/schemas/bridge-resync-reason.schema.json +0 -9
  57. package/dist/schemas/bridge-resync.schema.json +0 -18
  58. package/dist/schemas/bridge-rpc-error.schema.json +0 -19
  59. package/dist/schemas/bridge-rpc-id.schema.json +0 -12
  60. package/dist/schemas/bridge-rpc-notification.schema.json +0 -20
  61. package/dist/schemas/bridge-rpc-request.schema.json +0 -31
  62. package/dist/schemas/bridge-rpc-response.schema.json +0 -81
  63. package/dist/schemas/bridge-status-result.schema.json +0 -76
  64. package/dist/schemas/bridge-stream-clear-event.schema.json +0 -15
  65. package/dist/schemas/bridge-stream-event.schema.json +0 -112
  66. package/dist/schemas/bridge-stream-final-event.schema.json +0 -19
  67. package/dist/schemas/bridge-stream-text-event.schema.json +0 -31
  68. package/dist/schemas/bridge-stream-thinking-event.schema.json +0 -15
  69. package/dist/schemas/bridge-stream-tool-event.schema.json +0 -36
  70. package/dist/schemas/bridge-upload-media-params.schema.json +0 -28
  71. package/dist/schemas/bridge-upstream-state.schema.json +0 -10
  72. package/dist/schemas/contacts-response.schema.json +0 -52
  73. package/dist/schemas/control-channel-key.schema.json +0 -10
  74. package/dist/schemas/control-primitive-event.schema.json +0 -30
  75. package/dist/schemas/control-primitive-map.schema.json +0 -15
  76. package/dist/schemas/control-session-event.schema.json +0 -26
  77. package/dist/schemas/control-session-node.schema.json +0 -11
  78. package/dist/schemas/control-signal-event.schema.json +0 -35
  79. package/dist/schemas/control-signal-node.schema.json +0 -22
  80. package/dist/schemas/control-signal-type.schema.json +0 -10
  81. package/dist/schemas/conversations-response.schema.json +0 -167
  82. package/dist/schemas/create-contact-request-request.schema.json +0 -17
  83. package/dist/schemas/create-contact-request-response.schema.json +0 -51
  84. package/dist/schemas/create-conversation-request.schema.json +0 -82
  85. package/dist/schemas/create-conversation-response.schema.json +0 -29
  86. package/dist/schemas/delete-message-response.schema.json +0 -11
  87. package/dist/schemas/delivery-intent.schema.json +0 -11
  88. package/dist/schemas/forward-message-request.schema.json +0 -25
  89. package/dist/schemas/forward-message-response.schema.json +0 -35
  90. package/dist/schemas/forwarded-from.schema.json +0 -18
  91. package/dist/schemas/get-messages-query.schema.json +0 -17
  92. package/dist/schemas/hitl-approval-category.schema.json +0 -15
  93. package/dist/schemas/hitl-approval-detail.schema.json +0 -21
  94. package/dist/schemas/hitl-approval-outcome-record.schema.json +0 -33
  95. package/dist/schemas/hitl-approval-reply-record.schema.json +0 -57
  96. package/dist/schemas/hitl-approval-request-record.schema.json +0 -105
  97. package/dist/schemas/hitl-approval-risk.schema.json +0 -11
  98. package/dist/schemas/hitl-card-outcome-record.schema.json +0 -48
  99. package/dist/schemas/hitl-card-reply-record.schema.json +0 -46
  100. package/dist/schemas/hitl-card-request-record.schema.json +0 -73
  101. package/dist/schemas/hitl-input-answers.schema.json +0 -23
  102. package/dist/schemas/hitl-input-kind.schema.json +0 -10
  103. package/dist/schemas/hitl-input-outcome-record.schema.json +0 -46
  104. package/dist/schemas/hitl-input-question.schema.json +0 -54
  105. package/dist/schemas/hitl-input-reply-record.schema.json +0 -44
  106. package/dist/schemas/hitl-input-request-record.schema.json +0 -134
  107. package/dist/schemas/hitl-pending.schema.json +0 -31
  108. package/dist/schemas/hitl-request-accepted.schema.json +0 -18
  109. package/dist/schemas/hitl-request-kind.schema.json +0 -10
  110. package/dist/schemas/hitl-resolution-status.schema.json +0 -10
  111. package/dist/schemas/hitl-resolution.schema.json +0 -38
  112. package/dist/schemas/hitl-session-rule.schema.json +0 -32
  113. package/dist/schemas/inbound-disposition.schema.json +0 -13
  114. package/dist/schemas/inbound-frame-attachment-v1.schema.json +0 -94
  115. package/dist/schemas/inbound-frame-contact-card-v1.schema.json +0 -59
  116. package/dist/schemas/inbound-frame-group-context-v1.schema.json +0 -96
  117. package/dist/schemas/inbound-frame-reply-context-v1.schema.json +0 -261
  118. package/dist/schemas/inbound-frame-shape-v1.schema.json +0 -12
  119. package/dist/schemas/inbound-frame-v1.schema.json +0 -506
  120. package/dist/schemas/mark-read-response.schema.json +0 -11
  121. package/dist/schemas/media-attachment-kind.schema.json +0 -11
  122. package/dist/schemas/media-attachment.schema.json +0 -42
  123. package/dist/schemas/messages-page-response.schema.json +0 -290
  124. package/dist/schemas/participation-decision-input.schema.json +0 -44
  125. package/dist/schemas/participation-history-message.schema.json +0 -23
  126. package/dist/schemas/participation-history-snapshot.schema.json +0 -37
  127. package/dist/schemas/participation-style.schema.json +0 -13
  128. package/dist/schemas/react-request.schema.json +0 -22
  129. package/dist/schemas/react-response.schema.json +0 -35
  130. package/dist/schemas/resolve-admission-request.schema.json +0 -30
  131. package/dist/schemas/resolve-admission-response.schema.json +0 -129
  132. package/dist/schemas/resolved-agent-behavior-policy-record.schema.json +0 -81
  133. package/dist/schemas/rest-contact.schema.json +0 -40
  134. package/dist/schemas/rest-conversation-last-message.schema.json +0 -77
  135. package/dist/schemas/rest-conversation.schema.json +0 -155
  136. package/dist/schemas/rest-message.schema.json +0 -252
  137. package/dist/schemas/rest-ok-response.schema.json +0 -11
  138. package/dist/schemas/rest-session-selection.schema.json +0 -40
  139. package/dist/schemas/runtime-card-field-choice.schema.json +0 -23
  140. package/dist/schemas/runtime-card-field-type.schema.json +0 -17
  141. package/dist/schemas/runtime-card-field.schema.json +0 -180
  142. package/dist/schemas/runtime-card-line-item-column-type.schema.json +0 -12
  143. package/dist/schemas/runtime-card-line-item-column.schema.json +0 -74
  144. package/dist/schemas/send-contextual-deferred-response.schema.json +0 -39
  145. package/dist/schemas/send-contextual-messaged-response.schema.json +0 -42
  146. package/dist/schemas/send-contextual-request.schema.json +0 -103
  147. package/dist/schemas/send-contextual-response.schema.json +0 -105
  148. package/dist/schemas/send-contextual-self-context.schema.json +0 -19
  149. package/dist/schemas/send-contextual-state-response.schema.json +0 -24
  150. package/dist/schemas/send-message-request.schema.json +0 -106
  151. package/dist/schemas/send-message-response.schema.json +0 -14
  152. package/dist/schemas/sender-type.schema.json +0 -9
  153. package/dist/schemas/serialize-message-input.schema.json +0 -29
  154. package/dist/schemas/serialized-agent-client-type.schema.json +0 -12
  155. package/dist/schemas/serialized-contact-card.schema.json +0 -65
  156. package/dist/schemas/serialized-contact-request-status.schema.json +0 -11
  157. package/dist/schemas/serialized-contact-request.schema.json +0 -157
  158. package/dist/schemas/serialized-content-type.schema.json +0 -13
  159. package/dist/schemas/serialized-message.schema.json +0 -249
  160. package/dist/schemas/serialized-reactions.schema.json +0 -14
  161. package/dist/schemas/serialized-sender-type.schema.json +0 -9
  162. package/dist/schemas/set-streaming-request.schema.json +0 -67
  163. package/dist/schemas/set-typing-request.schema.json +0 -25
  164. package/dist/schemas/sse-access-policy.schema.json +0 -10
  165. package/dist/schemas/sse-agent-context-payload.schema.json +0 -57
  166. package/dist/schemas/sse-connected-payload.schema.json +0 -18
  167. package/dist/schemas/sse-contact-payload.schema.json +0 -40
  168. package/dist/schemas/sse-contact-removed-payload.schema.json +0 -14
  169. package/dist/schemas/sse-contact-request-payload.schema.json +0 -157
  170. package/dist/schemas/sse-conversation-updated-payload.schema.json +0 -48
  171. package/dist/schemas/sse-error-payload.schema.json +0 -23
  172. package/dist/schemas/sse-event-envelope.schema.json +0 -40
  173. package/dist/schemas/sse-event-family.schema.json +0 -12
  174. package/dist/schemas/sse-event-type.schema.json +0 -26
  175. package/dist/schemas/sse-heartbeat-payload.schema.json +0 -14
  176. package/dist/schemas/sse-membership-change.schema.json +0 -28
  177. package/dist/schemas/sse-message-created-payload.schema.json +0 -414
  178. package/dist/schemas/sse-message-deleted-payload.schema.json +0 -18
  179. package/dist/schemas/sse-message-updated-payload.schema.json +0 -46
  180. package/dist/schemas/sse-presence-payload.schema.json +0 -21
  181. package/dist/schemas/sse-provenance.schema.json +0 -64
  182. package/dist/schemas/sse-replay-expired-payload.schema.json +0 -21
  183. package/dist/schemas/sse-runtime-control-kind.schema.json +0 -13
  184. package/dist/schemas/sse-runtime-control-payload.schema.json +0 -44
  185. package/dist/schemas/sse-self-context.schema.json +0 -45
  186. package/dist/schemas/sse-turn-dispatch.schema.json +0 -29
  187. package/dist/schemas/sse-typing-payload.schema.json +0 -32
  188. package/dist/schemas/sse-voice-session-event-payload.schema.json +0 -170
  189. package/dist/schemas/sse-voice-session.schema.json +0 -157
  190. package/dist/schemas/trigger-decision.schema.json +0 -27
  191. package/dist/schemas/turn-lifecycle-state.schema.json +0 -14
  192. package/dist/schemas/turn-message-semantics.schema.json +0 -10
  193. package/dist/schemas/turn-metadata.schema.json +0 -56
  194. package/dist/schemas/turn-state-like.schema.json +0 -39
  195. package/dist/schemas/update-disposition-request.schema.json +0 -22
  196. package/dist/schemas/upload-media-request.schema.json +0 -25
  197. package/dist/schemas/upload-media-response.schema.json +0 -55
  198. package/dist/sendContextual.d.ts +0 -114
  199. package/dist/sendContextual.js +0 -89
  200. package/dist/sseEvents.d.ts +0 -474
  201. package/dist/sseEvents.js +0 -278
@@ -1,744 +0,0 @@
1
- // Bridge ⇄ runtime-adapter LOCAL JSON-RPC contract v1 (types + schemas ONLY —
2
- // no runtime, no transport; Phase 4 builds the bridge against these).
3
- //
4
- // Source spec: docs/design/bridge-local-api.md §A–§C. Where that draft
5
- // conflicts with code-grounded wire shapes (event names, REST payloads), the
6
- // code-grounded schemas in this package win — payload slots below COMPOSE
7
- // restEndpoints.ts / hitl.ts / sseEvents.ts / controlSignals.ts so every
8
- // shape is defined once.
9
- //
10
- // Protocol: JSON-RPC 2.0 over one duplex channel (stdio pipe or unix
11
- // socket / named pipe), NDJSON framing. Requests adapter→bridge (replied),
12
- // notifications bridge→adapter (never awaited). `hello` carries
13
- // protocolVersion — the local contract is version-guarded from v1.
14
- import { z } from 'zod';
15
- import { SerializedContactRequestSchema } from './contactRequest.js';
16
- import { DeliveryIntentSchema, TurnLifecycleStateSchema } from './turnProtocol.js';
17
- import { ControlPrimitiveEventSchema, ControlSessionEventSchema, ControlSignalEventSchema, } from './controlSignals.js';
18
- import { HitlApprovalCategorySchema, HitlApprovalDetailSchema, HitlApprovalRiskSchema, HitlInputKindSchema, HitlInputQuestionSchema, HitlPendingSchema, HitlRequestAcceptedSchema, HitlResolutionSchema, HitlResolutionStatusSchema, } from './hitl.js';
19
- import { ContactsResponseSchema, ConversationsResponseSchema, CreateContactRequestRequestSchema, CreateContactRequestResponseSchema, CreateConversationRequestSchema, CreateConversationResponseSchema, ForwardMessageRequestSchema, ForwardMessageResponseSchema, MessagesPageResponseSchema, ReactRequestSchema, ReactResponseSchema, ResolveAdmissionRequestSchema, ResolveAdmissionResponseSchema, RestContactSchema, RestSessionSelectionSchema, SendMessageRequestSchema, SendMessageResponseSchema, UpdateDispositionRequestSchema, UploadMediaResponseSchema, } from './restEndpoints.js';
20
- import { SendContextualRequestSchema, SendContextualResponseSchema, } from './sendContextual.js';
21
- import { SseAgentContextPayloadSchema, SseConversationUpdatedPayloadSchema, SseEventFamilySchema, SseMessageCreatedPayloadSchema, SseMessageDeletedPayloadSchema, SseMessageUpdatedPayloadSchema, SsePresencePayloadSchema, SseTypingPayloadSchema, SseVoiceSessionEventPayloadSchema, } from './sseEvents.js';
22
- export const BRIDGE_LOCAL_PROTOCOL_VERSION = 1;
23
- const OkResultSchema = z.object({ ok: z.literal(true) });
24
- export { OkResultSchema as BridgeOkResultSchema };
25
- // ── §A Session lifecycle ───────────────────────────────────────────────
26
- export const BridgeHelloParamsSchema = z.object({
27
- protocolVersion: z.number(),
28
- /** Adapter self-identification (claude-code | codex | openclaw | hermes | …). */
29
- clientType: z.string(),
30
- /** Event families to deliver; [] = pure-request adapter, no C-notifications. */
31
- wantFamilies: z.array(SseEventFamilySchema),
32
- /** Replay C-notifications missed while disconnected. */
33
- resumeCursor: z.string().optional(),
34
- /** Publish the runtime descriptor in the same round trip. */
35
- runtimeDescriptor: z.record(z.string(), z.unknown()).optional(),
36
- });
37
- // ── §G resume cursor / resync ───────────────────────────────────────────
38
- //
39
- // The bridge keeps a bounded, locally-sequenced event log. Every replayable
40
- // §C event notification carries the OPAQUE `cursor` token that produced it;
41
- // adapters persist the last cursor they fully processed and present it as
42
- // `hello.resumeCursor` on reconnect to replay exactly the gap. When the
43
- // cursor cannot be honored the hello result answers `sinceCursor: null`
44
- // PLUS a `resync` field — the adapter must treat the gap as lost locally and
45
- // recover out-of-band (REST backfill / state refresh). Never silent loss.
46
- export const BridgeResyncReasonSchema = z.enum([
47
- /** Cursor was valid but its events were evicted from the bounded log. */
48
- 'cursor-expired',
49
- /** Cursor was never issued by this bridge run (restart or corruption). */
50
- 'unknown-cursor',
51
- ]);
52
- export const BridgeResyncSchema = z.object({
53
- reason: BridgeResyncReasonSchema,
54
- });
55
- /**
56
- * Shape mixin for replayable §C notification params (`.extend(...)` onto the
57
- * payload schema). `cursor` is optional in the schema for compatibility, but
58
- * the bridge always sends it on log-delivered notifications. `ready` and
59
- * `connectionState` are connection-scoped (never replayed) and carry none.
60
- */
61
- export const BRIDGE_EVENT_CURSOR_SHAPE = {
62
- cursor: z.string().optional(),
63
- };
64
- export const BridgeHelloResultSchema = z.object({
65
- protocolVersion: z.number(),
66
- agentId: z.string(),
67
- agentName: z.string(),
68
- agentContext: SseAgentContextPayloadSchema,
69
- /** Cursor the delivered replay starts from; null = no replay. */
70
- sinceCursor: z.string().nullable(),
71
- /**
72
- * Present when resumeCursor could not be honored (§G): delivery starts at
73
- * the live head and the adapter must recover the gap out-of-band.
74
- */
75
- resync: BridgeResyncSchema.optional(),
76
- });
77
- export const BridgeUpstreamStateSchema = z.enum(['connected', 'reconnecting', 'down']);
78
- export const BridgeReadyNotificationSchema = z.object({
79
- agentId: z.string(),
80
- agentContext: SseAgentContextPayloadSchema,
81
- upstreamState: BridgeUpstreamStateSchema,
82
- });
83
- export const BridgeConnectionStateNotificationSchema = z.object({
84
- upstream: BridgeUpstreamStateSchema,
85
- });
86
- /**
87
- * `status` result (B4.6 observability): a live snapshot of the daemon
88
- * process, its upstream health, and the B4.2 hub counters. Additive to
89
- * contract v1 — served by the daemon and rendered by `canon-bridge status`.
90
- */
91
- export const BridgeStatusResultSchema = z.object({
92
- /** Daemon process id. */
93
- pid: z.number(),
94
- /** canon-bridge binary (package) version. */
95
- version: z.string(),
96
- /** bridgeLocal contract version the daemon speaks. */
97
- protocolVersion: z.number(),
98
- /** ISO timestamp of daemon start. */
99
- startedAt: z.string(),
100
- uptimeMs: z.number(),
101
- /** Lifecycle profile served (one identity per daemon). */
102
- profile: z.string(),
103
- /** Socket / named-pipe path; null in stdio (spawned-child) mode. */
104
- socketPath: z.string().nullable(),
105
- agentId: z.string(),
106
- agentName: z.string(),
107
- upstream: BridgeUpstreamStateSchema,
108
- /** Adapter connections currently subscribed to the fan-out. */
109
- connectedClients: z.number(),
110
- /** Non-zero B4.2 hub counters (upstream.* / fanout.*). */
111
- counters: z.record(z.string(), z.number()),
112
- });
113
- // ── §B7 HITL state (durable; resolution arrives via onHitlResolved) ────
114
- export const BridgeHitlStateSchema = z.object({
115
- status: z.union([z.literal('pending'), HitlResolutionStatusSchema]),
116
- answer: z.record(z.string(), z.unknown()).optional(),
117
- expiresAt: z.string(),
118
- });
119
- // ── §B8 media (named schemas so the bridge + Framework share the types) ──
120
- //
121
- // Byte transfer is bridge-owned (adapter-interface §1): adapters hand the
122
- // bridge a {path} (streamed from disk — preferred for anything big) or
123
- // inline {bytes} (base64, size-capped so a frame never breaches the NDJSON
124
- // line limit). getAttachment returns the bridge-cached materialization.
125
- export const BridgeUploadMediaParamsSchema = z.object({
126
- /** Absolute file path — the bridge streams it (exactly one of path | bytes). */
127
- path: z.string().optional(),
128
- /** Base64 bytes — size-capped by the bridge; prefer `path` for large files. */
129
- bytes: z.string().optional(),
130
- mime: z.string(),
131
- fileName: z.string(),
132
- conversationId: z.string(),
133
- });
134
- export const BridgeGetAttachmentParamsSchema = z.object({
135
- /** Storage download URL from a MediaAttachment (exactly one of url | attachmentId). */
136
- url: z.string().optional(),
137
- attachmentId: z.string().optional(),
138
- as: z.enum(['path', 'bytes']),
139
- });
140
- export const BridgeGetAttachmentResultSchema = z.object({
141
- /** Stable bridge-cache path — present when as:'path'. */
142
- path: z.string().optional(),
143
- /** Base64 — present when as:'bytes'; capped by the bridge's bytes ceiling. */
144
- bytes: z.string().optional(),
145
- mime: z.string(),
146
- });
147
- const conversationScoped = z.object({ conversationId: z.string() });
148
- const messageScoped = z.object({ conversationId: z.string(), messageId: z.string() });
149
- /**
150
- * RTDB server-timestamp sentinel — code-grounded pass-through (hosts write
151
- * `{'.sv':'timestamp'}` for openedAt/completedAt today; the bridge forwards
152
- * it to the RTDB REST API untouched).
153
- */
154
- const ServerTimestampSentinelSchema = z.object({ '.sv': z.literal('timestamp') });
155
- const EpochOrServerTimestampSchema = z.union([z.number(), ServerTimestampSentinelSchema]);
156
- // ─────────────────────────────────────────────────────────────────────────
157
- // W2-A4 REGION START — bridge-owned streaming assembly (raw event surface).
158
- // ADDITIVE: `publishStreamingEvent` lets a channel adapter forward RAW turn
159
- // events (a text segment begin/append/replace, a tool-trail item, a thinking
160
- // tick, a final compose, or a clear). The BRIDGE owns snapshot assembly, the
161
- // single write cadence, thinking keepalive, final compose, and node clear —
162
- // so adapters stop reimplementing turn-output-controller + a keepalive loop
163
- // (T1: they also stop swallowing the write failure). The pre-existing
164
- // snapshot `publishStreaming` (below) is UNTOUCHED — coding hosts still call
165
- // it via agent-host/runtime-writers.
166
- // ─────────────────────────────────────────────────────────────────────────
167
- /** A single tool-trail item forwarded raw; the bridge folds it into `blocks`. */
168
- export const BridgeStreamToolEventSchema = z.object({
169
- kind: z.literal('tool'),
170
- /** Stable block id (adapter-scoped, e.g. `tool:{turnId}`). */
171
- id: z.string(),
172
- status: z.enum(['running', 'completed', 'failed']).optional(),
173
- title: z.string().optional(),
174
- summary: z.string().optional(),
175
- detail: z.string().optional(),
176
- });
177
- /**
178
- * A visible-text segment. `mode:'append'` concatenates onto the segment's
179
- * running text; `mode:'replace'` overwrites it (the adapter already holds the
180
- * full segment). The bridge rebuilds the bubble text from its text segments in
181
- * sequence order, exactly as the core turn-output-controller does today.
182
- */
183
- export const BridgeStreamTextEventSchema = z.object({
184
- kind: z.literal('text'),
185
- /** Segment id (adapter-scoped, e.g. `text:{turnId}:{n}`). */
186
- id: z.string(),
187
- mode: z.enum(['append', 'replace']),
188
- text: z.string(),
189
- });
190
- /** A thinking tick (onset or keepalive) — no content; keeps the bubble warm. */
191
- export const BridgeStreamThinkingEventSchema = z.object({
192
- kind: z.literal('thinking'),
193
- });
194
- /**
195
- * Final compose: the assembled reply text is written one last time (status
196
- * settles to `streaming`) and the node is LEFT in place for the final-message
197
- * handoff. Clearing is a separate `clear` event / `clearStreaming`.
198
- */
199
- export const BridgeStreamFinalEventSchema = z.object({
200
- kind: z.literal('final'),
201
- text: z.string(),
202
- });
203
- /** Clear the streaming node (turn ended / interrupted). */
204
- export const BridgeStreamClearEventSchema = z.object({
205
- kind: z.literal('clear'),
206
- });
207
- export const BridgeStreamEventSchema = z.discriminatedUnion('kind', [
208
- BridgeStreamTextEventSchema,
209
- BridgeStreamToolEventSchema,
210
- BridgeStreamThinkingEventSchema,
211
- BridgeStreamFinalEventSchema,
212
- BridgeStreamClearEventSchema,
213
- ]);
214
- export const BridgePublishStreamingEventParamsSchema = conversationScoped.extend({
215
- /** New turnId resets the bridge's accumulation for this conversation. */
216
- turnId: z.string().nullable().optional(),
217
- messageId: z.string().optional(),
218
- event: BridgeStreamEventSchema,
219
- });
220
- // W2-A4 REGION END — bridge-owned streaming assembly.
221
- export const BRIDGE_LOCAL_METHODS = {
222
- // §A lifecycle
223
- hello: { params: BridgeHelloParamsSchema, result: BridgeHelloResultSchema },
224
- /** Liveness probe — the bridge answers without touching upstream. */
225
- ping: { params: z.object({}), result: OkResultSchema },
226
- shutdown: { params: z.object({}), result: OkResultSchema },
227
- /** Daemon snapshot (B4.6): process, upstream health, hub counters. */
228
- status: { params: z.object({}), result: BridgeStatusResultSchema },
229
- // §B1 messaging (outbound)
230
- sendMessage: {
231
- params: SendMessageRequestSchema,
232
- // B4.3 additive: the bridge chunks oversized text (core retry/chunking);
233
- // messageId is the FINAL chunk (carries the caller's metadata/turn
234
- // semantics), messageIds lists every part in send order.
235
- result: SendMessageResponseSchema.extend({
236
- messageIds: z.array(z.string()).optional(),
237
- }),
238
- },
239
- sendContextualMessage: {
240
- params: SendContextualRequestSchema,
241
- result: SendContextualResponseSchema,
242
- },
243
- reactToMessage: { params: ReactRequestSchema, result: ReactResponseSchema },
244
- forwardMessage: { params: ForwardMessageRequestSchema, result: ForwardMessageResponseSchema },
245
- deleteMessage: { params: messageScoped, result: OkResultSchema },
246
- markRead: { params: conversationScoped, result: OkResultSchema },
247
- setDisposition: {
248
- params: messageScoped.extend(UpdateDispositionRequestSchema.shape),
249
- result: OkResultSchema,
250
- },
251
- // §B2 reading / history
252
- getMessages: {
253
- params: z.object({
254
- conversationId: z.string(),
255
- limit: z.number().optional(),
256
- before: z.string().optional(),
257
- includeBehavior: z.boolean().optional(),
258
- }),
259
- // B4.3 additive: nextCursor is the bridge-derived `before` token for the
260
- // next page (oldest returned message id), present when the page filled
261
- // the requested limit. The REST surface itself emits no cursor.
262
- result: MessagesPageResponseSchema.extend({
263
- nextCursor: z.string().optional(),
264
- }),
265
- },
266
- getConversations: { params: z.object({}), result: ConversationsResponseSchema },
267
- // §B3 conversation / group management
268
- createConversation: {
269
- params: CreateConversationRequestSchema,
270
- result: CreateConversationResponseSchema,
271
- },
272
- updateTopic: {
273
- params: conversationScoped.extend({ topic: z.string() }),
274
- result: OkResultSchema,
275
- },
276
- updateName: {
277
- params: conversationScoped.extend({ name: z.string() }),
278
- result: OkResultSchema,
279
- },
280
- updateAvatar: {
281
- params: conversationScoped.extend({ avatarUrl: z.string().nullable().optional() }),
282
- result: OkResultSchema,
283
- },
284
- addMember: {
285
- params: conversationScoped.extend({ userId: z.string() }),
286
- result: z.union([
287
- z.object({ status: z.literal('added') }),
288
- z.object({ status: z.literal('pending'), requestId: z.string() }),
289
- ]),
290
- },
291
- removeMember: {
292
- params: conversationScoped.extend({ userId: z.string() }),
293
- result: OkResultSchema,
294
- },
295
- leaveConversation: { params: conversationScoped, result: OkResultSchema },
296
- muteConversation: {
297
- params: conversationScoped.extend({ until: z.number().optional() }),
298
- result: OkResultSchema,
299
- },
300
- unmuteConversation: { params: conversationScoped, result: OkResultSchema },
301
- hideConversation: { params: conversationScoped, result: OkResultSchema },
302
- // §B4 contacts / admission / blocking
303
- createContactRequest: {
304
- params: CreateContactRequestRequestSchema,
305
- result: CreateContactRequestResponseSchema,
306
- },
307
- listContacts: { params: z.object({}), result: ContactsResponseSchema },
308
- getContact: {
309
- params: z.object({ contactId: z.string() }),
310
- result: RestContactSchema.nullable(),
311
- },
312
- removeContact: { params: z.object({ contactId: z.string() }), result: OkResultSchema },
313
- listContactRequests: {
314
- params: z.object({}),
315
- result: z.object({ requests: z.array(SerializedContactRequestSchema) }),
316
- },
317
- resolveAdmission: {
318
- params: ResolveAdmissionRequestSchema,
319
- result: ResolveAdmissionResponseSchema,
320
- },
321
- reachOut: {
322
- // B6.1 additive (P6 openclaw): the optional fields mirror core
323
- // reachOutToCanonContact options — contact-request message, agent
324
- // session config/selection, and SendMessageOptions passthrough — so
325
- // channel adapters keep today's reach-out fidelity through the bridge.
326
- params: z.object({
327
- targetId: z.string(),
328
- /**
329
- * Optional (B6.1): core reach-out treats a missing text as
330
- * "resolve/open the conversation only" — the openclaw media path
331
- * reaches out first and sends the uploaded attachment separately.
332
- */
333
- text: z.string().optional(),
334
- selfContext: z.string().optional(),
335
- /** Contact-request message when admission requires approval. */
336
- requestMessage: z.string().optional(),
337
- /** Initial agent session config — opaque pass-through. */
338
- sessionConfig: z.record(z.string(), z.unknown()).nullable().optional(),
339
- /** Same union as the REST send surfaces (incl. mode: 'specific'). */
340
- sessionSelection: RestSessionSelectionSchema.optional(),
341
- /** SendMessageOptions passthrough (replyTo, selfContextId, metadata, …). */
342
- sendMessageOptions: z.record(z.string(), z.unknown()).optional(),
343
- }),
344
- result: z.record(z.string(), z.unknown()),
345
- },
346
- blockUser: { params: z.object({ userId: z.string() }), result: OkResultSchema },
347
- unblockUser: { params: z.object({ userId: z.string() }), result: OkResultSchema },
348
- // §B5 live presence / streaming (bridge throttles internally)
349
- setTyping: {
350
- params: conversationScoped.extend({ state: z.enum(['typing', 'thinking', 'clear']) }),
351
- result: OkResultSchema,
352
- },
353
- publishStreaming: {
354
- params: conversationScoped.extend({
355
- turnId: z.string().nullable().optional(),
356
- messageId: z.string().optional(),
357
- text: z.string().optional(),
358
- delta: z.string().optional(),
359
- /** TurnOutputBlock[] — opaque pass-through. */
360
- blocks: z.array(z.record(z.string(), z.unknown())).optional(),
361
- /**
362
- * Explicit bubble/presentation status override (union of B6.1 openclaw
363
- * + P6 host surface): 'tool' / 'waiting_input' cannot be derived from
364
- * text/blocks. Omitted: the bridge derives thinking/streaming from
365
- * content as before.
366
- */
367
- status: z.enum(['thinking', 'streaming', 'tool', 'waiting_input']).optional(),
368
- done: z.boolean().optional(),
369
- }),
370
- result: OkResultSchema,
371
- },
372
- clearStreaming: { params: conversationScoped, result: OkResultSchema },
373
- // W2-A4 additive: raw streaming events (bridge owns assembly/cadence/
374
- // keepalive/final/clear). Coexists with the snapshot publishStreaming above.
375
- publishStreamingEvent: {
376
- params: BridgePublishStreamingEventParamsSchema,
377
- result: OkResultSchema,
378
- },
379
- // §B6 runtime presentation (adapter owns content, bridge publishes)
380
- publishRuntimeDescriptor: {
381
- params: z.object({
382
- descriptor: z.record(z.string(), z.unknown()),
383
- /**
384
- * B6.1 additive (P6 openclaw): rides into the published /agent-runtime
385
- * payload — hosts and host-mode channel plugins publish hostMode: true
386
- * today and the field is app-visible. Latched for subsequent publishes;
387
- * defaults to false (bridge-adapter split is not a host process).
388
- */
389
- hostMode: z.boolean().optional(),
390
- }),
391
- result: OkResultSchema,
392
- },
393
- publishRuntimeStatus: {
394
- // Code-grounded correction (B4.3): RTDB runtime-info lives per
395
- // conversation (/runtime-info/{convoId}/{agentId}), so the publish is
396
- // conversation-scoped. `presentation` carries the remaining
397
- // RuntimeInfoPayload fields (surfaceMode, facts, inventories, notes, …)
398
- // as an opaque pass-through.
399
- params: conversationScoped.extend({
400
- statusItems: z.array(z.record(z.string(), z.unknown())).optional(),
401
- presentation: z.record(z.string(), z.unknown()).optional(),
402
- }),
403
- result: OkResultSchema,
404
- },
405
- publishTurnState: {
406
- // B6.1 additive (P6 openclaw): the remaining CanonTurnStatePayload
407
- // fields hosts publish today. `completedAt: true` asks the bridge to
408
- // stamp the server timestamp (the RTDB sentinel is bridge-owned).
409
- params: conversationScoped.extend({
410
- turnId: z.string().nullable().optional(),
411
- state: TurnLifecycleStateSchema,
412
- queueDepth: z.number().optional(),
413
- capabilities: z.record(z.string(), z.unknown()).optional(),
414
- // Union of B6.1 (openclaw) + P6 host surface — the remaining
415
- // code-grounded TurnStatePayload fields (rtdb-rest.ts).
416
- currentSpeakerId: z.string().nullable().optional(),
417
- lastAcceptedIntent: DeliveryIntentSchema.nullable().optional(),
418
- activeMessageIds: z.array(z.string()).optional(),
419
- openedAt: EpochOrServerTimestampSchema.optional(),
420
- turnUpdatedAt: EpochOrServerTimestampSchema.nullable().optional(),
421
- // `true` (B6.1 openclaw) asks the bridge to stamp the server-timestamp
422
- // sentinel; hosts (P6) pass an epoch or the sentinel through directly.
423
- completedAt: z
424
- .union([z.boolean(), EpochOrServerTimestampSchema])
425
- .nullable()
426
- .optional(),
427
- }),
428
- result: OkResultSchema,
429
- },
430
- clearTurnState: { params: conversationScoped, result: OkResultSchema },
431
- publishActivity: {
432
- params: conversationScoped.extend({
433
- activityItems: z.array(z.record(z.string(), z.unknown())),
434
- }),
435
- result: OkResultSchema,
436
- },
437
- clearActivity: {
438
- // B6.1 additive (P6 openclaw): remove activity blocks. `all` wipes the
439
- // conversation's node; `itemIds` removes specific blocks; neither falls
440
- // back to the publisher's terminal-only sweep semantics.
441
- params: conversationScoped.extend({
442
- all: z.boolean().optional(),
443
- itemIds: z.array(z.string()).optional(),
444
- }),
445
- result: OkResultSchema,
446
- },
447
- /** B6.1 additive: clear the per-conversation runtime-info node. */
448
- clearRuntimeStatus: { params: conversationScoped, result: OkResultSchema },
449
- /**
450
- * B6.1 additive: clear /agent-runtime for this identity (preserves
451
- * descriptor-bearing payloads with updatedAt: 0, like hosts do today).
452
- */
453
- clearRuntimeDescriptor: { params: z.object({}), result: OkResultSchema },
454
- // §B7 HITL (durable; returns requestId, resolves via onHitlResolved).
455
- //
456
- // P6 host surface (additive): process-executing hosts publish the SAME
457
- // rich records channel runtimes create today (approval cards with
458
- // diff/details/native handles, titled input cards, card ids the runtime
459
- // correlates on). All new fields are optional — the narrow Phase-4 shape
460
- // still parses. `requestId`/`inputId`/`cardId`-style explicit ids become
461
- // the bridge record's requestId so adapters keep their own correlation.
462
- // `responseUserId: null` means "let the server pick a reachable responder"
463
- // (codex cards today); absent keeps the Phase-4 default (agent owner).
464
- requestApproval: {
465
- // B6.1 additive (P6 openclaw): optional approval-card fidelity fields
466
- // (core createRuntimeApprovalRequest passthrough) so native runtime
467
- // approvals keep their tool name, detail rows, correlation handles and
468
- // allow-always affordance through the bridge.
469
- params: conversationScoped.extend({
470
- detail: z.string(),
471
- risk: HitlApprovalRiskSchema,
472
- category: HitlApprovalCategorySchema,
473
- expiresAt: z.string().optional(),
474
- approvalId: z.string().optional(),
475
- /** Card header tool name; defaults to the category. */
476
- toolName: z.string().optional(),
477
- riskLevel: z.enum(['normal', 'destructive']).optional(),
478
- details: z.array(HitlApprovalDetailSchema).optional(),
479
- /** File-change preview (CanonUnifiedDiff) — opaque pass-through. */
480
- diff: z.record(z.string(), z.unknown()).optional(),
481
- /** Native runtime correlation handles — opaque pass-through. */
482
- native: z.record(z.string(), z.unknown()).optional(),
483
- turnId: z.string().optional(),
484
- runtimeId: z.string().optional(),
485
- /** Offer the "always allow" session-rule affordance on the card. */
486
- allowSessionRule: z.boolean().optional(),
487
- responseUserId: z.string().nullable().optional(),
488
- }),
489
- result: HitlRequestAcceptedSchema,
490
- },
491
- requestInput: {
492
- params: conversationScoped.extend({
493
- questions: z.array(HitlInputQuestionSchema),
494
- expiresAt: z.string().optional(),
495
- inputId: z.string().optional(),
496
- title: z.string().optional(),
497
- prompt: z.string().optional(),
498
- kind: HitlInputKindSchema.optional(),
499
- sensitive: z.boolean().optional(),
500
- native: z.record(z.string(), z.unknown()).optional(),
501
- turnId: z.string().optional(),
502
- responseUserId: z.string().nullable().optional(),
503
- }),
504
- result: HitlRequestAcceptedSchema,
505
- },
506
- requestCard: {
507
- params: conversationScoped.extend({
508
- /** canon.card.v1 content — opaque pass-through. */
509
- card: z.record(z.string(), z.unknown()),
510
- expiresAt: z.string().optional(),
511
- cardId: z.string().optional(),
512
- native: z.record(z.string(), z.unknown()).optional(),
513
- turnId: z.string().optional(),
514
- responseUserId: z.string().nullable().optional(),
515
- }),
516
- result: HitlRequestAcceptedSchema,
517
- },
518
- getHitlState: {
519
- params: z.object({ requestId: z.string() }),
520
- result: BridgeHitlStateSchema,
521
- },
522
- cancelHitl: { params: z.object({ requestId: z.string() }), result: OkResultSchema },
523
- // §B8 media (named schemas above — the bridge + Framework share them)
524
- uploadMedia: {
525
- params: BridgeUploadMediaParamsSchema,
526
- result: UploadMediaResponseSchema,
527
- },
528
- getAttachment: {
529
- params: BridgeGetAttachmentParamsSchema,
530
- result: BridgeGetAttachmentResultSchema,
531
- },
532
- // §B9 voice (media stays client↔LiveKit; bridge brokers the token).
533
- // Code-grounded correction (B4.3): every /voice/sessions/:id REST op
534
- // requires the conversationId (body or query) — the session doc lives
535
- // under the conversation — so the session-scoped methods carry it too.
536
- createVoiceSession: {
537
- params: conversationScoped.extend({ targetAgentId: z.string().optional() }),
538
- result: z.looseObject({ sessionId: z.string() }),
539
- },
540
- joinVoiceSession: {
541
- params: z.object({ sessionId: z.string(), conversationId: z.string() }),
542
- result: z.looseObject({ url: z.string(), token: z.string() }),
543
- },
544
- endVoiceSession: {
545
- params: z.object({ sessionId: z.string(), conversationId: z.string() }),
546
- result: OkResultSchema,
547
- },
548
- getVoiceSession: {
549
- params: z.object({ sessionId: z.string(), conversationId: z.string() }),
550
- result: SseVoiceSessionEventPayloadSchema,
551
- },
552
- // §B10 registration / identity
553
- getAgent: {
554
- params: z.object({}),
555
- result: z.object({
556
- agentId: z.string(),
557
- agentName: z.string(),
558
- context: SseAgentContextPayloadSchema,
559
- }),
560
- },
561
- register: {
562
- params: z.record(z.string(), z.unknown()),
563
- // pollToken (when issued) authorizes subsequent status polls.
564
- result: z.object({ requestId: z.string(), pollToken: z.string().optional() }),
565
- },
566
- getRegistrationStatus: {
567
- // `baseUrl` (B6.1 additive) supports the pre-identity bootstrap: the
568
- // registration statics are unauthenticated, so a stub-identity bridge
569
- // can serve them with an explicit endpoint.
570
- params: z.object({
571
- requestId: z.string(),
572
- pollToken: z.string().optional(),
573
- baseUrl: z.string().optional(),
574
- }),
575
- result: z.looseObject({ status: z.string() }),
576
- },
577
- /**
578
- * Acknowledge one-time API-key delivery after an approved registration
579
- * (POST /agents/status/:id/ack). Best-effort on the wire; failures surface
580
- * as upstream-band errors. Additive union of Phase 5 + P6 openclaw: the
581
- * Hermes port consumed it through the GENERIC Python escape hatch first
582
- * (a bridge-side method addition with zero framework-py release), and
583
- * `baseUrl` (B6.1) supports the wizard's per-call pre-identity bootstrap.
584
- */
585
- ackRegistrationStatus: {
586
- params: z.object({
587
- requestId: z.string(),
588
- pollToken: z.string().optional(),
589
- baseUrl: z.string().optional(),
590
- }),
591
- result: OkResultSchema,
592
- },
593
- // §5b host-profile surface (bridge-execution-plan Phase 4 step 5b) —
594
- // additive methods for process-executing (host-mode) adapters, beyond §B.
595
- // Shapes mirror @canonmsg/core host-runtime-wire.ts; runtime payloads are
596
- // opaque pass-through (AgentRuntime).
597
- publishHostAgentRuntime: {
598
- params: z.object({
599
- /** Defaults to the hello clientType. */
600
- clientType: z.string().optional(),
601
- /** AgentRuntime — opaque pass-through. */
602
- runtime: z.record(z.string(), z.unknown()),
603
- }),
604
- result: OkResultSchema,
605
- },
606
- publishHostSessionSnapshots: {
607
- params: z.object({
608
- conversationIds: z.array(z.string()),
609
- /** Defaults to the hello clientType. */
610
- clientType: z.string().optional(),
611
- /** AgentRuntime — opaque pass-through. */
612
- runtime: z.record(z.string(), z.unknown()),
613
- workspaceOptions: z.array(z.object({ id: z.string(), cwd: z.string() })).optional(),
614
- defaultCwd: z.string().optional(),
615
- extraSessionConfigFields: z.array(z.string()).optional(),
616
- /** conversationId → live session-config overrides (JSON'd Map). */
617
- liveSessionConfigByConversation: z
618
- .record(z.string(), z.record(z.string(), z.unknown()))
619
- .optional(),
620
- }),
621
- result: OkResultSchema,
622
- },
623
- // §5b host-profile surface, P6 additions (bridge-execution-plan Phase 6) —
624
- // the remaining RTDB planes the process-executing hosts touched directly
625
- // until now: the per-conversation session-state node, its config read, and
626
- // the terminal clears. Payloads mirror @canonmsg/core rtdb-rest.ts shapes
627
- // as opaque pass-through; the bridge executes them through the core
628
- // runtime-state publisher (hostMode) over its single RTDB handle.
629
- getSessionConfig: {
630
- params: conversationScoped,
631
- /** Raw `/session-config/{convoId}/{agentId}` node; adapters parse locally. */
632
- result: z.record(z.string(), z.unknown()).nullable(),
633
- },
634
- publishSessionState: {
635
- params: conversationScoped.extend({
636
- /** SessionStatePayload minus updatedAt — opaque pass-through. */
637
- state: z.record(z.string(), z.unknown()),
638
- }),
639
- result: OkResultSchema,
640
- },
641
- clearSessionState: { params: conversationScoped, result: OkResultSchema },
642
- // clearTurnState is defined once under §B6 above (both legs added it; the
643
- // clear is publisher-agnostic so channel adapters and hosts share it).
644
- /**
645
- * Terminal /agent-runtime clear through the HOST publisher. Same RTDB
646
- * effect as §B6.1 clearRuntimeDescriptor (preserve descriptor-bearing
647
- * payloads with updatedAt: 0) — kept as a distinct §5b name because host
648
- * consumers correlate it with the publishHostAgentRuntime plane.
649
- */
650
- clearAgentRuntime: { params: z.object({}), result: OkResultSchema },
651
- };
652
- export const BRIDGE_LOCAL_METHOD_NAMES = Object.keys(BRIDGE_LOCAL_METHODS);
653
- // ── §C Notification registry (bridge → adapter, one-way) ───────────────
654
- export const BRIDGE_LOCAL_NOTIFICATIONS = {
655
- // §A — connection-scoped, never replayed, no cursor
656
- ready: BridgeReadyNotificationSchema,
657
- connectionState: BridgeConnectionStateNotificationSchema,
658
- // §C1 inbound messages (pre-filtered: deduped, self/control stripped)
659
- onMessage: SseMessageCreatedPayloadSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
660
- onMessageUpdated: SseMessageUpdatedPayloadSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
661
- onMessageDeleted: SseMessageDeletedPayloadSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
662
- onConversationUpdated: SseConversationUpdatedPayloadSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
663
- // §C2 control (bridge consumed from RTDB — adapters never poll)
664
- onControlSignal: ControlSignalEventSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
665
- onSessionControl: ControlSessionEventSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
666
- // §C2 addition (P6, both legs): `/control/.../primitive` runtime command
667
- // dispatch (/status, /compact, …) consumed by the bridge poller — adapters
668
- // (openclaw handlers, codex compact) never poll RTDB themselves.
669
- onControlPrimitive: ControlPrimitiveEventSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
670
- // §C6 HITL resolution (durable; matches a §B7 requestId)
671
- onHitlResolved: HitlResolutionSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
672
- onHitlPending: HitlPendingSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
673
- /**
674
- * §C6 addition (P6 host surface): a stripped HITL control-reply message
675
- * (metadata.type approval_reply / plan_approval_reply) that matched NO
676
- * bridge-owned HITL record fans out here instead of being dropped —
677
- * process-executing hosts run reply flows the bridge did not raise
678
- * (claude plan approval is a plain sendMessage + metadata-reply waiter).
679
- * `message` is the serialized Canon message, opaque pass-through.
680
- */
681
- onControlReply: z.object({
682
- conversationId: z.string(),
683
- message: z.record(z.string(), z.unknown()),
684
- }).extend(BRIDGE_EVENT_CURSOR_SHAPE),
685
- // §C3 contacts
686
- onContactChanged: z.object({
687
- kind: z.enum(['request', 'approved', 'added', 'updated', 'removed']),
688
- contact: z.record(z.string(), z.unknown()),
689
- }).extend(BRIDGE_EVENT_CURSOR_SHAPE),
690
- // §C4 typing / presence
691
- onTyping: SseTypingPayloadSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
692
- onPresence: SsePresencePayloadSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
693
- // §C5 voice
694
- onVoiceSessionStarted: SseVoiceSessionEventPayloadSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
695
- onVoiceSessionEnded: SseVoiceSessionEventPayloadSchema.extend(BRIDGE_EVENT_CURSOR_SHAPE),
696
- };
697
- export const BRIDGE_LOCAL_NOTIFICATION_NAMES = Object.keys(BRIDGE_LOCAL_NOTIFICATIONS);
698
- /**
699
- * Event family per §C notification, mirroring SSE_EVENT_FAMILY_BY_TYPE.
700
- * `lifecycle` notifications are connection-scoped and delivered to every
701
- * client; family notifications are filtered by `hello.wantFamilies`
702
- * (`wantFamilies: []` = pure-request adapter, no §C events at all).
703
- */
704
- export const BRIDGE_LOCAL_NOTIFICATION_FAMILIES = {
705
- ready: 'lifecycle',
706
- connectionState: 'lifecycle',
707
- onMessage: 'messages',
708
- onMessageUpdated: 'messages',
709
- onMessageDeleted: 'messages',
710
- onConversationUpdated: 'messages',
711
- onControlSignal: 'runtime_turn',
712
- onSessionControl: 'runtime_turn',
713
- onControlPrimitive: 'runtime_turn',
714
- onHitlResolved: 'runtime_turn',
715
- onHitlPending: 'runtime_turn',
716
- onControlReply: 'runtime_turn',
717
- onContactChanged: 'contacts',
718
- onTyping: 'typing_presence',
719
- onPresence: 'typing_presence',
720
- onVoiceSessionStarted: 'voice',
721
- onVoiceSessionEnded: 'voice',
722
- };
723
- // ── JSON-RPC 2.0 framing ───────────────────────────────────────────────
724
- export const BridgeRpcIdSchema = z.union([z.string(), z.number()]);
725
- export const BridgeRpcRequestSchema = z.object({
726
- jsonrpc: z.literal('2.0'),
727
- id: BridgeRpcIdSchema,
728
- method: z.string(),
729
- params: z.unknown().optional(),
730
- });
731
- export const BridgeRpcNotificationSchema = z.object({
732
- jsonrpc: z.literal('2.0'),
733
- method: z.string(),
734
- params: z.unknown().optional(),
735
- });
736
- export const BridgeRpcErrorSchema = z.object({
737
- code: z.number(),
738
- message: z.string(),
739
- data: z.unknown().optional(),
740
- });
741
- export const BridgeRpcResponseSchema = z.union([
742
- z.object({ jsonrpc: z.literal('2.0'), id: BridgeRpcIdSchema, result: z.unknown() }),
743
- z.object({ jsonrpc: z.literal('2.0'), id: BridgeRpcIdSchema.nullable(), error: BridgeRpcErrorSchema }),
744
- ]);