@dbx-tools/ui-mastra 0.3.11 → 0.3.12

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.
package/README.md CHANGED
@@ -28,8 +28,9 @@ Key features:
28
28
  - Concurrent threads: run several conversations at once, switch between them
29
29
  while each keeps streaming, and cancel any one independently (per-thread abort
30
30
  + routing, no shared client state).
31
- - Mid-turn steering: submit a message while a turn streams to fold it into the
32
- live run (Mastra queue-message), with an immediate interrupt-and-resend option.
31
+ - Mid-turn steering: submit a message while a turn streams to "send now" - it
32
+ interrupts the in-flight run and starts a fresh turn with your message
33
+ immediately.
33
34
  - Export menu for PDF and Markdown, resolving charts and tables so
34
35
  exported conversations remain useful offline.
35
36
 
@@ -50,8 +51,8 @@ understand Mastra-specific behavior:
50
51
  - `[chart:<id>]` and `[data:<id>]` assistant markers rendered as ECharts charts
51
52
  and sortable tables.
52
53
  - Concurrent multi-thread streaming, per-thread cancel, and mid-turn steering
53
- (queue-message with interrupt fallback) - the native AppKit chat surface runs
54
- one turn at a time and has no steering.
54
+ ("send now" interrupts the live run and restarts with your message) - the
55
+ native AppKit chat surface runs one turn at a time and has no steering.
55
56
  - Conversation export that resolves those embeds into Markdown or PDF.
56
57
 
57
58
  ## Add The Styles
package/package.json CHANGED
@@ -26,19 +26,19 @@
26
26
  "shiki": "^3.0.0",
27
27
  "sql-formatter": "^15.6.9",
28
28
  "streamdown": "^2.5.0",
29
- "@dbx-tools/shared-core": "0.3.11",
30
- "@dbx-tools/shared-genie": "0.3.11",
31
- "@dbx-tools/shared-mastra": "0.3.11",
32
- "@dbx-tools/shared-model": "0.3.11",
33
- "@dbx-tools/ui-appkit": "0.3.11",
34
- "@dbx-tools/ui-branding": "0.3.11"
29
+ "@dbx-tools/shared-core": "0.3.12",
30
+ "@dbx-tools/shared-model": "0.3.12",
31
+ "@dbx-tools/ui-appkit": "0.3.12",
32
+ "@dbx-tools/shared-mastra": "0.3.12",
33
+ "@dbx-tools/ui-branding": "0.3.12",
34
+ "@dbx-tools/shared-genie": "0.3.12"
35
35
  },
36
36
  "main": "index.ts",
37
37
  "license": "UNLICENSED",
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
- "version": "0.3.11",
41
+ "version": "0.3.12",
42
42
  "types": "index.ts",
43
43
  "type": "module",
44
44
  "exports": {
@@ -35,7 +35,6 @@ import {
35
35
  import { error as errorUtil } from "@dbx-tools/shared-core";
36
36
  import {
37
37
  ArrowDownIcon,
38
- FastForwardIcon,
39
38
  MessageSquareIcon,
40
39
  PanelLeftIcon,
41
40
  RefreshCwIcon,
@@ -101,7 +100,6 @@ export const ChatView = ({
101
100
  status,
102
101
  error,
103
102
  sendMessage,
104
- onInterrupt,
105
103
  regenerate,
106
104
  onStop,
107
105
  className,
@@ -278,16 +276,6 @@ export const ChatView = ({
278
276
  });
279
277
  };
280
278
 
281
- // Interrupt-submit: force an immediate course-correction (abort the current
282
- // run, resend with this text) rather than queuing to the live turn.
283
- const handleInterrupt = () => {
284
- const text = input.trim();
285
- if (!text || !onInterrupt) return;
286
- onInterrupt({ text });
287
- setInput("");
288
- setIsAtBottom(true);
289
- pinToBottom();
290
- };
291
279
 
292
280
  const lastMessage = messages.at(-1);
293
281
  const lastEvents = lastMessage ? toolEventsByMessage[lastMessage.id] : undefined;
@@ -665,38 +653,17 @@ export const ChatView = ({
665
653
  ) : (
666
654
  <>
667
655
  {/*
668
- * Running with typed text: offer an immediate interrupt
669
- * alongside the steer Send. Interrupt aborts the current
670
- * turn and resends now; Send folds the message into the
671
- * live turn (queue-steer).
672
- */}
673
- {isRunning && onInterrupt && input.trim() && (
674
- <Tooltip>
675
- <TooltipTrigger asChild>
676
- <InputGroupButton
677
- type="button"
678
- size="icon-sm"
679
- variant="outline"
680
- onClick={handleInterrupt}
681
- aria-label="Interrupt and send now"
682
- >
683
- <FastForwardIcon className="size-3" />
684
- </InputGroupButton>
685
- </TooltipTrigger>
686
- <TooltipContent>Interrupt &amp; send now</TooltipContent>
687
- </Tooltip>
688
- )}
689
- {/*
690
- * Idle, or running with typed text: the primary button
691
- * sends. A send mid-run steers the live turn (see the
692
- * driver's sendMessage).
656
+ * The primary button sends. Submitting while a turn is
657
+ * running is a "send now": it interrupts the live run and
658
+ * starts a fresh turn with this message immediately (see
659
+ * the driver's sendMessage). Idle, it just sends.
693
660
  */}
694
661
  <InputGroupButton
695
662
  type="submit"
696
663
  size="icon-sm"
697
664
  variant="default"
698
665
  disabled={!input.trim()}
699
- aria-label={isRunning ? "Steer response" : "Send message"}
666
+ aria-label={isRunning ? "Send now (interrupts)" : "Send message"}
700
667
  >
701
668
  <SendIcon className="size-3" />
702
669
  </InputGroupButton>
@@ -901,52 +901,23 @@ export const useMastraChat = (
901
901
  [activeThreadId, getSession, noteThreadActivity, updateSession, writeMessages],
902
902
  );
903
903
 
904
+ // Send a message, steering the active thread. Submitting while a turn is
905
+ // already streaming is a "send now": it interrupts the live run and starts a
906
+ // fresh turn that includes the new message, immediately. `runStream` /
907
+ // `driveStream` supersede the prior run (abort + runToken bump + settle any
908
+ // running tool pills), so the interrupted turn stops cleanly and the new one
909
+ // takes over. An idle submit just starts a turn.
904
910
  const sendMessage = useCallback<ChatViewProps["sendMessage"]>(
905
911
  (message) => {
906
912
  const text = message.text ?? "";
907
913
  if (!text) return;
908
914
  const threadId = activeKey;
909
915
  const { before, next } = appendUserMessage(threadId, text);
910
- // Steering: if a turn is already streaming on this thread, hand the
911
- // message to the live run so the agent folds it into the current turn
912
- // (no restart). If the server won't deliver it, fall back to
913
- // interrupting + resending so a steer never silently drops.
914
- if (isSessionRunning(before) && before.runId) {
915
- const runId = before.runId;
916
- const steerThreadId =
917
- threadId === DEFAULT_THREAD_SESSION_KEY ? undefined : threadId;
918
- void mastraClient
919
- .queueMessage({ agentId, runId, threadId: steerThreadId, message: text })
920
- .then((accepted) => {
921
- if (accepted) {
922
- logger.info("steer:queued", { runId });
923
- return;
924
- }
925
- logger.info("steer:fallback-interrupt", { runId });
926
- void runStream(threadId, getSession(threadId).messages);
927
- });
928
- return;
916
+ if (isSessionRunning(before)) {
917
+ logger.info("steer:interrupt", { threadId });
929
918
  }
930
919
  void runStream(threadId, next);
931
920
  },
932
- [appendUserMessage, runStream, activeKey, getSession, mastraClient, agentId],
933
- );
934
-
935
- // Interrupt-and-resend: unconditionally abort any in-flight run on the
936
- // active thread and start a fresh turn with `text` included immediately.
937
- // The composer offers this alongside the queue-steer send so the user can
938
- // force an immediate course-correction rather than waiting for the current
939
- // turn to fold in a queued message. `runStream`/`driveStream` supersede the
940
- // prior run (abort + runToken bump + pill cleanup).
941
- const interrupt = useCallback(
942
- (message: { text?: string }) => {
943
- const text = message.text ?? "";
944
- if (!text) return;
945
- const threadId = activeKey;
946
- const { next } = appendUserMessage(threadId, text);
947
- logger.info("steer:interrupt", { threadId });
948
- void runStream(threadId, next);
949
- },
950
921
  [appendUserMessage, runStream, activeKey],
951
922
  );
952
923
 
@@ -1412,7 +1383,6 @@ export const useMastraChat = (
1412
1383
  status: activeSession.status,
1413
1384
  error: activeSession.error,
1414
1385
  sendMessage,
1415
- onInterrupt: interrupt,
1416
1386
  regenerate,
1417
1387
  onStop: stop,
1418
1388
  suggestions,
@@ -89,16 +89,12 @@ export type ChatViewProps = {
89
89
  * a host driving `ChatView` itself supplies its own.
90
90
  */
91
91
  error?: Error | null;
92
- sendMessage: (message: { text: string }) => void;
93
92
  /**
94
- * Send a message that interrupts the in-flight turn immediately: abort the
95
- * current run and start a fresh turn with this message. When provided and
96
- * the chat is running, the composer shows a small "interrupt" action next
97
- * to the queue-steer Send, so the user can force an immediate
98
- * course-correction instead of folding the message into the live turn.
99
- * Omit to offer only the queue-steer send while running.
93
+ * Send a message on the active thread. Submitting while a turn is streaming
94
+ * is a "send now": it interrupts the in-flight run and starts a fresh turn
95
+ * including this message immediately.
100
96
  */
101
- onInterrupt?: (message: { text: string }) => void;
97
+ sendMessage: (message: { text: string }) => void;
102
98
  regenerate?: () => void;
103
99
  /**
104
100
  * Abort the in-flight response. When provided and the chat is running
@@ -123,50 +123,6 @@ export class MastraPluginClient extends MastraClient {
123
123
  return asMastraStreamResponse(response);
124
124
  }
125
125
 
126
- /**
127
- * Steer a live run by handing it another message mid-turn ("queue"), via
128
- * the agent `/queue-message` route. `behavior` controls what happens when
129
- * the run is active: `deliver` (default) folds the message into the current
130
- * turn so the agent picks it up without restarting; `persist` holds it for
131
- * the next turn; `discard` drops it. Routed per call (own thread header, no
132
- * shared client state) like {@link streamAgent}.
133
- *
134
- * Resolves `true` when the server accepted the message for delivery, `false`
135
- * otherwise (unknown run / route unsupported / non-2xx) - never throws for a
136
- * rejected steer, so the caller can fall back to interrupting + resending.
137
- * Queue / signal routes are `@experimental` in Mastra.
138
- */
139
- async queueMessage(params: {
140
- agentId: string;
141
- runId: string;
142
- threadId?: string;
143
- message: string;
144
- behavior?: "deliver" | "persist" | "discard";
145
- }): Promise<boolean> {
146
- const url = `${this.basePath}/agents/${encodeURIComponent(params.agentId)}/queue-message`;
147
- try {
148
- const response = await fetch(url, {
149
- method: "POST",
150
- credentials: "include",
151
- headers: {
152
- "Content-Type": "application/json",
153
- ...this.#routingHeaders({ threadId: params.threadId }),
154
- },
155
- body: JSON.stringify({
156
- runId: params.runId,
157
- ...(params.threadId ? { threadId: params.threadId } : {}),
158
- message: params.message,
159
- ifActive: { behavior: params.behavior ?? "deliver" },
160
- }),
161
- });
162
- if (!response.ok) return false;
163
- const body = (await response.json().catch(() => null)) as { accepted?: boolean } | null;
164
- return body?.accepted === true;
165
- } catch {
166
- return false;
167
- }
168
- }
169
-
170
126
  /**
171
127
  * Resume a suspended `requireApproval` tool via `approve-tool-call`.
172
128
  * Reads SSE directly instead of `agent.approveToolCall()` so the