@contextableai/clawg-ui 0.2.2 → 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 +15 -0
- package/index.ts +2 -0
- package/package.json +6 -2
- package/src/http-handler.ts +56 -12
- package/src/tool-store.ts +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
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
|
+
|
|
8
|
+
## 0.2.3 (2026-02-06)
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Append `\n\n` paragraph joiner to streamed text deltas so chunks render with proper spacing
|
|
12
|
+
- Include `runId` in all `TEXT_MESSAGE_START`, `TEXT_MESSAGE_CONTENT`, and `TEXT_MESSAGE_END` events for AG-UI protocol compliance
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Set channel defaults to `blockStreaming: true` and `chunkMode: "newline"` for correct paragraph-based streaming out of the box
|
|
16
|
+
- Clean up multi-run logic for tool-call-then-text flows (single run per request)
|
|
17
|
+
|
|
3
18
|
## 0.2.2 (2026-02-05)
|
|
4
19
|
|
|
5
20
|
### 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
|
+
"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",
|
|
@@ -39,7 +39,11 @@
|
|
|
39
39
|
"docsPath": "/channels/clawg-ui",
|
|
40
40
|
"docsLabel": "clawg-ui",
|
|
41
41
|
"blurb": "AG-UI protocol endpoint for CopilotKit and HttpAgent clients.",
|
|
42
|
-
"order": 90
|
|
42
|
+
"order": 90,
|
|
43
|
+
"defaults": {
|
|
44
|
+
"blockStreaming": true,
|
|
45
|
+
"chunkMode": "newline"
|
|
46
|
+
}
|
|
43
47
|
},
|
|
44
48
|
"install": {
|
|
45
49
|
"npmSpec": "@contextableai/clawg-ui",
|
package/src/http-handler.ts
CHANGED
|
@@ -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
|
|
|
@@ -368,8 +370,9 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
|
|
|
368
370
|
res.flushHeaders?.();
|
|
369
371
|
|
|
370
372
|
let closed = false;
|
|
371
|
-
|
|
373
|
+
let currentMessageId = `msg-${randomUUID()}`;
|
|
372
374
|
let messageStarted = false;
|
|
375
|
+
let currentRunId = runId;
|
|
373
376
|
|
|
374
377
|
const writeEvent = (event: { type: EventType } & Record<string, unknown>) => {
|
|
375
378
|
if (closed) {
|
|
@@ -383,6 +386,31 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
|
|
|
383
386
|
}
|
|
384
387
|
};
|
|
385
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
|
+
|
|
386
414
|
// Handle client disconnect
|
|
387
415
|
req.on("close", () => {
|
|
388
416
|
closed = true;
|
|
@@ -408,7 +436,7 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
|
|
|
408
436
|
}
|
|
409
437
|
|
|
410
438
|
// Register SSE writer so before/after_tool_call hooks can emit AG-UI events
|
|
411
|
-
setWriter(sessionKey, writeEvent,
|
|
439
|
+
setWriter(sessionKey, writeEvent, currentMessageId);
|
|
412
440
|
const storePath = runtime.channel.session.resolveStorePath(cfg.session?.store, {
|
|
413
441
|
agentId: route.agentId,
|
|
414
442
|
});
|
|
@@ -473,18 +501,25 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
|
|
|
473
501
|
if (!text) {
|
|
474
502
|
return false;
|
|
475
503
|
}
|
|
504
|
+
|
|
505
|
+
splitRunIfToolFired();
|
|
506
|
+
|
|
476
507
|
if (!messageStarted) {
|
|
477
508
|
messageStarted = true;
|
|
478
509
|
writeEvent({
|
|
479
510
|
type: EventType.TEXT_MESSAGE_START,
|
|
480
|
-
messageId,
|
|
511
|
+
messageId: currentMessageId,
|
|
512
|
+
runId: currentRunId,
|
|
481
513
|
role: "assistant",
|
|
482
514
|
});
|
|
483
515
|
}
|
|
516
|
+
|
|
517
|
+
// Join chunks with \n\n (breakPreference: paragraph uses double-newline joiner)
|
|
484
518
|
writeEvent({
|
|
485
519
|
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
486
|
-
messageId,
|
|
487
|
-
|
|
520
|
+
messageId: currentMessageId,
|
|
521
|
+
runId: currentRunId,
|
|
522
|
+
delta: text + "\n\n",
|
|
488
523
|
});
|
|
489
524
|
return true;
|
|
490
525
|
},
|
|
@@ -493,32 +528,39 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
|
|
|
493
528
|
return false;
|
|
494
529
|
}
|
|
495
530
|
const text = wasClientToolCalled(sessionKey) ? "" : payload.text?.trim();
|
|
531
|
+
|
|
496
532
|
if (text) {
|
|
533
|
+
splitRunIfToolFired();
|
|
534
|
+
|
|
497
535
|
if (!messageStarted) {
|
|
498
536
|
messageStarted = true;
|
|
499
537
|
writeEvent({
|
|
500
538
|
type: EventType.TEXT_MESSAGE_START,
|
|
501
|
-
messageId,
|
|
539
|
+
messageId: currentMessageId,
|
|
540
|
+
runId: currentRunId,
|
|
502
541
|
role: "assistant",
|
|
503
542
|
});
|
|
504
543
|
}
|
|
544
|
+
// Join chunks with \n\n (breakPreference: paragraph uses double-newline joiner)
|
|
505
545
|
writeEvent({
|
|
506
546
|
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
507
|
-
messageId,
|
|
508
|
-
|
|
547
|
+
messageId: currentMessageId,
|
|
548
|
+
runId: currentRunId,
|
|
549
|
+
delta: text + "\n\n",
|
|
509
550
|
});
|
|
510
551
|
}
|
|
511
552
|
// End the message and run
|
|
512
553
|
if (messageStarted) {
|
|
513
554
|
writeEvent({
|
|
514
555
|
type: EventType.TEXT_MESSAGE_END,
|
|
515
|
-
messageId,
|
|
556
|
+
messageId: currentMessageId,
|
|
557
|
+
runId: currentRunId,
|
|
516
558
|
});
|
|
517
559
|
}
|
|
518
560
|
writeEvent({
|
|
519
561
|
type: EventType.RUN_FINISHED,
|
|
520
562
|
threadId,
|
|
521
|
-
runId,
|
|
563
|
+
runId: currentRunId,
|
|
522
564
|
});
|
|
523
565
|
closed = true;
|
|
524
566
|
res.end();
|
|
@@ -550,13 +592,14 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
|
|
|
550
592
|
if (messageStarted) {
|
|
551
593
|
writeEvent({
|
|
552
594
|
type: EventType.TEXT_MESSAGE_END,
|
|
553
|
-
messageId,
|
|
595
|
+
messageId: currentMessageId,
|
|
596
|
+
runId: currentRunId,
|
|
554
597
|
});
|
|
555
598
|
}
|
|
556
599
|
writeEvent({
|
|
557
600
|
type: EventType.RUN_FINISHED,
|
|
558
601
|
threadId,
|
|
559
|
-
runId,
|
|
602
|
+
runId: currentRunId,
|
|
560
603
|
});
|
|
561
604
|
closed = true;
|
|
562
605
|
res.end();
|
|
@@ -574,6 +617,7 @@ export function createAguiHttpHandler(api: OpenClawPluginApi) {
|
|
|
574
617
|
clearWriter(sessionKey);
|
|
575
618
|
clearClientToolCalled(sessionKey);
|
|
576
619
|
clearClientToolNames(sessionKey);
|
|
620
|
+
clearToolFiredInRun(sessionKey);
|
|
577
621
|
}
|
|
578
622
|
};
|
|
579
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.
|
|
@@ -129,3 +149,4 @@ export function clearClientToolCalled(sessionKey: string): void {
|
|
|
129
149
|
console.log(`[clawg-ui] clearClientToolCalled: sessionKey=${sessionKey}`);
|
|
130
150
|
clientToolCalledFlags.delete(sessionKey);
|
|
131
151
|
}
|
|
152
|
+
|