@beeos-ai/device-agent 0.12.4 → 0.12.5

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 (34) hide show
  1. package/dist/agent-runtime/adapters/base.d.ts +1 -1
  2. package/dist/agent-runtime/adapters/openai.js +3 -1
  3. package/dist/agent-runtime/adapters/openai.js.map +1 -1
  4. package/dist/agent-runtime/core.d.ts +7 -4
  5. package/dist/agent-runtime/core.js +42 -5
  6. package/dist/agent-runtime/core.js.map +1 -1
  7. package/dist/agent-runtime/models/client.js +17 -8
  8. package/dist/agent-runtime/models/client.js.map +1 -1
  9. package/dist/agent-runtime/models/image/compress.d.ts +3 -2
  10. package/dist/agent-runtime/models/image/compress.js +12 -2
  11. package/dist/agent-runtime/models/image/compress.js.map +1 -1
  12. package/dist/agent-runtime/modes/hybrid/index.js +7 -7
  13. package/dist/agent-runtime/modes/hybrid/index.js.map +1 -1
  14. package/dist/agent-runtime/plan-executor.js +1 -1
  15. package/dist/agent-runtime/plan-executor.js.map +1 -1
  16. package/dist/agent-runtime/skills/replay.js +16 -1
  17. package/dist/agent-runtime/skills/replay.js.map +1 -1
  18. package/dist/app/session-manager.d.ts +3 -3
  19. package/dist/app/session-manager.js +18 -20
  20. package/dist/app/session-manager.js.map +1 -1
  21. package/dist/device/apps.d.ts +6 -0
  22. package/dist/device/apps.js +16 -0
  23. package/dist/device/apps.js.map +1 -1
  24. package/dist/messaging/message-client.d.ts +48 -0
  25. package/dist/messaging/message-client.js +132 -4
  26. package/dist/messaging/message-client.js.map +1 -1
  27. package/dist/plugins/message-bus/inbound.js +10 -0
  28. package/dist/plugins/message-bus/inbound.js.map +1 -1
  29. package/dist/plugins/message-bus/ms-channel-notifier.d.ts +26 -14
  30. package/dist/plugins/message-bus/ms-channel-notifier.js +135 -63
  31. package/dist/plugins/message-bus/ms-channel-notifier.js.map +1 -1
  32. package/dist/ports/agent-core.d.ts +5 -8
  33. package/dist/ports/agent-host.d.ts +2 -2
  34. package/package.json +1 -1
@@ -1,7 +1,134 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { EventEmitter } from "node:events";
3
3
  import { createMessageClient, } from "@beeos-ai/message-sdk";
4
- import { createNodeMessageClientComposition } from "@beeos-ai/message-sdk/node";
4
+ import { createNodeMessageClientComposition, } from "@beeos-ai/message-sdk/node";
5
+ /**
6
+ * SDK-compatible stream with an explicit write barrier. The public SDK stream
7
+ * intentionally keeps its mutation queue private; device actions need a
8
+ * stronger guarantee so their action card is persisted before execution.
9
+ */
10
+ export class DrainableMessageReplyStream {
11
+ writer;
12
+ command;
13
+ messageId;
14
+ receipt;
15
+ body = "";
16
+ streamParts = [];
17
+ terminated = false;
18
+ sequence = 0;
19
+ chain;
20
+ openPromise;
21
+ constructor(writer, command) {
22
+ this.writer = writer;
23
+ this.command = command;
24
+ this.messageId = command.clientMessageId;
25
+ this.openPromise = writer.startStream(command).then((receipt) => {
26
+ if (receipt.messageId !== command.clientMessageId) {
27
+ throw new Error("message service must preserve the client-owned stream message id");
28
+ }
29
+ this.messageId = receipt.messageId;
30
+ this.receipt = receipt;
31
+ return receipt;
32
+ });
33
+ this.chain = this.openPromise.then(() => undefined, () => undefined);
34
+ }
35
+ get id() {
36
+ return this.messageId;
37
+ }
38
+ get isTerminated() {
39
+ return this.terminated;
40
+ }
41
+ get parts() {
42
+ return this.streamParts;
43
+ }
44
+ opened() {
45
+ return this.openPromise;
46
+ }
47
+ async flush() {
48
+ await this.openPromise;
49
+ await this.chain;
50
+ }
51
+ appendBody(chunk) {
52
+ this.assertActive();
53
+ if (!chunk)
54
+ return;
55
+ const bodyFrom = new TextEncoder().encode(this.body).byteLength;
56
+ this.body += chunk;
57
+ const key = this.nextKey("append");
58
+ this.enqueue(() => this.writer.append(this.command.conversationId, this.messageId, chunk, bodyFrom, key));
59
+ }
60
+ addPart(part) {
61
+ this.assertActive();
62
+ this.streamParts = [...this.streamParts, part];
63
+ this.flushParts();
64
+ }
65
+ replacePart(index, part) {
66
+ this.assertActive();
67
+ if (!Number.isInteger(index) || index < 0 || index >= this.streamParts.length) {
68
+ throw new Error("message stream part index is out of range");
69
+ }
70
+ const parts = [...this.streamParts];
71
+ parts[index] = part;
72
+ this.streamParts = parts;
73
+ this.flushParts();
74
+ }
75
+ finalize(options) {
76
+ return this.terminate("completed", options?.stopReason);
77
+ }
78
+ fail(options) {
79
+ if (options?.body !== undefined)
80
+ this.setBody(options.body);
81
+ return this.terminate("failed", options?.stopReason ?? "error");
82
+ }
83
+ refuse(options) {
84
+ return this.terminate("refused", options?.stopReason ?? "refused");
85
+ }
86
+ cancel(options) {
87
+ return this.terminate("cancelled", options?.stopReason ?? "user_stop");
88
+ }
89
+ setBody(body) {
90
+ this.assertActive();
91
+ if (body === this.body)
92
+ return;
93
+ if (!this.writer.setBody) {
94
+ throw new Error("message stream writer does not support body snapshots");
95
+ }
96
+ this.body = body;
97
+ const key = this.nextKey("body");
98
+ this.enqueue(() => this.writer.setBody(this.command.conversationId, this.messageId, body, key));
99
+ }
100
+ flushParts() {
101
+ if (!this.writer.setParts) {
102
+ throw new Error("message stream writer does not support parts");
103
+ }
104
+ const parts = [...this.streamParts];
105
+ const key = this.nextKey("parts");
106
+ this.enqueue(() => this.writer.setParts(this.command.conversationId, this.messageId, parts, key));
107
+ }
108
+ enqueue(write) {
109
+ this.chain = this.chain.then(async () => {
110
+ await this.openPromise;
111
+ await write();
112
+ });
113
+ void this.chain.catch(() => undefined);
114
+ }
115
+ async terminate(state, stopReason) {
116
+ this.assertActive();
117
+ this.terminated = true;
118
+ const receipt = await this.openPromise;
119
+ await this.chain;
120
+ await this.writer.finalize(this.command.conversationId, this.messageId, state, this.nextKey(`terminal:${state}`), stopReason);
121
+ return this.receipt ?? receipt;
122
+ }
123
+ nextKey(operation) {
124
+ this.sequence += 1;
125
+ return `${this.command.idempotencyKey}:${operation}:${this.sequence}`;
126
+ }
127
+ assertActive() {
128
+ if (this.terminated)
129
+ throw new Error("message stream is already terminated");
130
+ }
131
+ }
5
132
  /** Device-agent composition for the Message SDK 2.x Node runtime. */
6
133
  export class DeviceMessageClient extends EventEmitter {
7
134
  messages;
@@ -12,11 +139,12 @@ export class DeviceMessageClient extends EventEmitter {
12
139
  lastConnection = "disconnected";
13
140
  constructor(options) {
14
141
  super();
15
- this.client = createMessageClient(createNodeMessageClientComposition({
142
+ const composition = createNodeMessageClientComposition({
16
143
  identityId: options.identityId,
17
144
  serviceUrl: options.serviceUrl,
18
145
  tokenProvider: options.tokenProvider,
19
- }));
146
+ });
147
+ this.client = createMessageClient(composition);
20
148
  this.messages = {
21
149
  send: async (input) => {
22
150
  const messageId = input.id ?? randomUUID();
@@ -31,7 +159,7 @@ export class DeviceMessageClient extends EventEmitter {
31
159
  },
32
160
  startStream: (input) => {
33
161
  const messageId = input.id ?? randomUUID();
34
- return this.client.messages.startStream({
162
+ return new DrainableMessageReplyStream(composition.messageStream, {
35
163
  conversationId: input.conversationId,
36
164
  clientMessageId: messageId,
37
165
  idempotencyKey: messageId,
@@ -1 +1 @@
1
- {"version":3,"file":"message-client.js","sourceRoot":"","sources":["../../src/messaging/message-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message-client.js","sourceRoot":"","sources":["../../src/messaging/message-client.ts"],"names":[],"mappings":"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"}
@@ -94,6 +94,16 @@ export function buildMessageBusInboundHandler(opts) {
94
94
  files: prompt.files.length,
95
95
  }, "message-bus: turn started");
96
96
  try {
97
+ // Match OpenClaw's ordering: establish the durable streaming reply
98
+ // before invoking the model. This prevents the first visible chunks
99
+ // from sitting behind the initial POST in the SDK write queue.
100
+ await notifier.opened().catch((error) => {
101
+ opts.logger.warn({
102
+ channelId: prompt.channelId,
103
+ inboundMessageId: prompt.messageId,
104
+ err: error instanceof Error ? error.message : String(error),
105
+ }, "message-bus: reply stream open probe failed; continuing for terminal fallback");
106
+ });
97
107
  const result = await opts.sessionManager.promptForSession(sid, promptText, extractTurnExecutionOptions(msg.content));
98
108
  opts.logger.info({
99
109
  sid,
@@ -1 +1 @@
1
- {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/inbound.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/plugins/message-bus/inbound.ts"],"names":[],"mappings":"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"}
@@ -73,24 +73,23 @@ export declare class MsChannelNotifier implements SessionUpdateNotifier {
73
73
  private readonly sleep;
74
74
  private readonly stream;
75
75
  private streamFlushTimer;
76
+ private streamDrain;
76
77
  private pendingBodyText;
78
+ private handleQueue;
79
+ /** Current prose segment, committed to parts only at a tool/terminal boundary. */
77
80
  private pendingReplyText;
78
81
  private pendingReasoningText;
79
82
  private reasoningText;
80
83
  private reasoningPartIndex;
81
- private activeTextPart;
82
84
  private nextPartSequence;
83
85
  private readonly toolParts;
84
86
  constructor(opts: MsChannelNotifierOptions);
85
87
  /** Durable reply id targeted by Message Service `chat_cancel` envelopes. */
86
88
  get replyMessageId(): string | undefined;
87
- /**
88
- * Called by SessionManager for every `session/update` envelope on
89
- * this notifier's bound sid. Fire-and-forget: the SessionManager
90
- * event hot path is synchronous, so we kick off the publish in the
91
- * background and never block.
92
- */
93
- send(_sessionId: string, update: Record<string, unknown>): void;
89
+ /** Wait until the v3 reply row exists before starting the model run. */
90
+ opened(): Promise<void>;
91
+ /** Process updates in source order and expose backpressure to SessionManager. */
92
+ send(_sessionId: string, update: Record<string, unknown>): Promise<void>;
94
93
  /**
95
94
  * Cumulative reply text emitted so far. Exposed for diagnostics +
96
95
  * tests; not part of any wire contract.
@@ -111,19 +110,32 @@ export declare class MsChannelNotifier implements SessionUpdateNotifier {
111
110
  private publishFinal;
112
111
  private publishFinalOnce;
113
112
  private scheduleStreamFlush;
114
- private flushStreamDeltas;
113
+ /**
114
+ * Persist at most one small body frame, then wait for Message Service.
115
+ * This is the backpressure boundary that prevents a fast VLM from building
116
+ * an unbounded serialized HTTP queue inside MessageStreamWriter.
117
+ */
118
+ private drainStreamBatch;
119
+ /** Drain every pending frame before a tool/terminal ordering boundary. */
120
+ private drainStreamDeltas;
115
121
  private publishReasoning;
116
122
  /**
117
- * Keep one mutable wire text part per uninterrupted prose segment. Token
118
- * deltas replace that part in place; a tool call closes it so later prose
119
- * receives a new sequence number and can render after the tool card.
123
+ * Freeze one prose segment before a tool card or terminal state. Live text
124
+ * stays on the append-only body wire; Web derives the uncommitted suffix
125
+ * from that body, so token updates do not enqueue an additional setParts
126
+ * request for every flush.
120
127
  */
121
- private publishText;
122
- private finishText;
128
+ private commitTextSegment;
123
129
  private finishReasoning;
124
130
  private publishToolCall;
125
131
  private publishToolUpdate;
126
132
  private appendFilePart;
133
+ /**
134
+ * Produce a harmless prose separator after a parts mutation. Besides making
135
+ * adjacent VLM rounds readable, this creates the canonical message.delta
136
+ * that Message Service uses to fan the latest tool parts out in real time.
137
+ */
138
+ private appendToolBoundaryText;
127
139
  private partSequence;
128
140
  private logTerminalDelivered;
129
141
  private logTerminalFailure;
@@ -49,9 +49,13 @@ const DELTA_MAX_ATTEMPTS = 3;
49
49
  const TERMINAL_MAX_ATTEMPTS = 8;
50
50
  const RETRY_BASE_MS = 500;
51
51
  const RETRY_CAP_MS = 30_000;
52
- // Message SDK serializes every stream mutation over HTTP. Coalesce token-sized
53
- // updates so a terminal/cancel is never stuck behind hundreds of queued writes.
54
- const STREAM_FLUSH_MS = 750;
52
+ // Bound the producer to one Message Service mutation at a time. Phone VLMs can
53
+ // emit many one-character deltas per second while a remote PATCH takes tens or
54
+ // hundreds of milliseconds; queueing every timer tick makes later rounds lag
55
+ // behind the phone and then arrive in bursts.
56
+ const STREAM_FLUSH_MS = 40;
57
+ const STREAM_BODY_CHUNK_CHARS = 12;
58
+ const TOOL_BOUNDARY_TEXT = "\n";
55
59
  const realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
56
60
  export class MsChannelNotifier {
57
61
  opts;
@@ -66,12 +70,14 @@ export class MsChannelNotifier {
66
70
  sleep;
67
71
  stream;
68
72
  streamFlushTimer = null;
73
+ streamDrain = null;
69
74
  pendingBodyText = "";
75
+ handleQueue = Promise.resolve();
76
+ /** Current prose segment, committed to parts only at a tool/terminal boundary. */
70
77
  pendingReplyText = "";
71
78
  pendingReasoningText = "";
72
79
  reasoningText = "";
73
80
  reasoningPartIndex = null;
74
- activeTextPart = null;
75
81
  nextPartSequence = 0;
76
82
  toolParts = new Map();
77
83
  constructor(opts) {
@@ -89,14 +95,15 @@ export class MsChannelNotifier {
89
95
  get replyMessageId() {
90
96
  return this.stream?.id;
91
97
  }
92
- /**
93
- * Called by SessionManager for every `session/update` envelope on
94
- * this notifier's bound sid. Fire-and-forget: the SessionManager
95
- * event hot path is synchronous, so we kick off the publish in the
96
- * background and never block.
97
- */
98
+ /** Wait until the v3 reply row exists before starting the model run. */
99
+ async opened() {
100
+ await this.stream?.opened();
101
+ }
102
+ /** Process updates in source order and expose backpressure to SessionManager. */
98
103
  send(_sessionId, update) {
99
- void this.handle(update);
104
+ const queued = this.handleQueue.then(() => this.handle(update));
105
+ this.handleQueue = queued.catch(() => undefined);
106
+ return queued;
100
107
  }
101
108
  /**
102
109
  * Cumulative reply text emitted so far. Exposed for diagnostics +
@@ -111,6 +118,7 @@ export class MsChannelNotifier {
111
118
  }
112
119
  /** Ensure the durable reply reaches a terminal state before unbinding it. */
113
120
  async finish(stopReason, errorText = "", task = null) {
121
+ await this.handleQueue;
114
122
  await this.publishFinal(stopReason, errorText, task);
115
123
  }
116
124
  async handle(update) {
@@ -137,11 +145,11 @@ export class MsChannelNotifier {
137
145
  return;
138
146
  }
139
147
  case "tool_call": {
140
- this.publishToolCall(update);
148
+ await this.publishToolCall(update);
141
149
  return;
142
150
  }
143
151
  case "tool_call_update": {
144
- this.publishToolUpdate(update);
152
+ await this.publishToolUpdate(update);
145
153
  return;
146
154
  }
147
155
  case "stream_complete": {
@@ -222,8 +230,8 @@ export class MsChannelNotifier {
222
230
  async publishFinalOnce(stopReason, errorText, task) {
223
231
  if (this.stream) {
224
232
  try {
225
- this.flushStreamDeltas();
226
- this.finishText();
233
+ await this.drainStreamDeltas();
234
+ this.commitTextSegment();
227
235
  this.finishReasoning();
228
236
  const taskPayload = taskContent(task);
229
237
  if (Object.keys(taskPayload).length > 0) {
@@ -308,34 +316,84 @@ export class MsChannelNotifier {
308
316
  }
309
317
  }
310
318
  scheduleStreamFlush() {
311
- if (!this.stream || this.streamFlushTimer || this.finalized)
319
+ if (!this.stream
320
+ || this.streamFlushTimer
321
+ || this.streamDrain
322
+ || this.finalized)
312
323
  return;
313
324
  this.streamFlushTimer = setTimeout(() => {
314
325
  this.streamFlushTimer = null;
315
326
  if (!this.finalized && !this.stream?.isTerminated) {
316
- this.flushStreamDeltas();
327
+ void this.drainStreamBatch().catch((err) => {
328
+ this.opts.logger.error({
329
+ ...describeError(err),
330
+ channelId: this.opts.channelId,
331
+ }, "ms-channel-notifier: live stream drain failed");
332
+ });
317
333
  }
318
334
  }, STREAM_FLUSH_MS);
319
335
  }
320
- flushStreamDeltas() {
336
+ /**
337
+ * Persist at most one small body frame, then wait for Message Service.
338
+ * This is the backpressure boundary that prevents a fast VLM from building
339
+ * an unbounded serialized HTTP queue inside MessageStreamWriter.
340
+ */
341
+ async drainStreamBatch() {
342
+ if (!this.stream || this.streamDrain) {
343
+ await this.streamDrain;
344
+ return;
345
+ }
346
+ if (this.streamFlushTimer) {
347
+ clearTimeout(this.streamFlushTimer);
348
+ this.streamFlushTimer = null;
349
+ }
350
+ const run = (async () => {
351
+ const reasoning = this.pendingReasoningText;
352
+ const { head: body, tail } = takeStreamBodyChunk(this.pendingBodyText, STREAM_BODY_CHUNK_CHARS);
353
+ this.pendingReasoningText = "";
354
+ this.pendingBodyText = tail;
355
+ if (reasoning)
356
+ this.publishReasoning(reasoning);
357
+ if (body)
358
+ this.stream?.appendBody(body);
359
+ if (reasoning || body)
360
+ await this.stream?.flush();
361
+ })();
362
+ this.streamDrain = run;
363
+ let succeeded = false;
364
+ try {
365
+ await run;
366
+ succeeded = true;
367
+ }
368
+ finally {
369
+ this.streamDrain = null;
370
+ if (succeeded && (this.pendingReasoningText || this.pendingBodyText)) {
371
+ this.scheduleStreamFlush();
372
+ }
373
+ }
374
+ }
375
+ /** Drain every pending frame before a tool/terminal ordering boundary. */
376
+ async drainStreamDeltas() {
321
377
  if (!this.stream)
322
378
  return;
323
379
  if (this.streamFlushTimer) {
324
380
  clearTimeout(this.streamFlushTimer);
325
381
  this.streamFlushTimer = null;
326
382
  }
327
- const reasoning = this.pendingReasoningText;
328
- const body = this.pendingBodyText;
329
- const reply = this.pendingReplyText;
330
- this.pendingReasoningText = "";
331
- this.pendingBodyText = "";
332
- this.pendingReplyText = "";
333
- if (reasoning)
334
- this.publishReasoning(reasoning);
335
- if (body)
336
- this.stream.appendBody(body);
337
- if (reply)
338
- this.publishText(reply);
383
+ while (this.streamDrain
384
+ || this.pendingReasoningText.length > 0
385
+ || this.pendingBodyText.length > 0) {
386
+ if (this.streamDrain) {
387
+ await this.streamDrain;
388
+ }
389
+ else {
390
+ await this.drainStreamBatch();
391
+ }
392
+ if (this.streamFlushTimer) {
393
+ clearTimeout(this.streamFlushTimer);
394
+ this.streamFlushTimer = null;
395
+ }
396
+ }
339
397
  }
340
398
  publishReasoning(text) {
341
399
  if (!this.stream || !text)
@@ -356,38 +414,21 @@ export class MsChannelNotifier {
356
414
  }
357
415
  }
358
416
  /**
359
- * Keep one mutable wire text part per uninterrupted prose segment. Token
360
- * deltas replace that part in place; a tool call closes it so later prose
361
- * receives a new sequence number and can render after the tool card.
417
+ * Freeze one prose segment before a tool card or terminal state. Live text
418
+ * stays on the append-only body wire; Web derives the uncommitted suffix
419
+ * from that body, so token updates do not enqueue an additional setParts
420
+ * request for every flush.
362
421
  */
363
- publishText(text) {
364
- if (!this.stream || !text)
365
- return;
366
- if (this.activeTextPart === null) {
367
- const seq = this.partSequence();
368
- const index = this.stream.parts.length;
369
- this.activeTextPart = { index, seq, text };
370
- this.stream.addPart({ type: "text", text, state: "streaming", _seq: seq });
422
+ commitTextSegment() {
423
+ if (!this.stream || !this.pendingReplyText)
371
424
  return;
372
- }
373
- this.activeTextPart.text += text;
374
- this.stream.replacePart(this.activeTextPart.index, {
375
- type: "text",
376
- text: this.activeTextPart.text,
377
- state: "streaming",
378
- _seq: this.activeTextPart.seq,
379
- });
380
- }
381
- finishText() {
382
- if (!this.stream || this.activeTextPart === null)
383
- return;
384
- this.stream.replacePart(this.activeTextPart.index, {
425
+ this.stream.addPart({
385
426
  type: "text",
386
- text: this.activeTextPart.text,
427
+ text: this.pendingReplyText,
387
428
  state: "done",
388
- _seq: this.activeTextPart.seq,
429
+ _seq: this.partSequence(),
389
430
  });
390
- this.activeTextPart = null;
431
+ this.pendingReplyText = "";
391
432
  }
392
433
  finishReasoning() {
393
434
  if (!this.stream || this.reasoningPartIndex === null)
@@ -399,14 +440,14 @@ export class MsChannelNotifier {
399
440
  _seq: this.reasoningPartIndex,
400
441
  });
401
442
  }
402
- publishToolCall(update) {
443
+ async publishToolCall(update) {
403
444
  if (!this.stream)
404
445
  return;
405
446
  const id = pickString(update.toolCallId);
406
447
  if (!id || this.toolParts.has(id))
407
448
  return;
408
- this.flushStreamDeltas();
409
- this.finishText();
449
+ await this.drainStreamDeltas();
450
+ this.commitTextSegment();
410
451
  const name = pickString(update.title) || "device_action";
411
452
  const args = parseJsonValue(update.arguments);
412
453
  const useIndex = this.stream.parts.length;
@@ -419,8 +460,15 @@ export class MsChannelNotifier {
419
460
  _seq: this.partSequence(),
420
461
  });
421
462
  this.toolParts.set(id, { useIndex, name, args });
422
- }
423
- publishToolUpdate(update) {
463
+ this.appendToolBoundaryText();
464
+ // SessionManager awaits notifier.send(). Make that boundary mean the
465
+ // narration and action card reached Message Service before MCP touches
466
+ // the phone. Message Service intentionally does not broadcast parts-only
467
+ // PATCHes, so the trailing text boundary supplies the delta event that
468
+ // carries the newly persisted parts to Web/Mobile observers.
469
+ await this.stream.flush();
470
+ }
471
+ async publishToolUpdate(update) {
424
472
  if (!this.stream)
425
473
  return;
426
474
  const id = pickString(update.toolCallId);
@@ -428,7 +476,7 @@ export class MsChannelNotifier {
428
476
  return;
429
477
  let tool = this.toolParts.get(id);
430
478
  if (!tool) {
431
- this.publishToolCall({
479
+ await this.publishToolCall({
432
480
  ...update,
433
481
  title: pickString(update.title) || "device_action",
434
482
  arguments: update.arguments ?? "{}",
@@ -463,6 +511,8 @@ export class MsChannelNotifier {
463
511
  else {
464
512
  this.stream.replacePart(tool.progressIndex, result);
465
513
  }
514
+ this.appendToolBoundaryText();
515
+ await this.stream.flush();
466
516
  return;
467
517
  }
468
518
  if (update.output === undefined)
@@ -481,6 +531,7 @@ export class MsChannelNotifier {
481
531
  else {
482
532
  this.stream.replacePart(tool.progressIndex, progress);
483
533
  }
534
+ await this.stream.flush();
484
535
  }
485
536
  appendFilePart(file) {
486
537
  if (!this.stream)
@@ -493,6 +544,18 @@ export class MsChannelNotifier {
493
544
  _seq: this.partSequence(),
494
545
  });
495
546
  }
547
+ /**
548
+ * Produce a harmless prose separator after a parts mutation. Besides making
549
+ * adjacent VLM rounds readable, this creates the canonical message.delta
550
+ * that Message Service uses to fan the latest tool parts out in real time.
551
+ */
552
+ appendToolBoundaryText() {
553
+ if (!this.stream)
554
+ return;
555
+ this.accumulatedText += TOOL_BOUNDARY_TEXT;
556
+ this.pendingReplyText += TOOL_BOUNDARY_TEXT;
557
+ this.stream.appendBody(TOOL_BOUNDARY_TEXT);
558
+ }
496
559
  partSequence() {
497
560
  return this.nextPartSequence++;
498
561
  }
@@ -556,6 +619,15 @@ export class MsChannelNotifier {
556
619
  }
557
620
  }
558
621
  }
622
+ function takeStreamBodyChunk(body, maxChars) {
623
+ if (!body)
624
+ return { head: "", tail: "" };
625
+ const codePoints = Array.from(body);
626
+ return {
627
+ head: codePoints.slice(0, maxChars).join(""),
628
+ tail: codePoints.slice(maxChars).join(""),
629
+ };
630
+ }
559
631
  function pickTaskOutcome(value) {
560
632
  if (!value || typeof value !== "object")
561
633
  return null;