@clawling/clawchat-plugin-openclaw 2026.6.24-1 → 2026.6.30-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.
- package/README.md +11 -3
- package/dist/src/api-client.js +3 -0
- package/dist/src/inbound.js +21 -0
- package/dist/src/login.runtime.js +2 -0
- package/dist/src/reply-dispatcher.js +1 -1
- package/dist/src/runtime.js +80 -0
- package/dist/src/skill-update.js +559 -0
- package/dist/src/tools.js +1 -1
- package/dist/src/ws-client.js +1 -1
- package/package.json +18 -1
- package/skills/clawchat/SKILL.md +1 -0
- package/skills/liveware-app/SKILL.md +1 -0
- package/src/api-client.ts +13 -2
- package/src/inbound.ts +41 -0
- package/src/login.runtime.ts +2 -0
- package/src/reply-dispatcher.ts +1 -1
- package/src/runtime.ts +100 -0
- package/src/skill-update.ts +752 -0
- package/src/tools.ts +1 -1
- package/src/ws-client.ts +1 -1
package/README.md
CHANGED
|
@@ -90,20 +90,26 @@ src/
|
|
|
90
90
|
api-types.ts /v1/* request/response type definitions
|
|
91
91
|
inbound.ts envelope → agent turn
|
|
92
92
|
outbound.ts agent reply → envelope
|
|
93
|
-
streaming.ts progressive reply emitter
|
|
94
|
-
buffered-stream.ts chunk coalescing + flush policy
|
|
95
93
|
message-mapper.ts fragment ↔ text/media mapping
|
|
96
94
|
group-message-coalescer.ts
|
|
97
95
|
per-group idle/max batching for non-mention turns
|
|
96
|
+
group-settings.ts per-group agent settings cache (GET /v1/agents/me/
|
|
97
|
+
group-settings)
|
|
98
98
|
reply-dispatcher.ts static vs stream routing
|
|
99
|
+
no-reply.ts clawchat:no-reply / :silent suppression token guard
|
|
99
100
|
login.runtime.ts invite-code exchange flow
|
|
101
|
+
refresh-manager.ts ClawChat token refresh + auto-logout orchestration
|
|
102
|
+
skill-update.ts conversational, adapter-driven skill hot-update
|
|
100
103
|
media-runtime.ts media download/upload pipeline
|
|
104
|
+
liveware-cli.ts liveware asset download helper (custom CDN User-Agent)
|
|
101
105
|
tools.ts clawchat_* agent tools (account / media / memory /
|
|
102
106
|
metadata / moments / mention)
|
|
103
107
|
tools-schema.ts JSON schemas for the clawchat_* tools
|
|
104
108
|
protocol.ts inbound payload type guards
|
|
105
109
|
protocol-types.ts v2 envelope, fragment, transport shapes
|
|
106
110
|
config.ts defaults + typebox schema
|
|
111
|
+
config-compat.ts migration for the plugin rename (openclaw-clawchat →
|
|
112
|
+
clawchat-plugin-openclaw id/channel key)
|
|
107
113
|
storage.ts plugin-owned SQLite (activations, connections,
|
|
108
114
|
clawchat_messages, tool_calls)
|
|
109
115
|
clawchat-memory.ts file-backed memory tools (see docs/clawchat-memory.md)
|
|
@@ -114,6 +120,8 @@ src/
|
|
|
114
120
|
plugin-prompts.ts loads prompts/platform.md and default-* prompts
|
|
115
121
|
ws-alignment.ts handshake capabilities + hello-ok diagnostics
|
|
116
122
|
ws-log.ts clawchat.ws single-line log formatting
|
|
123
|
+
llm-context-debug.ts opt-in LLM input/output snapshot capture for debugging
|
|
124
|
+
plugin-report.ts best-effort read of this plugin's package version
|
|
117
125
|
commands.ts /clawchat-activate runtime slash command
|
|
118
126
|
terminal-send.ts terminal-send suppression for clawchat_mention_message
|
|
119
127
|
mock-transport.ts in-memory Transport double for tests
|
|
@@ -178,4 +186,4 @@ plugin tests or publish the package.
|
|
|
178
186
|
|
|
179
187
|
## License
|
|
180
188
|
|
|
181
|
-
|
|
189
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/src/api-client.js
CHANGED
|
@@ -418,6 +418,9 @@ export function createOpenclawClawlingApiClient(opts) {
|
|
|
418
418
|
if (userId?.trim()) {
|
|
419
419
|
body.user_id = userId.trim();
|
|
420
420
|
}
|
|
421
|
+
if (opts.pluginVersion?.trim()) {
|
|
422
|
+
body.plugin_version = opts.pluginVersion.trim();
|
|
423
|
+
}
|
|
421
424
|
return await call("POST", "/v1/agents/connect", {
|
|
422
425
|
// `X-Device-Id` is added globally via `authHeaders` on every request.
|
|
423
426
|
headers: { "content-type": "application/json" },
|
package/dist/src/inbound.js
CHANGED
|
@@ -149,6 +149,27 @@ export async function dispatchOpenclawClawlingInbound(params) {
|
|
|
149
149
|
chatType,
|
|
150
150
|
userId: account.userId,
|
|
151
151
|
});
|
|
152
|
+
// Pending-consent gate for conversational skill hot-update. Only owner direct
|
|
153
|
+
// messages can answer a pending skill-update ask; the gate parses consent
|
|
154
|
+
// conservatively and, when it consumes the reply (affirm/deny), the message
|
|
155
|
+
// does NOT continue to the LLM. Ambiguous/unrelated replies return false and
|
|
156
|
+
// flow on normally (pending survives until timeout). See src/skill-update.ts.
|
|
157
|
+
if (chatType === "direct" &&
|
|
158
|
+
params.consumeOwnerConsent &&
|
|
159
|
+
account.ownerUserId &&
|
|
160
|
+
sender.id === account.ownerUserId) {
|
|
161
|
+
let consumed = false;
|
|
162
|
+
try {
|
|
163
|
+
consumed = await params.consumeOwnerConsent({ senderId: sender.id, text: rawBody, chatId });
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
log?.error?.(`[${account.accountId}] clawchat-plugin-openclaw skill-update consent gate error msg=${payload.message_id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
167
|
+
}
|
|
168
|
+
if (consumed) {
|
|
169
|
+
log?.info?.(`[${account.accountId}] clawchat-plugin-openclaw owner reply consumed as skill-update consent msg=${payload.message_id}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
152
173
|
// NOTE: the static mention-mode early-drop was removed here (Task 7 fix).
|
|
153
174
|
// Non-mention group messages now always flow through to ingestTurn, which
|
|
154
175
|
// applies the dynamic gate (GroupSettingsCache.effective) and returns
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { createOpenclawClawlingApiClient } from "./api-client.js";
|
|
3
3
|
import { ClawlingApiError } from "./api-types.js";
|
|
4
|
+
import { resolvePluginVersion } from "./plugin-report.js";
|
|
4
5
|
import { CHANNEL_ID, mergeOpenclawClawchatRuntimePluginActivation, mergeOpenclawClawchatToolAllow, resolveOpenclawClawlingAccount, } from "./config.js";
|
|
5
6
|
import { getClawChatStore } from "./storage.js";
|
|
6
7
|
/**
|
|
@@ -135,6 +136,7 @@ export async function runOpenclawClawlingLogin(params) {
|
|
|
135
136
|
// Pre-login we may not have a token yet. Send the current one (or empty)
|
|
136
137
|
// — the server should accept an unauthenticated invite-code exchange.
|
|
137
138
|
token: account.token || "",
|
|
139
|
+
pluginVersion: resolvePluginVersion(),
|
|
138
140
|
});
|
|
139
141
|
runtime.log("Verifying invite code …");
|
|
140
142
|
let result;
|
|
@@ -418,7 +418,7 @@ export function createOpenclawClawlingReplyDispatcher(options) {
|
|
|
418
418
|
};
|
|
419
419
|
// §7.6 (binding): client-supplied message_id MUST be `msg-` + a 26-char
|
|
420
420
|
// Crockford base32 ULID. Use the shared conformant minter — do not invent a
|
|
421
|
-
// different scheme (planned
|
|
421
|
+
// different scheme (planned backend validation will reject it).
|
|
422
422
|
const mintStaticMessageId = () => mintMessageId();
|
|
423
423
|
const emitTyping = (isTyping) => {
|
|
424
424
|
if (!isTyping && !typingActive)
|
package/dist/src/runtime.js
CHANGED
|
@@ -11,6 +11,7 @@ import { RefreshManager } from "./refresh-manager.js";
|
|
|
11
11
|
import { runOpenclawClawlingLogin, } from "./login.runtime.js";
|
|
12
12
|
import { CHANNEL_ID, effectiveOutputVisibility, effectiveGroupCommandMode, effectiveGroupMode, hasOpenclawClawlingConnectCredentials, resolveOpenclawClawlingAccount, } from "./config.js";
|
|
13
13
|
import { dispatchOpenclawClawlingInbound } from "./inbound.js";
|
|
14
|
+
import { PendingConsentStore, handleOwnerConsentReply, readEffectiveSkillVersions, resolveBundledSkillsDir, resolveManagedSkillsDir, runSkillUpdateCheck, } from "./skill-update.js";
|
|
14
15
|
import { fetchInboundMedia } from "./media-runtime.js";
|
|
15
16
|
import { createOpenclawClawlingReplyDispatcher } from "./reply-dispatcher.js";
|
|
16
17
|
import { runWithTerminalClawChatSendScope } from "./terminal-send.js";
|
|
@@ -1655,6 +1656,75 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1655
1656
|
client.on("metadata:invalidated", (env) => {
|
|
1656
1657
|
void handleMetadataInvalidation(env);
|
|
1657
1658
|
});
|
|
1659
|
+
// --- Conversational skill hot-update (adapter-driven; see src/skill-update.ts) ---
|
|
1660
|
+
// One pending-consent record per runtime.
|
|
1661
|
+
// WRITE target = the OpenClaw-managed skills dir (`<stateDir>/skills`): it is
|
|
1662
|
+
// chokidar-watched (host src/skills/runtime/refresh.ts:107) so a write bumps
|
|
1663
|
+
// the snapshot + rebuilds the prompt next turn, and it loads at HIGHER
|
|
1664
|
+
// precedence than plugin-bundled skills (managed=3 > bundled=2, host
|
|
1665
|
+
// src/skills/loading/workspace.ts:1220-1239), overriding the bundled copy.
|
|
1666
|
+
// The bundled `./skills` dir is READ-ONLY here: a first-boot fallback for the
|
|
1667
|
+
// "current installed version" only — never written (it lives in node_modules
|
|
1668
|
+
// → may be read-only and is clobbered on `openclaw plugins update`).
|
|
1669
|
+
const skillUpdateConsent = new PendingConsentStore();
|
|
1670
|
+
const managedSkillsDir = resolveManagedSkillsDir();
|
|
1671
|
+
let bundledSkillsDir = null;
|
|
1672
|
+
try {
|
|
1673
|
+
bundledSkillsDir = resolveBundledSkillsDir();
|
|
1674
|
+
}
|
|
1675
|
+
catch (err) {
|
|
1676
|
+
log?.info?.(`[${accountId}] clawchat-plugin-openclaw skill-update: bundled skills dir not resolvable (using managed-only current): ${err instanceof Error ? err.message : String(err)}`);
|
|
1677
|
+
}
|
|
1678
|
+
const sendOwnerSkillMessage = async (text) => {
|
|
1679
|
+
const ownerUserId = account.ownerUserId?.trim();
|
|
1680
|
+
if (!ownerUserId)
|
|
1681
|
+
return;
|
|
1682
|
+
await sendOpenclawClawlingText({
|
|
1683
|
+
client,
|
|
1684
|
+
account,
|
|
1685
|
+
to: { chatId: ownerUserId, chatType: "direct" },
|
|
1686
|
+
text,
|
|
1687
|
+
log,
|
|
1688
|
+
});
|
|
1689
|
+
};
|
|
1690
|
+
const triggerSkillUpdateCheck = async () => {
|
|
1691
|
+
const ownerUserId = account.ownerUserId?.trim();
|
|
1692
|
+
if (!ownerUserId) {
|
|
1693
|
+
log?.info?.(`[${accountId}] clawchat-plugin-openclaw skill-update check skipped: no owner user id`);
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
// Effective current = managed copy if present, else bundled (host precedence).
|
|
1697
|
+
const localVersions = await readEffectiveSkillVersions(managedSkillsDir, bundledSkillsDir);
|
|
1698
|
+
await runSkillUpdateCheck({
|
|
1699
|
+
ownerUserId,
|
|
1700
|
+
localVersions,
|
|
1701
|
+
fetchFn: globalThis.fetch,
|
|
1702
|
+
store: skillUpdateConsent,
|
|
1703
|
+
sendOwnerMessage: sendOwnerSkillMessage,
|
|
1704
|
+
log: {
|
|
1705
|
+
info: (m) => log?.info?.(`[${accountId}] clawchat-plugin-openclaw ${m}`),
|
|
1706
|
+
error: (m) => log?.error?.(`[${accountId}] clawchat-plugin-openclaw ${m}`),
|
|
1707
|
+
},
|
|
1708
|
+
});
|
|
1709
|
+
};
|
|
1710
|
+
const consumeOwnerSkillConsent = async (args) => {
|
|
1711
|
+
const ownerUserId = account.ownerUserId?.trim();
|
|
1712
|
+
if (!ownerUserId)
|
|
1713
|
+
return false;
|
|
1714
|
+
return handleOwnerConsentReply({
|
|
1715
|
+
ownerUserId,
|
|
1716
|
+
senderId: args.senderId,
|
|
1717
|
+
text: args.text,
|
|
1718
|
+
store: skillUpdateConsent,
|
|
1719
|
+
fetchFn: globalThis.fetch,
|
|
1720
|
+
skillsDir: managedSkillsDir,
|
|
1721
|
+
sendOwnerMessage: sendOwnerSkillMessage,
|
|
1722
|
+
log: {
|
|
1723
|
+
info: (m) => log?.info?.(`[${accountId}] clawchat-plugin-openclaw ${m}`),
|
|
1724
|
+
error: (m) => log?.error?.(`[${accountId}] clawchat-plugin-openclaw ${m}`),
|
|
1725
|
+
},
|
|
1726
|
+
});
|
|
1727
|
+
};
|
|
1658
1728
|
client.on("notify:signal", (env) => {
|
|
1659
1729
|
// §9.4 reliable system notification. The plugin holds no friend/roster
|
|
1660
1730
|
// cache (friends are fetched on demand via REST tools), so there is nothing
|
|
@@ -1670,6 +1740,15 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1670
1740
|
log?.info?.(`[${accountId}] clawchat-plugin-openclaw agent.config.changed signal received; re-pulling group settings`);
|
|
1671
1741
|
pullGroupSettings();
|
|
1672
1742
|
}
|
|
1743
|
+
else if (type === "clawchat.skill.update.check") {
|
|
1744
|
+
// Content-free trigger: check the official skill source in code and, if
|
|
1745
|
+
// there is an update, ask the owner in chat for consent. The atomic
|
|
1746
|
+
// overwrite happens later when the owner affirms (consent gate).
|
|
1747
|
+
log?.info?.(`[${accountId}] clawchat-plugin-openclaw clawchat.skill.update.check signal received; checking official skill source`);
|
|
1748
|
+
void triggerSkillUpdateCheck().catch((err) => {
|
|
1749
|
+
log?.error?.(`[${accountId}] clawchat-plugin-openclaw skill-update check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1673
1752
|
}
|
|
1674
1753
|
});
|
|
1675
1754
|
client.on("replay:done", (env) => {
|
|
@@ -2278,6 +2357,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
2278
2357
|
runtime,
|
|
2279
2358
|
account,
|
|
2280
2359
|
log,
|
|
2360
|
+
consumeOwnerConsent: consumeOwnerSkillConsent,
|
|
2281
2361
|
ingest: async (turn) => {
|
|
2282
2362
|
ingestResult = await ingestTurn(turn);
|
|
2283
2363
|
},
|