@contextableai/clawg-ui 0.2.3 → 0.2.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.4 (2026-02-06)
4
+
5
+ ### Changed
6
+ - Separate tool call events and text message events into distinct AG-UI runs — when text follows a tool call, the tool run is finished and a new run (with a unique runId) is started for the text messages
7
+
3
8
  ## 0.2.3 (2026-02-06)
4
9
 
5
10
  ### Fixed
package/index.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  popToolCallId,
14
14
  isClientTool,
15
15
  setClientToolCalled,
16
+ setToolFiredInRun,
16
17
  } from "./src/tool-store.js";
17
18
 
18
19
  const plugin = {
@@ -50,6 +51,7 @@ const plugin = {
50
51
  toolCallId,
51
52
  toolCallName: event.toolName,
52
53
  });
54
+ setToolFiredInRun(sk);
53
55
  if (event.params && Object.keys(event.params).length > 0) {
54
56
  console.log(`[clawg-ui] before_tool_call: emitting TOOL_CALL_ARGS, params=${JSON.stringify(event.params)}`);
55
57
  writer({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contextableai/clawg-ui",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "AG-UI protocol channel plugin for OpenClaw — connect CopilotKit and AG-UI clients to your OpenClaw gateway",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -12,6 +12,8 @@ import {
12
12
  wasClientToolCalled,
13
13
  clearClientToolCalled,
14
14
  clearClientToolNames,
15
+ wasToolFiredInRun,
16
+ clearToolFiredInRun,
15
17
  } from "./tool-store.js";
16
18
  import { aguiChannelPlugin } from "./channel.js";
17
19
 
@@ -384,6 +386,31 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
384
386
  }
385
387
  };
386
388
 
389
+ // If a tool call was emitted in the current run, finish that run and start
390
+ // a fresh one for text messages. This keeps tool events and text events in
391
+ // separate runs per the AG-UI protocol.
392
+ const splitRunIfToolFired = () => {
393
+ if (!wasToolFiredInRun(sessionKey)) {
394
+ return;
395
+ }
396
+ // End the tool run
397
+ writeEvent({
398
+ type: EventType.RUN_FINISHED,
399
+ threadId,
400
+ runId: currentRunId,
401
+ });
402
+ // Start a new run for text messages
403
+ currentRunId = `clawg-ui-run-${randomUUID()}`;
404
+ currentMessageId = `msg-${randomUUID()}`;
405
+ messageStarted = false;
406
+ clearToolFiredInRun(sessionKey);
407
+ writeEvent({
408
+ type: EventType.RUN_STARTED,
409
+ threadId,
410
+ runId: currentRunId,
411
+ });
412
+ };
413
+
387
414
  // Handle client disconnect
388
415
  req.on("close", () => {
389
416
  closed = true;
@@ -475,6 +502,8 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
475
502
  return false;
476
503
  }
477
504
 
505
+ splitRunIfToolFired();
506
+
478
507
  if (!messageStarted) {
479
508
  messageStarted = true;
480
509
  writeEvent({
@@ -501,6 +530,8 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
501
530
  const text = wasClientToolCalled(sessionKey) ? "" : payload.text?.trim();
502
531
 
503
532
  if (text) {
533
+ splitRunIfToolFired();
534
+
504
535
  if (!messageStarted) {
505
536
  messageStarted = true;
506
537
  writeEvent({
@@ -586,6 +617,7 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
586
617
  clearWriter(sessionKey);
587
618
  clearClientToolCalled(sessionKey);
588
619
  clearClientToolNames(sessionKey);
620
+ clearToolFiredInRun(sessionKey);
589
621
  }
590
622
  };
591
623
  }
package/src/tool-store.ts CHANGED
@@ -108,6 +108,26 @@ export function clearClientToolNames(sessionKey: string): void {
108
108
  clientToolNames.delete(sessionKey);
109
109
  }
110
110
 
111
+ // --- Tool-fired-in-run flag ---
112
+ // Tracks whether any tool call (server or client) was emitted in the current
113
+ // run. When a text message is about to be emitted and this flag is set, the
114
+ // http-handler splits into a new run so tool events and text events live in
115
+ // separate runs (per AG-UI protocol best practice).
116
+
117
+ const toolFiredInRunFlags = new Map<string, boolean>();
118
+
119
+ export function setToolFiredInRun(sessionKey: string): void {
120
+ toolFiredInRunFlags.set(sessionKey, true);
121
+ }
122
+
123
+ export function wasToolFiredInRun(sessionKey: string): boolean {
124
+ return toolFiredInRunFlags.get(sessionKey) ?? false;
125
+ }
126
+
127
+ export function clearToolFiredInRun(sessionKey: string): void {
128
+ toolFiredInRunFlags.delete(sessionKey);
129
+ }
130
+
111
131
  // --- Client-tool-called flag ---
112
132
  // Set when a client tool is invoked during a run so the dispatcher can
113
133
  // suppress text output and end the run after the tool call events.