@canonmsg/backend-contracts 1.8.0 → 1.9.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.
@@ -175,6 +175,111 @@ export interface BridgeLocalMethodContract {
175
175
  params: z.ZodType;
176
176
  result: z.ZodType;
177
177
  }
178
+ /** A single tool-trail item forwarded raw; the bridge folds it into `blocks`. */
179
+ export declare const BridgeStreamToolEventSchema: z.ZodObject<{
180
+ kind: z.ZodLiteral<"tool">;
181
+ id: z.ZodString;
182
+ status: z.ZodOptional<z.ZodEnum<{
183
+ completed: "completed";
184
+ running: "running";
185
+ failed: "failed";
186
+ }>>;
187
+ title: z.ZodOptional<z.ZodString>;
188
+ summary: z.ZodOptional<z.ZodString>;
189
+ detail: z.ZodOptional<z.ZodString>;
190
+ }, z.core.$strip>;
191
+ /**
192
+ * A visible-text segment. `mode:'append'` concatenates onto the segment's
193
+ * running text; `mode:'replace'` overwrites it (the adapter already holds the
194
+ * full segment). The bridge rebuilds the bubble text from its text segments in
195
+ * sequence order, exactly as the core turn-output-controller does today.
196
+ */
197
+ export declare const BridgeStreamTextEventSchema: z.ZodObject<{
198
+ kind: z.ZodLiteral<"text">;
199
+ id: z.ZodString;
200
+ mode: z.ZodEnum<{
201
+ replace: "replace";
202
+ append: "append";
203
+ }>;
204
+ text: z.ZodString;
205
+ }, z.core.$strip>;
206
+ /** A thinking tick (onset or keepalive) — no content; keeps the bubble warm. */
207
+ export declare const BridgeStreamThinkingEventSchema: z.ZodObject<{
208
+ kind: z.ZodLiteral<"thinking">;
209
+ }, z.core.$strip>;
210
+ /**
211
+ * Final compose: the assembled reply text is written one last time (status
212
+ * settles to `streaming`) and the node is LEFT in place for the final-message
213
+ * handoff. Clearing is a separate `clear` event / `clearStreaming`.
214
+ */
215
+ export declare const BridgeStreamFinalEventSchema: z.ZodObject<{
216
+ kind: z.ZodLiteral<"final">;
217
+ text: z.ZodString;
218
+ }, z.core.$strip>;
219
+ /** Clear the streaming node (turn ended / interrupted). */
220
+ export declare const BridgeStreamClearEventSchema: z.ZodObject<{
221
+ kind: z.ZodLiteral<"clear">;
222
+ }, z.core.$strip>;
223
+ export declare const BridgeStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
224
+ kind: z.ZodLiteral<"text">;
225
+ id: z.ZodString;
226
+ mode: z.ZodEnum<{
227
+ replace: "replace";
228
+ append: "append";
229
+ }>;
230
+ text: z.ZodString;
231
+ }, z.core.$strip>, z.ZodObject<{
232
+ kind: z.ZodLiteral<"tool">;
233
+ id: z.ZodString;
234
+ status: z.ZodOptional<z.ZodEnum<{
235
+ completed: "completed";
236
+ running: "running";
237
+ failed: "failed";
238
+ }>>;
239
+ title: z.ZodOptional<z.ZodString>;
240
+ summary: z.ZodOptional<z.ZodString>;
241
+ detail: z.ZodOptional<z.ZodString>;
242
+ }, z.core.$strip>, z.ZodObject<{
243
+ kind: z.ZodLiteral<"thinking">;
244
+ }, z.core.$strip>, z.ZodObject<{
245
+ kind: z.ZodLiteral<"final">;
246
+ text: z.ZodString;
247
+ }, z.core.$strip>, z.ZodObject<{
248
+ kind: z.ZodLiteral<"clear">;
249
+ }, z.core.$strip>], "kind">;
250
+ export type BridgeStreamEvent = z.infer<typeof BridgeStreamEventSchema>;
251
+ export declare const BridgePublishStreamingEventParamsSchema: z.ZodObject<{
252
+ conversationId: z.ZodString;
253
+ turnId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
254
+ messageId: z.ZodOptional<z.ZodString>;
255
+ event: z.ZodDiscriminatedUnion<[z.ZodObject<{
256
+ kind: z.ZodLiteral<"text">;
257
+ id: z.ZodString;
258
+ mode: z.ZodEnum<{
259
+ replace: "replace";
260
+ append: "append";
261
+ }>;
262
+ text: z.ZodString;
263
+ }, z.core.$strip>, z.ZodObject<{
264
+ kind: z.ZodLiteral<"tool">;
265
+ id: z.ZodString;
266
+ status: z.ZodOptional<z.ZodEnum<{
267
+ completed: "completed";
268
+ running: "running";
269
+ failed: "failed";
270
+ }>>;
271
+ title: z.ZodOptional<z.ZodString>;
272
+ summary: z.ZodOptional<z.ZodString>;
273
+ detail: z.ZodOptional<z.ZodString>;
274
+ }, z.core.$strip>, z.ZodObject<{
275
+ kind: z.ZodLiteral<"thinking">;
276
+ }, z.core.$strip>, z.ZodObject<{
277
+ kind: z.ZodLiteral<"final">;
278
+ text: z.ZodString;
279
+ }, z.core.$strip>, z.ZodObject<{
280
+ kind: z.ZodLiteral<"clear">;
281
+ }, z.core.$strip>], "kind">;
282
+ }, z.core.$strip>;
178
283
  export declare const BRIDGE_LOCAL_METHODS: {
179
284
  readonly hello: {
180
285
  readonly params: z.ZodObject<{
@@ -519,24 +624,24 @@ export declare const BRIDGE_LOCAL_METHODS: {
519
624
  behavior: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
520
625
  hasUnread: z.ZodOptional<z.ZodBoolean>;
521
626
  lastMessage: z.ZodNullable<z.ZodObject<{
522
- text: z.ZodString;
627
+ text: z.ZodNullable<z.ZodString>;
523
628
  messageId: z.ZodOptional<z.ZodString>;
524
- senderId: z.ZodString;
525
- senderType: z.ZodEnum<{
629
+ senderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
630
+ senderType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
526
631
  human: "human";
527
632
  ai_agent: "ai_agent";
528
- }>;
529
- contentType: z.ZodOptional<z.ZodEnum<{
633
+ }>>>;
634
+ contentType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
530
635
  file: "file";
531
636
  image: "image";
532
637
  audio: "audio";
533
638
  video: "video";
534
639
  text: "text";
535
640
  contact_card: "contact_card";
536
- }>>;
537
- timestamp: z.ZodString;
641
+ }>>>;
642
+ timestamp: z.ZodNullable<z.ZodString>;
538
643
  }, z.core.$loose>>;
539
- createdAt: z.ZodOptional<z.ZodString>;
644
+ createdAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
540
645
  }, z.core.$loose>>;
541
646
  }, z.core.$strip>;
542
647
  };
@@ -867,6 +972,43 @@ export declare const BRIDGE_LOCAL_METHODS: {
867
972
  ok: z.ZodLiteral<true>;
868
973
  }, z.core.$strip>;
869
974
  };
975
+ readonly publishStreamingEvent: {
976
+ readonly params: z.ZodObject<{
977
+ conversationId: z.ZodString;
978
+ turnId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
979
+ messageId: z.ZodOptional<z.ZodString>;
980
+ event: z.ZodDiscriminatedUnion<[z.ZodObject<{
981
+ kind: z.ZodLiteral<"text">;
982
+ id: z.ZodString;
983
+ mode: z.ZodEnum<{
984
+ replace: "replace";
985
+ append: "append";
986
+ }>;
987
+ text: z.ZodString;
988
+ }, z.core.$strip>, z.ZodObject<{
989
+ kind: z.ZodLiteral<"tool">;
990
+ id: z.ZodString;
991
+ status: z.ZodOptional<z.ZodEnum<{
992
+ completed: "completed";
993
+ running: "running";
994
+ failed: "failed";
995
+ }>>;
996
+ title: z.ZodOptional<z.ZodString>;
997
+ summary: z.ZodOptional<z.ZodString>;
998
+ detail: z.ZodOptional<z.ZodString>;
999
+ }, z.core.$strip>, z.ZodObject<{
1000
+ kind: z.ZodLiteral<"thinking">;
1001
+ }, z.core.$strip>, z.ZodObject<{
1002
+ kind: z.ZodLiteral<"final">;
1003
+ text: z.ZodString;
1004
+ }, z.core.$strip>, z.ZodObject<{
1005
+ kind: z.ZodLiteral<"clear">;
1006
+ }, z.core.$strip>], "kind">;
1007
+ }, z.core.$strip>;
1008
+ readonly result: z.ZodObject<{
1009
+ ok: z.ZodLiteral<true>;
1010
+ }, z.core.$strip>;
1011
+ };
870
1012
  readonly publishRuntimeDescriptor: {
871
1013
  readonly params: z.ZodObject<{
872
1014
  descriptor: z.ZodRecord<z.ZodString, z.ZodUnknown>;
@@ -153,6 +153,71 @@ const messageScoped = z.object({ conversationId: z.string(), messageId: z.string
153
153
  */
154
154
  const ServerTimestampSentinelSchema = z.object({ '.sv': z.literal('timestamp') });
155
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.
156
221
  export const BRIDGE_LOCAL_METHODS = {
157
222
  // §A lifecycle
158
223
  hello: { params: BridgeHelloParamsSchema, result: BridgeHelloResultSchema },
@@ -305,6 +370,12 @@ export const BRIDGE_LOCAL_METHODS = {
305
370
  result: OkResultSchema,
306
371
  },
307
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
+ },
308
379
  // §B6 runtime presentation (adapter owns content, bridge publishes)
309
380
  publishRuntimeDescriptor: {
310
381
  params: z.object({
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BridgeRpcResponseSchema = exports.BridgeRpcErrorSchema = exports.BridgeRpcNotificationSchema = exports.BridgeRpcRequestSchema = exports.BridgeRpcIdSchema = exports.BRIDGE_LOCAL_NOTIFICATION_FAMILIES = exports.BRIDGE_LOCAL_NOTIFICATION_NAMES = exports.BRIDGE_LOCAL_NOTIFICATIONS = exports.BRIDGE_LOCAL_METHOD_NAMES = exports.BRIDGE_LOCAL_METHODS = exports.BridgeGetAttachmentResultSchema = exports.BridgeGetAttachmentParamsSchema = exports.BridgeUploadMediaParamsSchema = exports.BridgeHitlStateSchema = exports.BridgeStatusResultSchema = exports.BridgeConnectionStateNotificationSchema = exports.BridgeReadyNotificationSchema = exports.BridgeUpstreamStateSchema = exports.BridgeHelloResultSchema = exports.BRIDGE_EVENT_CURSOR_SHAPE = exports.BridgeResyncSchema = exports.BridgeResyncReasonSchema = exports.BridgeHelloParamsSchema = exports.BridgeOkResultSchema = exports.BRIDGE_LOCAL_PROTOCOL_VERSION = void 0;
3
+ exports.BridgeRpcResponseSchema = exports.BridgeRpcErrorSchema = exports.BridgeRpcNotificationSchema = exports.BridgeRpcRequestSchema = exports.BridgeRpcIdSchema = exports.BRIDGE_LOCAL_NOTIFICATION_FAMILIES = exports.BRIDGE_LOCAL_NOTIFICATION_NAMES = exports.BRIDGE_LOCAL_NOTIFICATIONS = exports.BRIDGE_LOCAL_METHOD_NAMES = exports.BRIDGE_LOCAL_METHODS = exports.BridgePublishStreamingEventParamsSchema = exports.BridgeStreamEventSchema = exports.BridgeStreamClearEventSchema = exports.BridgeStreamFinalEventSchema = exports.BridgeStreamThinkingEventSchema = exports.BridgeStreamTextEventSchema = exports.BridgeStreamToolEventSchema = exports.BridgeGetAttachmentResultSchema = exports.BridgeGetAttachmentParamsSchema = exports.BridgeUploadMediaParamsSchema = exports.BridgeHitlStateSchema = exports.BridgeStatusResultSchema = exports.BridgeConnectionStateNotificationSchema = exports.BridgeReadyNotificationSchema = exports.BridgeUpstreamStateSchema = exports.BridgeHelloResultSchema = exports.BRIDGE_EVENT_CURSOR_SHAPE = exports.BridgeResyncSchema = exports.BridgeResyncReasonSchema = exports.BridgeHelloParamsSchema = exports.BridgeOkResultSchema = exports.BRIDGE_LOCAL_PROTOCOL_VERSION = void 0;
4
4
  // Bridge ⇄ runtime-adapter LOCAL JSON-RPC contract v1 (types + schemas ONLY —
5
5
  // no runtime, no transport; Phase 4 builds the bridge against these).
6
6
  //
@@ -156,6 +156,71 @@ const messageScoped = zod_1.z.object({ conversationId: zod_1.z.string(), message
156
156
  */
157
157
  const ServerTimestampSentinelSchema = zod_1.z.object({ '.sv': zod_1.z.literal('timestamp') });
158
158
  const EpochOrServerTimestampSchema = zod_1.z.union([zod_1.z.number(), ServerTimestampSentinelSchema]);
159
+ // ─────────────────────────────────────────────────────────────────────────
160
+ // W2-A4 REGION START — bridge-owned streaming assembly (raw event surface).
161
+ // ADDITIVE: `publishStreamingEvent` lets a channel adapter forward RAW turn
162
+ // events (a text segment begin/append/replace, a tool-trail item, a thinking
163
+ // tick, a final compose, or a clear). The BRIDGE owns snapshot assembly, the
164
+ // single write cadence, thinking keepalive, final compose, and node clear —
165
+ // so adapters stop reimplementing turn-output-controller + a keepalive loop
166
+ // (T1: they also stop swallowing the write failure). The pre-existing
167
+ // snapshot `publishStreaming` (below) is UNTOUCHED — coding hosts still call
168
+ // it via agent-host/runtime-writers.
169
+ // ─────────────────────────────────────────────────────────────────────────
170
+ /** A single tool-trail item forwarded raw; the bridge folds it into `blocks`. */
171
+ exports.BridgeStreamToolEventSchema = zod_1.z.object({
172
+ kind: zod_1.z.literal('tool'),
173
+ /** Stable block id (adapter-scoped, e.g. `tool:{turnId}`). */
174
+ id: zod_1.z.string(),
175
+ status: zod_1.z.enum(['running', 'completed', 'failed']).optional(),
176
+ title: zod_1.z.string().optional(),
177
+ summary: zod_1.z.string().optional(),
178
+ detail: zod_1.z.string().optional(),
179
+ });
180
+ /**
181
+ * A visible-text segment. `mode:'append'` concatenates onto the segment's
182
+ * running text; `mode:'replace'` overwrites it (the adapter already holds the
183
+ * full segment). The bridge rebuilds the bubble text from its text segments in
184
+ * sequence order, exactly as the core turn-output-controller does today.
185
+ */
186
+ exports.BridgeStreamTextEventSchema = zod_1.z.object({
187
+ kind: zod_1.z.literal('text'),
188
+ /** Segment id (adapter-scoped, e.g. `text:{turnId}:{n}`). */
189
+ id: zod_1.z.string(),
190
+ mode: zod_1.z.enum(['append', 'replace']),
191
+ text: zod_1.z.string(),
192
+ });
193
+ /** A thinking tick (onset or keepalive) — no content; keeps the bubble warm. */
194
+ exports.BridgeStreamThinkingEventSchema = zod_1.z.object({
195
+ kind: zod_1.z.literal('thinking'),
196
+ });
197
+ /**
198
+ * Final compose: the assembled reply text is written one last time (status
199
+ * settles to `streaming`) and the node is LEFT in place for the final-message
200
+ * handoff. Clearing is a separate `clear` event / `clearStreaming`.
201
+ */
202
+ exports.BridgeStreamFinalEventSchema = zod_1.z.object({
203
+ kind: zod_1.z.literal('final'),
204
+ text: zod_1.z.string(),
205
+ });
206
+ /** Clear the streaming node (turn ended / interrupted). */
207
+ exports.BridgeStreamClearEventSchema = zod_1.z.object({
208
+ kind: zod_1.z.literal('clear'),
209
+ });
210
+ exports.BridgeStreamEventSchema = zod_1.z.discriminatedUnion('kind', [
211
+ exports.BridgeStreamTextEventSchema,
212
+ exports.BridgeStreamToolEventSchema,
213
+ exports.BridgeStreamThinkingEventSchema,
214
+ exports.BridgeStreamFinalEventSchema,
215
+ exports.BridgeStreamClearEventSchema,
216
+ ]);
217
+ exports.BridgePublishStreamingEventParamsSchema = conversationScoped.extend({
218
+ /** New turnId resets the bridge's accumulation for this conversation. */
219
+ turnId: zod_1.z.string().nullable().optional(),
220
+ messageId: zod_1.z.string().optional(),
221
+ event: exports.BridgeStreamEventSchema,
222
+ });
223
+ // W2-A4 REGION END — bridge-owned streaming assembly.
159
224
  exports.BRIDGE_LOCAL_METHODS = {
160
225
  // §A lifecycle
161
226
  hello: { params: exports.BridgeHelloParamsSchema, result: exports.BridgeHelloResultSchema },
@@ -308,6 +373,12 @@ exports.BRIDGE_LOCAL_METHODS = {
308
373
  result: OkResultSchema,
309
374
  },
310
375
  clearStreaming: { params: conversationScoped, result: OkResultSchema },
376
+ // W2-A4 additive: raw streaming events (bridge owns assembly/cadence/
377
+ // keepalive/final/clear). Coexists with the snapshot publishStreaming above.
378
+ publishStreamingEvent: {
379
+ params: exports.BridgePublishStreamingEventParamsSchema,
380
+ result: OkResultSchema,
381
+ },
311
382
  // §B6 runtime presentation (adapter owns content, bridge publishes)
312
383
  publishRuntimeDescriptor: {
313
384
  params: zod_1.z.object({
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.UploadMediaResponseSchema = exports.UploadMediaRequestSchema = exports.SetStreamingRequestSchema = exports.SetTypingRequestSchema = exports.ResolveAdmissionResponseSchema = exports.ResolveAdmissionRequestSchema = exports.ContactsResponseSchema = exports.CreateContactRequestResponseSchema = exports.CreateContactRequestRequestSchema = exports.CreateConversationResponseSchema = exports.CreateConversationRequestSchema = exports.MessagesPageResponseSchema = exports.GetMessagesQuerySchema = exports.ConversationsResponseSchema = exports.UpdateDispositionRequestSchema = exports.MarkReadResponseSchema = exports.DeleteMessageResponseSchema = exports.ForwardMessageResponseSchema = exports.ForwardMessageRequestSchema = exports.ReactResponseSchema = exports.ReactRequestSchema = exports.SendMessageResponseSchema = exports.SendMessageRequestSchema = exports.RestSessionSelectionSchema = exports.RestContactSchema = exports.RestConversationSchema = exports.RestMessageSchema = exports.RestOkResponseSchema = void 0;
3
+ exports.UploadMediaResponseSchema = exports.UploadMediaRequestSchema = exports.SetStreamingRequestSchema = exports.SetTypingRequestSchema = exports.ResolveAdmissionResponseSchema = exports.ResolveAdmissionRequestSchema = exports.ContactsResponseSchema = exports.CreateContactRequestResponseSchema = exports.CreateContactRequestRequestSchema = exports.CreateConversationResponseSchema = exports.CreateConversationRequestSchema = exports.MessagesPageResponseSchema = exports.GetMessagesQuerySchema = exports.ConversationsResponseSchema = exports.UpdateDispositionRequestSchema = exports.MarkReadResponseSchema = exports.DeleteMessageResponseSchema = exports.ForwardMessageResponseSchema = exports.ForwardMessageRequestSchema = exports.ReactResponseSchema = exports.ReactRequestSchema = exports.SendMessageResponseSchema = exports.SendMessageRequestSchema = exports.RestSessionSelectionSchema = exports.RestContactSchema = exports.RestConversationSchema = exports.RestConversationLastMessageSchema = exports.RestMessageSchema = exports.RestOkResponseSchema = void 0;
4
4
  // Wire contract v1 — request/response schemas for the agent-facing "Canon op"
5
5
  // REST surface the bridge proxies (bridge-local-api.md §B).
6
6
  //
@@ -20,6 +20,30 @@ exports.RestOkResponseSchema = zod_1.z.looseObject({
20
20
  exports.RestMessageSchema = message_js_1.SerializedMessageSchema.extend({
21
21
  deleted: zod_1.z.boolean().optional(),
22
22
  });
23
+ /**
24
+ * STRICT WRITES, TOLERANT READS: this is a READ schema over live production
25
+ * data, so it must describe what Firestore actually holds — not what today's
26
+ * writers produce. The GET /conversations serializer
27
+ * (functions/src/api/getConversations.ts) passes `lastMessage` fields through
28
+ * from the conversation doc, and legacy rows (pre-refactor writes,
29
+ * deleted-message repair) hold explicit nulls:
30
+ * - `text` is serialized `?? null` and `timestamp` as
31
+ * `?.toDate?.()?.toISOString() ?? null` — both emit null by construction.
32
+ * - `senderId` / `senderType` / `contentType` are raw pass-throughs; live
33
+ * rows exist with all three null (the getConversations "ghost fix" comment
34
+ * acknowledges null senderId).
35
+ * - conversation `createdAt` is also serialized `?? null`.
36
+ * Writers (onMessageCreated / deletedMessageRepair) stay strict — they always
37
+ * produce the full shape; only the read contract admits the legacy nulls.
38
+ */
39
+ exports.RestConversationLastMessageSchema = zod_1.z.looseObject({
40
+ text: zod_1.z.string().nullable(),
41
+ messageId: zod_1.z.string().optional(),
42
+ senderId: zod_1.z.string().nullable().optional(),
43
+ senderType: zod_1.z.enum(['human', 'ai_agent']).nullable().optional(),
44
+ contentType: message_js_1.SerializedContentTypeSchema.nullable().optional(),
45
+ timestamp: zod_1.z.string().nullable(),
46
+ });
23
47
  exports.RestConversationSchema = zod_1.z.looseObject({
24
48
  id: zod_1.z.string(),
25
49
  type: zod_1.z.enum(['direct', 'group']),
@@ -30,15 +54,8 @@ exports.RestConversationSchema = zod_1.z.looseObject({
30
54
  /** Resolved agent behavior policy — opaque pass-through. */
31
55
  behavior: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
32
56
  hasUnread: zod_1.z.boolean().optional(),
33
- lastMessage: zod_1.z.looseObject({
34
- text: zod_1.z.string(),
35
- messageId: zod_1.z.string().optional(),
36
- senderId: zod_1.z.string(),
37
- senderType: zod_1.z.enum(['human', 'ai_agent']),
38
- contentType: message_js_1.SerializedContentTypeSchema.optional(),
39
- timestamp: zod_1.z.string(),
40
- }).nullable(),
41
- createdAt: zod_1.z.string().optional(),
57
+ lastMessage: exports.RestConversationLastMessageSchema.nullable(),
58
+ createdAt: zod_1.z.string().nullable().optional(),
42
59
  });
43
60
  exports.RestContactSchema = zod_1.z.object({
44
61
  id: zod_1.z.string(),
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SseEventEnvelopeSchema = exports.SSE_EVENT_FAMILY_BY_TYPE = exports.SseEventFamilySchema = exports.SSE_EVENT_FAMILIES = exports.SSE_EVENT_PAYLOAD_SCHEMAS = exports.SseEventTypeSchema = exports.SSE_EVENT_TYPES = exports.SseVoiceSessionEventPayloadSchema = exports.SseVoiceSessionSchema = exports.SseRuntimeControlPayloadSchema = exports.SseRuntimeControlKindSchema = exports.SseTurnUpdatedPayloadSchema = exports.SseRuntimeUpdatedPayloadSchema = exports.SsePresencePayloadSchema = exports.SseTypingPayloadSchema = exports.SseContactRemovedPayloadSchema = exports.SseContactPayloadSchema = exports.SseContactRequestPayloadSchema = exports.SseConversationUpdatedPayloadSchema = exports.SseMembershipChangeSchema = exports.SseMessageDeletedPayloadSchema = exports.SseMessageUpdatedPayloadSchema = exports.SseMessageCreatedPayloadSchema = exports.SseProvenanceSchema = exports.SseTurnDispatchSchema = exports.SseSelfContextSchema = exports.SseErrorPayloadSchema = exports.SseReplayExpiredPayloadSchema = exports.SseHeartbeatPayloadSchema = exports.SseAgentContextPayloadSchema = exports.SseConnectedPayloadSchema = exports.SseAccessPolicySchema = void 0;
3
+ exports.SseEventEnvelopeSchema = exports.SSE_EVENT_FAMILY_BY_TYPE = exports.SseEventFamilySchema = exports.SSE_EVENT_FAMILIES = exports.SSE_EVENT_PAYLOAD_SCHEMAS = exports.SseEventTypeSchema = exports.SSE_EVENT_TYPES = exports.SseVoiceSessionEventPayloadSchema = exports.SseVoiceSessionSchema = exports.SseRuntimeControlPayloadSchema = exports.SseRuntimeControlKindSchema = exports.SsePresencePayloadSchema = exports.SseTypingPayloadSchema = exports.SseContactRemovedPayloadSchema = exports.SseContactPayloadSchema = exports.SseContactRequestPayloadSchema = exports.SseConversationUpdatedPayloadSchema = exports.SseMembershipChangeSchema = exports.SseMessageDeletedPayloadSchema = exports.SseMessageUpdatedPayloadSchema = exports.SseMessageCreatedPayloadSchema = exports.SseProvenanceSchema = exports.SseTurnDispatchSchema = exports.SseSelfContextSchema = exports.SseErrorPayloadSchema = exports.SseReplayExpiredPayloadSchema = exports.SseHeartbeatPayloadSchema = exports.SseAgentContextPayloadSchema = exports.SseConnectedPayloadSchema = exports.SseAccessPolicySchema = void 0;
4
4
  // Wire contract v1 — SSE event envelope + per-family payload schemas.
5
5
  //
6
6
  // Grounded in what stream-service ACTUALLY emits (stream-service/src/stream.ts
@@ -142,20 +142,9 @@ exports.SsePresencePayloadSchema = zod_1.z.object({
142
142
  lastSeen: zod_1.z.string().optional(),
143
143
  });
144
144
  // ── Runtime / turn family ──────────────────────────────────────────────
145
- /** Aggregated RTDB runtime snapshot — nodes are passed through opaquely. */
146
- exports.SseRuntimeUpdatedPayloadSchema = zod_1.z.object({
147
- conversationId: zod_1.z.string(),
148
- agentId: zod_1.z.string(),
149
- runtime: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable(),
150
- runtimeInfo: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable(),
151
- sessionState: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable(),
152
- sessionConfig: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable(),
153
- });
154
- exports.SseTurnUpdatedPayloadSchema = zod_1.z.object({
155
- conversationId: zod_1.z.string(),
156
- agentId: zod_1.z.string(),
157
- turn: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable(),
158
- });
145
+ // `runtime.updated` / `turn.updated` were retired 2026-07 (zero consumers;
146
+ // the bridge reads RTDB directly). `runtime.control` remains — it is the
147
+ // planned event-push control transport (housecleaning plan §6 W2-A2).
159
148
  exports.SseRuntimeControlKindSchema = zod_1.z.enum([
160
149
  'session',
161
150
  'signal',
@@ -219,8 +208,6 @@ exports.SSE_EVENT_TYPES = [
219
208
  'contact.removed',
220
209
  'typing',
221
210
  'presence',
222
- 'runtime.updated',
223
- 'turn.updated',
224
211
  'runtime.control',
225
212
  'voice.session.started',
226
213
  'voice.session.ended',
@@ -244,8 +231,6 @@ exports.SSE_EVENT_PAYLOAD_SCHEMAS = {
244
231
  'contact.removed': exports.SseContactRemovedPayloadSchema,
245
232
  'typing': exports.SseTypingPayloadSchema,
246
233
  'presence': exports.SsePresencePayloadSchema,
247
- 'runtime.updated': exports.SseRuntimeUpdatedPayloadSchema,
248
- 'turn.updated': exports.SseTurnUpdatedPayloadSchema,
249
234
  'runtime.control': exports.SseRuntimeControlPayloadSchema,
250
235
  'voice.session.started': exports.SseVoiceSessionEventPayloadSchema,
251
236
  'voice.session.ended': exports.SseVoiceSessionEventPayloadSchema,
@@ -279,8 +264,6 @@ exports.SSE_EVENT_FAMILY_BY_TYPE = {
279
264
  'contact.removed': 'contacts',
280
265
  'typing': 'typing_presence',
281
266
  'presence': 'typing_presence',
282
- 'runtime.updated': 'runtime_turn',
283
- 'turn.updated': 'runtime_turn',
284
267
  'runtime.control': 'runtime_turn',
285
268
  'voice.session.started': 'voice',
286
269
  'voice.session.ended': 'voice',
@@ -78,6 +78,41 @@ export declare const RestMessageSchema: z.ZodObject<{
78
78
  deleted: z.ZodOptional<z.ZodBoolean>;
79
79
  }, z.core.$strip>;
80
80
  export type RestMessage = z.infer<typeof RestMessageSchema>;
81
+ /**
82
+ * STRICT WRITES, TOLERANT READS: this is a READ schema over live production
83
+ * data, so it must describe what Firestore actually holds — not what today's
84
+ * writers produce. The GET /conversations serializer
85
+ * (functions/src/api/getConversations.ts) passes `lastMessage` fields through
86
+ * from the conversation doc, and legacy rows (pre-refactor writes,
87
+ * deleted-message repair) hold explicit nulls:
88
+ * - `text` is serialized `?? null` and `timestamp` as
89
+ * `?.toDate?.()?.toISOString() ?? null` — both emit null by construction.
90
+ * - `senderId` / `senderType` / `contentType` are raw pass-throughs; live
91
+ * rows exist with all three null (the getConversations "ghost fix" comment
92
+ * acknowledges null senderId).
93
+ * - conversation `createdAt` is also serialized `?? null`.
94
+ * Writers (onMessageCreated / deletedMessageRepair) stay strict — they always
95
+ * produce the full shape; only the read contract admits the legacy nulls.
96
+ */
97
+ export declare const RestConversationLastMessageSchema: z.ZodObject<{
98
+ text: z.ZodNullable<z.ZodString>;
99
+ messageId: z.ZodOptional<z.ZodString>;
100
+ senderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
101
+ senderType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
102
+ human: "human";
103
+ ai_agent: "ai_agent";
104
+ }>>>;
105
+ contentType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
106
+ file: "file";
107
+ image: "image";
108
+ audio: "audio";
109
+ video: "video";
110
+ text: "text";
111
+ contact_card: "contact_card";
112
+ }>>>;
113
+ timestamp: z.ZodNullable<z.ZodString>;
114
+ }, z.core.$loose>;
115
+ export type RestConversationLastMessage = z.infer<typeof RestConversationLastMessageSchema>;
81
116
  export declare const RestConversationSchema: z.ZodObject<{
82
117
  id: z.ZodString;
83
118
  type: z.ZodEnum<{
@@ -91,24 +126,24 @@ export declare const RestConversationSchema: z.ZodObject<{
91
126
  behavior: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
92
127
  hasUnread: z.ZodOptional<z.ZodBoolean>;
93
128
  lastMessage: z.ZodNullable<z.ZodObject<{
94
- text: z.ZodString;
129
+ text: z.ZodNullable<z.ZodString>;
95
130
  messageId: z.ZodOptional<z.ZodString>;
96
- senderId: z.ZodString;
97
- senderType: z.ZodEnum<{
131
+ senderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
132
+ senderType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
98
133
  human: "human";
99
134
  ai_agent: "ai_agent";
100
- }>;
101
- contentType: z.ZodOptional<z.ZodEnum<{
135
+ }>>>;
136
+ contentType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
102
137
  file: "file";
103
138
  image: "image";
104
139
  audio: "audio";
105
140
  video: "video";
106
141
  text: "text";
107
142
  contact_card: "contact_card";
108
- }>>;
109
- timestamp: z.ZodString;
143
+ }>>>;
144
+ timestamp: z.ZodNullable<z.ZodString>;
110
145
  }, z.core.$loose>>;
111
- createdAt: z.ZodOptional<z.ZodString>;
146
+ createdAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
112
147
  }, z.core.$loose>;
113
148
  export type RestConversation = z.infer<typeof RestConversationSchema>;
114
149
  export declare const RestContactSchema: z.ZodObject<{
@@ -230,24 +265,24 @@ export declare const ConversationsResponseSchema: z.ZodObject<{
230
265
  behavior: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
231
266
  hasUnread: z.ZodOptional<z.ZodBoolean>;
232
267
  lastMessage: z.ZodNullable<z.ZodObject<{
233
- text: z.ZodString;
268
+ text: z.ZodNullable<z.ZodString>;
234
269
  messageId: z.ZodOptional<z.ZodString>;
235
- senderId: z.ZodString;
236
- senderType: z.ZodEnum<{
270
+ senderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
271
+ senderType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
237
272
  human: "human";
238
273
  ai_agent: "ai_agent";
239
- }>;
240
- contentType: z.ZodOptional<z.ZodEnum<{
274
+ }>>>;
275
+ contentType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
241
276
  file: "file";
242
277
  image: "image";
243
278
  audio: "audio";
244
279
  video: "video";
245
280
  text: "text";
246
281
  contact_card: "contact_card";
247
- }>>;
248
- timestamp: z.ZodString;
282
+ }>>>;
283
+ timestamp: z.ZodNullable<z.ZodString>;
249
284
  }, z.core.$loose>>;
250
- createdAt: z.ZodOptional<z.ZodString>;
285
+ createdAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
251
286
  }, z.core.$loose>>;
252
287
  }, z.core.$strip>;
253
288
  export type ConversationsResponse = z.infer<typeof ConversationsResponseSchema>;
@@ -17,6 +17,30 @@ export const RestOkResponseSchema = z.looseObject({
17
17
  export const RestMessageSchema = SerializedMessageSchema.extend({
18
18
  deleted: z.boolean().optional(),
19
19
  });
20
+ /**
21
+ * STRICT WRITES, TOLERANT READS: this is a READ schema over live production
22
+ * data, so it must describe what Firestore actually holds — not what today's
23
+ * writers produce. The GET /conversations serializer
24
+ * (functions/src/api/getConversations.ts) passes `lastMessage` fields through
25
+ * from the conversation doc, and legacy rows (pre-refactor writes,
26
+ * deleted-message repair) hold explicit nulls:
27
+ * - `text` is serialized `?? null` and `timestamp` as
28
+ * `?.toDate?.()?.toISOString() ?? null` — both emit null by construction.
29
+ * - `senderId` / `senderType` / `contentType` are raw pass-throughs; live
30
+ * rows exist with all three null (the getConversations "ghost fix" comment
31
+ * acknowledges null senderId).
32
+ * - conversation `createdAt` is also serialized `?? null`.
33
+ * Writers (onMessageCreated / deletedMessageRepair) stay strict — they always
34
+ * produce the full shape; only the read contract admits the legacy nulls.
35
+ */
36
+ export const RestConversationLastMessageSchema = z.looseObject({
37
+ text: z.string().nullable(),
38
+ messageId: z.string().optional(),
39
+ senderId: z.string().nullable().optional(),
40
+ senderType: z.enum(['human', 'ai_agent']).nullable().optional(),
41
+ contentType: SerializedContentTypeSchema.nullable().optional(),
42
+ timestamp: z.string().nullable(),
43
+ });
20
44
  export const RestConversationSchema = z.looseObject({
21
45
  id: z.string(),
22
46
  type: z.enum(['direct', 'group']),
@@ -27,15 +51,8 @@ export const RestConversationSchema = z.looseObject({
27
51
  /** Resolved agent behavior policy — opaque pass-through. */
28
52
  behavior: z.record(z.string(), z.unknown()).optional(),
29
53
  hasUnread: z.boolean().optional(),
30
- lastMessage: z.looseObject({
31
- text: z.string(),
32
- messageId: z.string().optional(),
33
- senderId: z.string(),
34
- senderType: z.enum(['human', 'ai_agent']),
35
- contentType: SerializedContentTypeSchema.optional(),
36
- timestamp: z.string(),
37
- }).nullable(),
38
- createdAt: z.string().optional(),
54
+ lastMessage: RestConversationLastMessageSchema.nullable(),
55
+ createdAt: z.string().nullable().optional(),
39
56
  });
40
57
  export const RestContactSchema = z.object({
41
58
  id: z.string(),