@clawling/clawchat-plugin-openclaw 2026.9.26-3 → 2026.10.7-1

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.
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Outbound reply streaming (protocol §8) for one assistant message.
3
+ *
4
+ * Lifecycle on the wire (§8.4 finalize-reply pattern):
5
+ *
6
+ * message.created(M) → message.add(M, seq 0..n)* → message.done(M)
7
+ * → message.reply (message_id = M, the polished final)
8
+ *
9
+ * `message.done` carries the FINAL text, not the last partial: adds are
10
+ * throttled, so the last one that went out can be well short of the answer,
11
+ * and the server materializes its merged reply from the stream. `finish`
12
+ * therefore takes the final text, sends whatever tail of it the throttle held
13
+ * back as one last add, and closes with the whole text. A final that does not
14
+ * extend what was shown cannot be appended (§8.2) — the stream is failed and
15
+ * the final goes out under its own id.
16
+ *
17
+ * or, when the reply turns out not to be sendable (no-reply token, run
18
+ * aborted, nothing delivered): message.created(M) → … → message.failed(M),
19
+ * which aborts the stream without materializing anything.
20
+ *
21
+ * Streaming frames get no ack (§8.5) and no server-side mention repair, so the
22
+ * stream only ever carries plain text; mentions, media and rich fragments ride
23
+ * on the final materialized message, which reuses M so the stored row collapses
24
+ * to the final reply.
25
+ *
26
+ * No-reply guard. A partial must never show something that later turns out to
27
+ * be a no-reply: nothing is opened until the text is at least
28
+ * STREAM_HOLD_CHARS long and is not a no-reply / silence marker; a trailing
29
+ * fragment that could be the start of a no-reply token is held back; and if a
30
+ * token shows up later the stream stops growing and is failed at finalize.
31
+ */
32
+ import { EVENT } from "./protocol-types.js";
33
+ import { isClawChatNoopResponseText } from "./profile-prompt.js";
34
+ import { isNoReplyTokenPrefix } from "./no-reply.js";
35
+ /** Minimum visible (trimmed) length before a stream is opened. */
36
+ export const STREAM_HOLD_CHARS = 32;
37
+ /** Minimum spacing between two `message.add` frames for one stream. */
38
+ export const STREAM_MIN_ADD_INTERVAL_MS = 250;
39
+ /** How far back from the end a partial no-reply token is looked for. */
40
+ const TOKEN_TAIL_WINDOW = 40;
41
+ /**
42
+ * Decide how much of a cumulative partial `text` may be shown.
43
+ * - `poisoned`: the text is (or contains) a no-reply token; never show it.
44
+ * - `hold`: too short to be sure yet.
45
+ * - `ok`: `text` with any trailing possible-token prefix cut off.
46
+ */
47
+ export function streamableText(text) {
48
+ if (isClawChatNoopResponseText(text))
49
+ return { kind: "poisoned" };
50
+ let safe = text;
51
+ for (let i = Math.max(0, text.length - TOKEN_TAIL_WINDOW); i < text.length; i += 1) {
52
+ const ch = text[i];
53
+ if (/\s/.test(ch))
54
+ continue;
55
+ if (isNoReplyTokenPrefix(text.slice(i))) {
56
+ safe = text.slice(0, i);
57
+ break;
58
+ }
59
+ }
60
+ if (safe.trim().length < STREAM_HOLD_CHARS)
61
+ return { kind: "hold" };
62
+ return { kind: "ok", text: safe };
63
+ }
64
+ export function createReplyStream(options) {
65
+ const now = options.now ?? Date.now;
66
+ const prefix = options.logPrefix ?? "clawchat-plugin-openclaw";
67
+ let messageId = null;
68
+ let closed = false;
69
+ let poisoned = false;
70
+ let diverged = false;
71
+ let sent = "";
72
+ let sequence = -1;
73
+ let lastAddAt = 0;
74
+ let startedAt = 0;
75
+ const canSend = () => {
76
+ const client = options.client;
77
+ if (client.transportState !== undefined && client.transportState !== "open")
78
+ return false;
79
+ if (client.state !== undefined && client.state !== "connected")
80
+ return false;
81
+ return true;
82
+ };
83
+ const emit = (event, payload) => {
84
+ try {
85
+ options.client.emitRaw(event, payload, { chat_id: options.chatId });
86
+ return true;
87
+ }
88
+ catch (error) {
89
+ options.log?.error?.(`${prefix} stream ${event} failed msg=${messageId}: ${String(error)}`);
90
+ return false;
91
+ }
92
+ };
93
+ const streamingState = (status, completedAt) => ({
94
+ status,
95
+ sequence: Math.max(sequence, 0),
96
+ mutation_policy: "append_text_only",
97
+ started_at: startedAt,
98
+ completed_at: completedAt,
99
+ });
100
+ const sendAdd = (next, at) => {
101
+ sequence += 1;
102
+ const ok = emit(EVENT.MESSAGE_ADD, {
103
+ message_id: messageId,
104
+ sequence,
105
+ mutation: { type: "append", target_fragment_index: 0 },
106
+ fragments: [{ kind: "text", text: next, delta: next.slice(sent.length) }],
107
+ streaming: streamingState("streaming", null),
108
+ added_at: at,
109
+ });
110
+ if (!ok) {
111
+ sequence -= 1;
112
+ return false;
113
+ }
114
+ sent = next;
115
+ lastAddAt = at;
116
+ return true;
117
+ };
118
+ const fail = (reason) => {
119
+ if (!messageId || closed)
120
+ return;
121
+ closed = true;
122
+ const at = now();
123
+ emit(EVENT.MESSAGE_FAILED, {
124
+ message_id: messageId,
125
+ fragments: [{ kind: "text", text: sent }],
126
+ streaming: streamingState("failed", at),
127
+ completed_at: at,
128
+ });
129
+ options.log?.info?.(`${prefix} stream failed msg=${messageId} reason=${reason}`);
130
+ };
131
+ return {
132
+ update(text) {
133
+ if (closed || poisoned || diverged)
134
+ return;
135
+ const decision = streamableText(text);
136
+ if (decision.kind === "poisoned") {
137
+ poisoned = true;
138
+ options.log?.info?.(`${prefix} stream halted: no-reply token in partial msg=${messageId ?? "-"}`);
139
+ return;
140
+ }
141
+ if (decision.kind === "hold")
142
+ return;
143
+ const next = decision.text;
144
+ if (messageId && !next.startsWith(sent)) {
145
+ // §8.2 delta invariant cannot hold for a rewrite; stop growing the
146
+ // stream and let the final message (same id) carry the real text.
147
+ diverged = true;
148
+ return;
149
+ }
150
+ if (next === sent)
151
+ return;
152
+ if (!canSend())
153
+ return;
154
+ if (!messageId) {
155
+ const id = options.mintMessageId();
156
+ startedAt = now();
157
+ if (!emit(EVENT.MESSAGE_CREATED, { message_id: id, message_mode: "normal" }))
158
+ return;
159
+ messageId = id;
160
+ options.log?.info?.(`${prefix} stream opened msg=${id} to=${options.chatId}`);
161
+ }
162
+ else if (now() - lastAddAt < STREAM_MIN_ADD_INTERVAL_MS) {
163
+ return;
164
+ }
165
+ sendAdd(next, now());
166
+ },
167
+ diverges(text) {
168
+ if (!messageId || closed)
169
+ return false;
170
+ const decision = streamableText(text);
171
+ return decision.kind === "ok" && !decision.text.startsWith(sent);
172
+ },
173
+ isOpen() {
174
+ return messageId !== null && !closed;
175
+ },
176
+ shownText() {
177
+ return sent;
178
+ },
179
+ finish(finalText) {
180
+ if (!messageId || closed)
181
+ return null;
182
+ if (streamableText(finalText).kind === "poisoned") {
183
+ fail("no-reply");
184
+ return null;
185
+ }
186
+ // Trailing whitespace a partial carried may be trimmed off the final.
187
+ if (!finalText.startsWith(sent.trimEnd())) {
188
+ fail("superseded");
189
+ return null;
190
+ }
191
+ if (finalText.length > sent.length && finalText.startsWith(sent)) {
192
+ // Past the throttle: this is the last frame before done.
193
+ sendAdd(finalText, now());
194
+ }
195
+ closed = true;
196
+ const at = now();
197
+ emit(EVENT.MESSAGE_DONE, {
198
+ message_id: messageId,
199
+ fragments: [{ kind: "text", text: finalText }],
200
+ streaming: streamingState("done", at),
201
+ completed_at: at,
202
+ });
203
+ options.log?.info?.(`${prefix} stream done msg=${messageId} text_len=${finalText.length}`);
204
+ return messageId;
205
+ },
206
+ fail,
207
+ };
208
+ }
@@ -33,6 +33,7 @@ import { loadClawChatPromptMetadata, renderClawChatProfilePrompt, resolveSenderR
33
33
  import { refreshGroupProfile, syncFirstSeenClawChatProfiles } from "./profile-sync.js";
34
34
  import { pullGroupMetadata, pullOwnerMetadata } from "./clawchat-metadata.js";
35
35
  import { deleteClawChatMemoryFile, readClawChatMemoryFile } from "./clawchat-memory.js";
36
+ import { registerMentionRosterResolver } from "./mention-autolink.js";
36
37
  import { clearClawChatPromptInjectionForSession, stageClawChatPromptInjection, } from "./prompt-injection.js";
37
38
  import { createGroupMessageCoalescer } from "./group-message-coalescer.js";
38
39
  import { openclawLlmContextDebug } from "./llm-context-debug.js";
@@ -3229,6 +3230,23 @@ export async function startOpenclawClawlingGateway(params) {
3229
3230
  return;
3230
3231
  }
3231
3232
  activeClients.set(accountId, client);
3233
+ // Text "@name" autolinking on outbound group sends reads the same cached
3234
+ // roster (participant ids + nicknames) the group prompt shows the model.
3235
+ const unregisterMentionRoster = registerMentionRosterResolver(accountId, async (groupId) => {
3236
+ const memoryRoot = resolveMemoryRootForPeer({ kind: "group", id: groupId });
3237
+ if (!memoryRoot)
3238
+ return [];
3239
+ let ownerMetadata = null;
3240
+ try {
3241
+ const ownerFile = await readClawChatMemoryFile(memoryRoot, { targetType: "owner", targetId: "owner" });
3242
+ ownerMetadata = ownerFile.exists ? ownerFile.metadata : null;
3243
+ }
3244
+ catch {
3245
+ ownerMetadata = null;
3246
+ }
3247
+ const participants = await loadGroupParticipantsForPrompt({ memoryRoot, groupId, ownerMetadata });
3248
+ return participants.map((participant) => ({ userId: participant.id, name: participant.name }));
3249
+ });
3232
3250
  log?.info?.(`[${accountId}] clawchat-plugin-openclaw runtime active client registered`);
3233
3251
  setStatus({
3234
3252
  ...getStatus(),
@@ -3240,6 +3258,7 @@ export async function startOpenclawClawlingGateway(params) {
3240
3258
  await waitUntilAbort(abortSignal, async () => {
3241
3259
  log?.info?.(`[${accountId}] clawchat-plugin-openclaw runtime abort received; closing client`);
3242
3260
  activeClients.delete(accountId);
3261
+ unregisterMentionRoster();
3243
3262
  closingForAbort = true;
3244
3263
  clearBootstrapRetry();
3245
3264
  // §A.1 — stop the proactive refresh timer on shutdown.
@@ -66,7 +66,7 @@ export const OFFICIAL_SKILLS_BASE = "https://raw.githubusercontent.com/clawling/
66
66
  * in the install-cli repo, bump this constant, ship it. `liveware-sample.ts`
67
67
  * imports the same ref, so the `livewares` tree at that tag is pinned too.
68
68
  */
69
- export const DEFAULT_SKILLS_REF = "skills-v1.15.2";
69
+ export const DEFAULT_SKILLS_REF = "skills-v1.16.0";
70
70
  /** Refuse to treat an absurdly large response as a skill file (defence in depth). */
71
71
  export const MAX_SKILL_BYTES = 256 * 1024;
72
72
  /** This adapter's host target inside `skills/manifest.json`. */
package/dist/src/tools.js CHANGED
@@ -1449,11 +1449,18 @@ export function registerOpenclawClawlingTools(api, options = {}) {
1449
1449
  return {
1450
1450
  name: "clawchat_orchestrate_set_agent_behavior",
1451
1451
  label: "Rewrite A Sibling's System Prompt",
1452
- description: toolDescription("Replace the WHOLE system prompt (behavior) of one of the owner's other agents. " +
1452
+ description: toolDescription("Stage/room speaking rules go in the group description, not here; for a stage, append the stage sentence below to off-desktop members (clawchat-orchestration skill). " +
1453
+ "Replace the WHOLE system prompt (behavior) of one of the owner's other agents. " +
1453
1454
  "TRIGGER — invoke when the owner asks to change how another agent behaves, talks, or what it is for. " +
1454
1455
  "This REPLACES the entire field — call clawchat_orchestrate_get_agent first, edit the text you got back, and send the full new value. " +
1455
1456
  "Sending a fragment deletes everything else and the owner cannot recover it. Max 3000 runes. " +
1456
- "Nickname and bio cannot be changed here."),
1457
+ "Nickname and bio cannot be changed here. " +
1458
+ "Behavior follows an agent into every room, so one room's rules never go here — they go in that group's description (clawchat_orchestrate_set_group_prompt). " +
1459
+ "A stage (a group whose description has its agents chime in freely) adds only one line here, the same for every room: " +
1460
+ "give each member that the ClawChat desktop app does not run on the owner's computer — Hermes and OpenClaw plugin agents among them — this sentence, verbatim, " +
1461
+ "appended without deleting anything and without naming the room: " +
1462
+ "\"In a group whose description makes it a stage, you are one of the players: pick up the other characters' lines without waiting to be called.\" " +
1463
+ "Older plugin versions rank the group description below their own reply rules; this line is what lets them speak on a stage."),
1457
1464
  parameters: ClawchatOrchSetAgentBehaviorSchema,
1458
1465
  async execute(_callId, params) {
1459
1466
  return await recordClawchatToolCall(accountId, "clawchat_orchestrate_set_agent_behavior", params, async () => {
@@ -1497,11 +1504,15 @@ export function registerOpenclawClawlingTools(api, options = {}) {
1497
1504
  return {
1498
1505
  name: "clawchat_orchestrate_set_group_prompt",
1499
1506
  label: "Rewrite A Group's System Prompt",
1500
- description: toolDescription("Replace the WHOLE system prompt (description) of a group the owner administers. " +
1507
+ description: toolDescription("Stage/room speaking rules go here, in the group description; for a stage, also append the stage sentence to off-desktop members' behavior (clawchat-orchestration skill). " +
1508
+ "Replace the WHOLE system prompt (description) of a group the owner administers. " +
1501
1509
  "TRIGGER — invoke when the owner asks to change how a group behaves as a whole. " +
1502
1510
  "This REPLACES the entire field — call clawchat_orchestrate_get_group first and send the full new value. " +
1503
1511
  "Sending a fragment deletes everything else and the owner cannot recover it. Max 3000 runes. " +
1504
- "The group title cannot be changed here."),
1512
+ "The group title cannot be changed here. " +
1513
+ "A room where agents should chime in freely, a stage, says so in its description, and its own rules go nowhere else: behavior follows an agent into every room. " +
1514
+ "A stage also needs one room-independent line in the behavior of each member the ClawChat desktop app does not run on the owner's computer, appended verbatim with clawchat_orchestrate_set_agent_behavior: " +
1515
+ "\"In a group whose description makes it a stage, you are one of the players: pick up the other characters' lines without waiting to be called.\""),
1505
1516
  parameters: ClawchatOrchSetGroupPromptSchema,
1506
1517
  async execute(_callId, params) {
1507
1518
  return await recordClawchatToolCall(accountId, "clawchat_orchestrate_set_group_prompt", params, async () => {
@@ -124,6 +124,7 @@
124
124
  "forwardThinking": { "type": "boolean" },
125
125
  "forwardToolCalls": { "type": "boolean" },
126
126
  "richInteractions": { "type": "boolean" },
127
+ "streamReplies": { "type": "boolean" },
127
128
  "awarenessNote": { "type": "boolean" },
128
129
  "friendGreeting": { "type": "boolean" },
129
130
  "livewareSample": { "type": "boolean" },
@@ -197,6 +198,7 @@
197
198
  "forwardThinking": { "type": "boolean" },
198
199
  "forwardToolCalls": { "type": "boolean" },
199
200
  "richInteractions": { "type": "boolean" },
201
+ "streamReplies": { "type": "boolean" },
200
202
  "awarenessNote": { "type": "boolean" },
201
203
  "friendGreeting": { "type": "boolean" },
202
204
  "livewareSample": { "type": "boolean" },
@@ -276,6 +278,7 @@
276
278
  "forwardThinking": { "type": "boolean" },
277
279
  "forwardToolCalls": { "type": "boolean" },
278
280
  "richInteractions": { "type": "boolean" },
281
+ "streamReplies": { "type": "boolean" },
279
282
  "awarenessNote": { "type": "boolean" },
280
283
  "friendGreeting": { "type": "boolean" },
281
284
  "livewareSample": { "type": "boolean" },
@@ -349,6 +352,7 @@
349
352
  "forwardThinking": { "type": "boolean" },
350
353
  "forwardToolCalls": { "type": "boolean" },
351
354
  "richInteractions": { "type": "boolean" },
355
+ "streamReplies": { "type": "boolean" },
352
356
  "awarenessNote": { "type": "boolean" },
353
357
  "friendGreeting": { "type": "boolean" },
354
358
  "livewareSample": { "type": "boolean" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawling/clawchat-plugin-openclaw",
3
- "version": "2026.9.26-3",
3
+ "version": "2026.10.7-1",
4
4
  "description": "OpenClaw ClawChat channel plugin",
5
5
  "license": "MIT",
6
6
  "author": "CLAWLING PTE. LTD.",
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  name: clawchat-liveware-sample
3
- version: 2.0.0
4
- description: Use when the owner talks about the auto-installed "Liveware Sample" (page title "Hello from your agent") — the guide to their first Liveware. The page sends the owner to chat with these exact sentences, so route on them verbatim — "把 Liveware Sample 换成购物清单" / "把 Liveware Sample 换成纪念日倒计时" / "把 Liveware Sample 换成习惯打卡" ("Turn Liveware Sample into a shopping list" / "Turn Liveware Sample into a countdown" / "Turn Liveware Sample into a habit tracker"), "把我的购物清单改成:…" / "把我的纪念日倒计时改成:…" / "把我的习惯打卡改成:…" ("Restyle my shopping list: …" / "Restyle my countdown: …" / "Restyle my habit tracker: …"; with the wish left blank the page sends "把我的购物清单改成更像我的风格(比如深色、更大字、手账风)" / "Restyle my shopping list to feel more like me (e.g. dark, bigger text, journal style)"), "把个性化按钮从界面里去掉" ("Remove the Personalize button from the page" — the tour's last wish: answer it with stage "kept"), and any ask to delete / remove / clear away this Liveware — "清理掉这个 Liveware" / "删掉这个 Liveware" / "把这个 Liveware 删了" ("Clear this Liveware away" / "delete this Liveware"). Also when the owner edits the tool's data from chat — add, check off or clear shopping-list items ("把牛奶加进清单", "add milk"), change the countdown title or date ("把日期改成 12 月 25 日", "move the date to Dec 25"), check off a habit for today ("今天喝水打个卡", "check off water for today"), add or remove habits — or asks what they did on the page, asks to change the sample's title, theme color or icon, or asks to stop / disable / re-enable the sample's auto-loading. Covers writing state.json (stage, tool, title), data.json, the personalized tool/index.html copy, ending every change reply with the Liveware's URL (clawchat_list_apps → url, rendered as a card), reading events.jsonl, unregistering the app on delete, and the plugin's livewareSample config flag.
3
+ version: 2.0.1
4
+ description: >-
5
+ Use when the owner talks about the auto-installed "Liveware Sample" (page title "Hello from your agent") — the guide to their first Liveware. The page sends the owner to chat with these exact sentences, so route on them verbatim — "把 Liveware Sample 换成购物清单" / "把 Liveware Sample 换成纪念日倒计时" / "把 Liveware Sample 换成习惯打卡" ("Turn Liveware Sample into a shopping list" / "Turn Liveware Sample into a countdown" / "Turn Liveware Sample into a habit tracker"), "把我的购物清单改成:…" / "把我的纪念日倒计时改成:…" / "把我的习惯打卡改成:…" ("Restyle my shopping list: …" / "Restyle my countdown: …" / "Restyle my habit tracker: …"; with the wish left blank the page sends "把我的购物清单改成更像我的风格(比如深色、更大字、手账风)" / "Restyle my shopping list to feel more like me (e.g. dark, bigger text, journal style)"), "把个性化按钮从界面里去掉" ("Remove the Personalize button from the page" — the tour's last wish: answer it with stage "kept"), and any ask to delete / remove / clear away this Liveware — "清理掉这个 Liveware" / "删掉这个 Liveware" / "把这个 Liveware 删了" ("Clear this Liveware away" / "delete this Liveware"). Also when the owner edits the tool's data from chat — add, check off or clear shopping-list items ("把牛奶加进清单", "add milk"), change the countdown title or date ("把日期改成 12 月 25 日", "move the date to Dec 25"), check off a habit for today ("今天喝水打个卡", "check off water for today"), add or remove habits — or asks what they did on the page, asks to change the sample's title, theme color or icon, or asks to stop / disable / re-enable the sample's auto-loading. Covers writing state.json (stage, tool, title), data.json, the personalized tool/index.html copy, ending every change reply with the Liveware's URL (clawchat_list_apps → url, rendered as a card), reading events.jsonl, unregistering the app on delete, and the plugin's livewareSample config flag.
5
6
  ---
6
7
 
7
8
  # ClawChat Liveware Sample — the owner's first Liveware
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: clawchat-orchestration
3
- version: 2.0.2
3
+ version: 2.1.0
4
4
  description: Use when the owner asks this agent to manage their OTHER ClawChat agents or their groups — 编排 / orchestrate a fleet, read or rewrite another agent's 提示词 / system prompt / behavior, 禁言 / mute an agent, change 回复模式 / reply mode, stop 刷屏 / flooding in a group, 建群 / create a group of agents, add or remove agents from a group, or 签发连接码 / issue a connect code.
5
5
  ---
6
6
 
@@ -121,6 +121,34 @@ show in a roleplay group. Ask the owner if you cannot tell.
121
121
  kills a scene — characters are picking up a line, not taking a ticket.
122
122
  "Stop after three rounds" truncates a discussion instead of converging it.
123
123
 
124
+ ### A stage: where its speaking rules go
125
+
126
+ A group whose agents should pick up each other's lines without being called —
127
+ a roleplay scene, a free-running roundtable — is a *stage*. Two writes set
128
+ one up, and only two:
129
+
130
+ 1. **The room's rules go in the group description, and nowhere else.** A room
131
+ where agents should chime in freely, a stage, says so in its description,
132
+ and its own rules go nowhere else: behavior follows an agent into every
133
+ room. Who plays whom, how a turn works, when to speak — all of it goes
134
+ through `clawchat_orchestrate_set_group_prompt`. Do not copy any of it into
135
+ each member's behavior, and do not name this room in a behavior.
136
+ 2. **One line in the behavior of each member the ClawChat desktop app does
137
+ not run.** Hermes plugins before `0.14.0-96` and OpenClaw plugins before
138
+ `2026.9.26-3` rank the group description below their own "answer only
139
+ when invited" rule, so on a stage they stay quiet. You cannot see a
140
+ member's plugin version from here, so give every member that does not run
141
+ in the ClawChat desktop app on the owner's computer — every Hermes or
142
+ OpenClaw plugin agent, and any other agent that holds its own connection —
143
+ this sentence, verbatim:
144
+
145
+ > In a group whose description makes it a stage, you are one of the players: pick up the other characters' lines without waiting to be called.
146
+
147
+ Read the behavior first (`clawchat_orchestrate_get_agent`), append the
148
+ sentence, delete nothing, and send the whole field back. It holds in every
149
+ room — it only does anything where a description makes the room a stage —
150
+ so it is safe to add when you cannot tell where a member runs.
151
+
124
152
  ### Then, in any group
125
153
 
126
154
  1. **Hard before soft.** When something is going wrong, the speaking settings
@@ -27,24 +27,24 @@
27
27
  "bytes": 4184
28
28
  },
29
29
  "clawchat-liveware-sample": {
30
- "version": "2.0.0",
30
+ "version": "2.0.1",
31
31
  "path": "openclaw/clawchat-liveware-sample/SKILL.md",
32
- "sha256": "edafa5f802907c97c32af63feb5b62dcbb41c064649912c710808d27187aaa57",
33
- "bytes": 13938
32
+ "sha256": "9b789fdd8e867af73dd0b851134b4287298f08e2fde87e3138f3d5b841aed0c5",
33
+ "bytes": 13943
34
34
  },
35
35
  "clawchat-orchestration": {
36
- "version": "2.0.2",
36
+ "version": "2.1.0",
37
37
  "path": "shared/clawchat-orchestration/SKILL.md",
38
- "sha256": "e32f29c5f41a7621732625a58a9f3d6fe365d7dde9bdcd892f9623e7fc180414",
39
- "bytes": 9477
38
+ "sha256": "501b757b1c4718b85f1a5dd2fc3a7f30a5849eebd57e6acb78ae50390d6d0722",
39
+ "bytes": 11155
40
40
  }
41
41
  },
42
42
  "hermes": {
43
43
  "clawchat-core": {
44
- "version": "1.15.0",
44
+ "version": "1.16.0",
45
45
  "path": "hermes/clawchat-core/SKILL.md",
46
- "sha256": "0d203321c4f5a8d3f85fa3e7de15803891c68c27d3862d47553d3de86fbee363",
47
- "bytes": 23959
46
+ "sha256": "091f3e36632629d6783e06e6321ccdce98f9a17baf131f2f55c7700adb9335d5",
47
+ "bytes": 24659
48
48
  },
49
49
  "clawchat-liveware": {
50
50
  "version": "1.2.3",
@@ -65,16 +65,16 @@
65
65
  "bytes": 4184
66
66
  },
67
67
  "clawchat-liveware-sample": {
68
- "version": "2.0.0",
68
+ "version": "2.0.1",
69
69
  "path": "hermes/clawchat-liveware-sample/SKILL.md",
70
- "sha256": "8278db2646d1553e41f4cf061c710b5363c3f41fa9ae32d8b1021fa2175b9a41",
71
- "bytes": 13838
70
+ "sha256": "93d489ee7ee7607bc9a61defd16e06b1ee3c93d09a1115fa809ef06879e57cc4",
71
+ "bytes": 13843
72
72
  },
73
73
  "clawchat-orchestration": {
74
- "version": "2.0.2",
74
+ "version": "2.1.0",
75
75
  "path": "shared/clawchat-orchestration/SKILL.md",
76
- "sha256": "e32f29c5f41a7621732625a58a9f3d6fe365d7dde9bdcd892f9623e7fc180414",
77
- "bytes": 9477
76
+ "sha256": "501b757b1c4718b85f1a5dd2fc3a7f30a5849eebd57e6acb78ae50390d6d0722",
77
+ "bytes": 11155
78
78
  }
79
79
  }
80
80
  },
package/src/config.ts CHANGED
@@ -112,6 +112,8 @@ export type OpenclawClawlingAccountConfig = {
112
112
  forwardToolCalls?: boolean;
113
113
  /** Emit approval/action rich fragments instead of plain fallback text. */
114
114
  richInteractions?: boolean;
115
+ /** Experimental: stream reply text with protocol §8 frames (default false). */
116
+ streamReplies?: boolean;
115
117
  /** Emit ONE consolidated awareness note to the agent when friend/conversation signals arrive. */
116
118
  awarenessNote?: boolean;
117
119
  /** Speak first to a newly added non-owner friend (default true). */
@@ -175,6 +177,7 @@ export const openclawClawlingAccountConfigSchema = {
175
177
  forwardThinking: { type: "boolean" },
176
178
  forwardToolCalls: { type: "boolean" },
177
179
  richInteractions: { type: "boolean" },
180
+ streamReplies: { type: "boolean" },
178
181
  awarenessNote: { type: "boolean" },
179
182
  friendGreeting: { type: "boolean" },
180
183
  livewareSample: { type: "boolean" },
@@ -313,6 +316,7 @@ export type ResolvedOpenclawClawlingAccount = {
313
316
  forwardThinking: boolean;
314
317
  forwardToolCalls: boolean;
315
318
  richInteractions: boolean;
319
+ streamReplies: boolean;
316
320
  awarenessNote: boolean;
317
321
  friendGreeting: boolean;
318
322
  livewareSample: boolean;
@@ -651,6 +655,8 @@ export function resolveOpenclawClawlingAccount(
651
655
  typeof channel.forwardToolCalls === "boolean" ? channel.forwardToolCalls : false;
652
656
  const richInteractions =
653
657
  typeof channel.richInteractions === "boolean" ? channel.richInteractions : false;
658
+ const streamReplies =
659
+ typeof channel.streamReplies === "boolean" ? channel.streamReplies : false;
654
660
  const awarenessNote =
655
661
  typeof channel.awarenessNote === "boolean" ? channel.awarenessNote : false;
656
662
  const friendGreeting =
@@ -683,6 +689,7 @@ export function resolveOpenclawClawlingAccount(
683
689
  forwardThinking,
684
690
  forwardToolCalls,
685
691
  richInteractions,
692
+ streamReplies,
686
693
  awarenessNote,
687
694
  friendGreeting,
688
695
  livewareSample,