@clawling/clawchat-plugin-openclaw 2026.5.12-28
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/INSTALL.md +64 -0
- package/README.md +227 -0
- package/dist/index.js +20 -0
- package/dist/setup-entry.js +3 -0
- package/dist/src/api-client.js +263 -0
- package/dist/src/api-types.js +17 -0
- package/dist/src/api-types.test-d.js +10 -0
- package/dist/src/buffered-stream.js +177 -0
- package/dist/src/channel.js +66 -0
- package/dist/src/channel.setup.js +119 -0
- package/dist/src/clawchat-memory.js +403 -0
- package/dist/src/clawchat-metadata.js +310 -0
- package/dist/src/client.js +35 -0
- package/dist/src/commands.js +35 -0
- package/dist/src/config.js +274 -0
- package/dist/src/group-message-coalescer.js +119 -0
- package/dist/src/inbound.js +170 -0
- package/dist/src/llm-context-debug.js +86 -0
- package/dist/src/login.runtime.js +204 -0
- package/dist/src/media-runtime.js +85 -0
- package/dist/src/message-mapper.js +146 -0
- package/dist/src/mock-transport.js +31 -0
- package/dist/src/outbound.js +628 -0
- package/dist/src/plugin-prompts.js +89 -0
- package/dist/src/profile-prompt.js +269 -0
- package/dist/src/profile-sync.js +110 -0
- package/dist/src/prompt-injection.js +25 -0
- package/dist/src/protocol-types.js +63 -0
- package/dist/src/protocol-types.typecheck.js +1 -0
- package/dist/src/protocol.js +33 -0
- package/dist/src/reply-dispatcher.js +422 -0
- package/dist/src/runtime.js +1254 -0
- package/dist/src/storage.js +525 -0
- package/dist/src/streaming.js +65 -0
- package/dist/src/terminal-send.js +36 -0
- package/dist/src/tools-schema.js +208 -0
- package/dist/src/tools.js +920 -0
- package/dist/src/ws-alignment.js +178 -0
- package/dist/src/ws-client.js +588 -0
- package/dist/src/ws-log.js +19 -0
- package/index.ts +24 -0
- package/openclaw.plugin.json +169 -0
- package/package.json +80 -0
- package/prompts/default-group-bio.md +19 -0
- package/prompts/default-owner-behavior.md +27 -0
- package/prompts/platform.md +13 -0
- package/setup-entry.ts +4 -0
- package/skills/clawchat/SKILL.md +91 -0
- package/src/api-client.test.ts +827 -0
- package/src/api-client.ts +414 -0
- package/src/api-types.ts +146 -0
- package/src/channel.outbound.test.ts +433 -0
- package/src/channel.setup.ts +145 -0
- package/src/channel.test.ts +262 -0
- package/src/channel.ts +81 -0
- package/src/clawchat-memory.test.ts +480 -0
- package/src/clawchat-memory.ts +533 -0
- package/src/clawchat-metadata.test.ts +477 -0
- package/src/clawchat-metadata.ts +429 -0
- package/src/client.test.ts +169 -0
- package/src/client.ts +56 -0
- package/src/commands.test.ts +39 -0
- package/src/commands.ts +41 -0
- package/src/config.test.ts +344 -0
- package/src/config.ts +404 -0
- package/src/group-message-coalescer.test.ts +237 -0
- package/src/group-message-coalescer.ts +171 -0
- package/src/inbound.test.ts +508 -0
- package/src/inbound.ts +278 -0
- package/src/llm-context-debug.test.ts +55 -0
- package/src/llm-context-debug.ts +139 -0
- package/src/login.runtime.test.ts +737 -0
- package/src/login.runtime.ts +277 -0
- package/src/manifest.test.ts +352 -0
- package/src/media-runtime.test.ts +207 -0
- package/src/media-runtime.ts +152 -0
- package/src/message-mapper.test.ts +201 -0
- package/src/message-mapper.ts +174 -0
- package/src/mock-transport.test.ts +35 -0
- package/src/mock-transport.ts +38 -0
- package/src/outbound.test.ts +1269 -0
- package/src/outbound.ts +803 -0
- package/src/plugin-entry.test.ts +38 -0
- package/src/plugin-prompts.test.ts +94 -0
- package/src/plugin-prompts.ts +107 -0
- package/src/profile-prompt.test.ts +274 -0
- package/src/profile-prompt.ts +351 -0
- package/src/profile-sync.test.ts +539 -0
- package/src/profile-sync.ts +191 -0
- package/src/prompt-injection.test.ts +39 -0
- package/src/prompt-injection.ts +45 -0
- package/src/protocol-types.test.ts +69 -0
- package/src/protocol-types.ts +296 -0
- package/src/protocol-types.typecheck.ts +89 -0
- package/src/protocol.test.ts +39 -0
- package/src/protocol.ts +42 -0
- package/src/reply-dispatcher.test.ts +1324 -0
- package/src/reply-dispatcher.ts +555 -0
- package/src/runtime.test.ts +4719 -0
- package/src/runtime.ts +1493 -0
- package/src/scripts.test.ts +85 -0
- package/src/storage.test.ts +560 -0
- package/src/storage.ts +807 -0
- package/src/terminal-send.test.ts +81 -0
- package/src/terminal-send.ts +56 -0
- package/src/tools-schema.ts +337 -0
- package/src/tools.test.ts +933 -0
- package/src/tools.ts +1185 -0
- package/src/ws-alignment.test.ts +103 -0
- package/src/ws-alignment.ts +275 -0
- package/src/ws-client.test.ts +1217 -0
- package/src/ws-client.ts +662 -0
- package/src/ws-log.test.ts +32 -0
- package/src/ws-log.ts +31 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ConnectPayload,
|
|
3
|
+
EmptyPayload,
|
|
4
|
+
Envelope,
|
|
5
|
+
Fragment,
|
|
6
|
+
MessagePayload,
|
|
7
|
+
OfflineBatchPayload,
|
|
8
|
+
ProtocolFragment,
|
|
9
|
+
Sender,
|
|
10
|
+
StreamAddPayload,
|
|
11
|
+
StreamDonePayload,
|
|
12
|
+
} from "./protocol-types.ts";
|
|
13
|
+
|
|
14
|
+
type Assert<T extends true> = T;
|
|
15
|
+
type IsAssignable<From, To> = [From] extends [To] ? true : false;
|
|
16
|
+
type IsEqual<Actual, Expected> =
|
|
17
|
+
(<T>() => T extends Actual ? 1 : 2) extends <T>() => T extends Expected ? 1 : 2
|
|
18
|
+
? true
|
|
19
|
+
: false;
|
|
20
|
+
type Not<T extends boolean> = T extends true ? false : true;
|
|
21
|
+
|
|
22
|
+
type _SenderRequiresType = Assert<
|
|
23
|
+
Not<IsAssignable<{ id: string; nick_name: string }, Sender>>
|
|
24
|
+
>;
|
|
25
|
+
|
|
26
|
+
type _MaterializedMessageIdMayBeOmittedOnUplink = Assert<
|
|
27
|
+
IsAssignable<
|
|
28
|
+
{ message_mode: string; message: MessagePayload["message"] },
|
|
29
|
+
MessagePayload
|
|
30
|
+
>
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
type _KnownTextFragmentRequiresText = Assert<
|
|
34
|
+
Not<IsAssignable<{ kind: "text" }, Fragment>>
|
|
35
|
+
>;
|
|
36
|
+
|
|
37
|
+
type _KnownMentionFragmentRequiresUserId = Assert<
|
|
38
|
+
Not<IsAssignable<{ kind: "mention" }, Fragment>>
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
type _UnknownWireFragmentsAreRepresentable = Assert<
|
|
42
|
+
IsAssignable<{ kind: "sticker"; sticker_id: string }, ProtocolFragment>
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
type _MaterializedMessageBodyUsesWireFragments = Assert<
|
|
46
|
+
IsEqual<MessagePayload["message"]["body"]["fragments"], Fragment[]>
|
|
47
|
+
>;
|
|
48
|
+
|
|
49
|
+
type _MaterializedMessageRejectsMalformedKnownFragments = Assert<
|
|
50
|
+
Not<
|
|
51
|
+
IsAssignable<
|
|
52
|
+
{
|
|
53
|
+
message_mode: string;
|
|
54
|
+
message: {
|
|
55
|
+
body: { fragments: [{ kind: "text" }] };
|
|
56
|
+
context: MessagePayload["message"]["context"];
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
MessagePayload
|
|
60
|
+
>
|
|
61
|
+
>
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
type _StreamCompletionUsesWireFragments = Assert<
|
|
65
|
+
IsEqual<StreamDonePayload["fragments"], Fragment[]>
|
|
66
|
+
>;
|
|
67
|
+
|
|
68
|
+
type _StreamCompletionPayloadIsFlat = Assert<
|
|
69
|
+
Not<"message" extends keyof StreamDonePayload ? true : false>
|
|
70
|
+
>;
|
|
71
|
+
|
|
72
|
+
type _ConnectPayloadAllowsCanonicalMinimum = Assert<
|
|
73
|
+
IsAssignable<{ token: string; nonce: string }, ConnectPayload>
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
type _StreamAddPayloadAllowsCanonicalMinimum = Assert<
|
|
77
|
+
IsAssignable<
|
|
78
|
+
{ message_id: string; sequence: number; fragments: Fragment[] },
|
|
79
|
+
StreamAddPayload
|
|
80
|
+
>
|
|
81
|
+
>;
|
|
82
|
+
|
|
83
|
+
type _OfflineBatchPayloadCarriesEnvelopes = Assert<
|
|
84
|
+
IsAssignable<{ batch_id: number; items: Envelope[]; remaining: number }, OfflineBatchPayload>
|
|
85
|
+
>;
|
|
86
|
+
|
|
87
|
+
type _ControlPayloadsAreEmptyObjects = Assert<
|
|
88
|
+
IsAssignable<Record<string, never>, EmptyPayload>
|
|
89
|
+
>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { isInboundMessagePayload, hasRenderableText } from "./protocol.ts";
|
|
3
|
+
|
|
4
|
+
describe("clawchat-plugin-openclaw protocol guards", () => {
|
|
5
|
+
it("accepts a well-formed downlink message payload", () => {
|
|
6
|
+
const payload = {
|
|
7
|
+
message_id: "m1",
|
|
8
|
+
message_mode: "normal",
|
|
9
|
+
message: {
|
|
10
|
+
body: { fragments: [{ kind: "text", text: "hi" }] },
|
|
11
|
+
context: { mentions: [], reply: null },
|
|
12
|
+
streaming: {
|
|
13
|
+
status: "static",
|
|
14
|
+
sequence: 0,
|
|
15
|
+
mutation_policy: "sealed",
|
|
16
|
+
started_at: null,
|
|
17
|
+
completed_at: null,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
expect(isInboundMessagePayload(payload)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("rejects payload missing message_id", () => {
|
|
25
|
+
expect(isInboundMessagePayload({ message: { body: { fragments: [] } } })).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("rejects payload missing message", () => {
|
|
29
|
+
expect(isInboundMessagePayload({ message_id: "m1" })).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("detects renderable text", () => {
|
|
33
|
+
expect(hasRenderableText({ body: { fragments: [{ kind: "text", text: "hi" }] } })).toBe(true);
|
|
34
|
+
expect(hasRenderableText({ body: { fragments: [{ kind: "image", url: "x" }] } })).toBe(true);
|
|
35
|
+
expect(hasRenderableText({ body: { fragments: [{ kind: "file", url: "x" }] } })).toBe(true);
|
|
36
|
+
expect(hasRenderableText({ body: { fragments: [{ kind: "text", text: " " }] } })).toBe(false);
|
|
37
|
+
expect(hasRenderableText({ body: { fragments: [{ kind: "image" }] } })).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
});
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local narrow guards for inbound protocol envelopes.
|
|
3
|
+
*
|
|
4
|
+
* Raw client events hand us `Envelope<unknown>`. Before casting the
|
|
5
|
+
* payload to `MessagePayload` we run these cheap structural checks
|
|
6
|
+
* so runtime errors surface as skipped messages, not crashes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export function isInboundMessagePayload(payload: unknown): payload is {
|
|
10
|
+
message_id: string;
|
|
11
|
+
message_mode: string;
|
|
12
|
+
message: {
|
|
13
|
+
body: { fragments: Array<Record<string, unknown>> };
|
|
14
|
+
context: { mentions: unknown[]; reply: unknown };
|
|
15
|
+
};
|
|
16
|
+
} {
|
|
17
|
+
if (!payload || typeof payload !== "object") return false;
|
|
18
|
+
const p = payload as Record<string, unknown>;
|
|
19
|
+
if (typeof p.message_id !== "string" || !p.message_id) return false;
|
|
20
|
+
if (typeof p.message !== "object" || p.message === null) return false;
|
|
21
|
+
const m = p.message as Record<string, unknown>;
|
|
22
|
+
if (typeof m.body !== "object" || m.body === null) return false;
|
|
23
|
+
const body = m.body as Record<string, unknown>;
|
|
24
|
+
if (!Array.isArray(body.fragments)) return false;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function hasRenderableText(message: {
|
|
29
|
+
body?: { fragments?: Array<Record<string, unknown>> };
|
|
30
|
+
}): boolean {
|
|
31
|
+
const fragments = message?.body?.fragments ?? [];
|
|
32
|
+
return fragments.some(
|
|
33
|
+
(f) =>
|
|
34
|
+
(((f as { kind?: string }).kind === "text" &&
|
|
35
|
+
typeof (f as { text?: unknown }).text === "string" &&
|
|
36
|
+
(f as { text: string }).text.trim().length > 0) ||
|
|
37
|
+
(typeof (f as { kind?: unknown }).kind === "string" &&
|
|
38
|
+
["image", "file", "audio", "video"].includes((f as { kind: string }).kind) &&
|
|
39
|
+
typeof (f as { url?: unknown }).url === "string" &&
|
|
40
|
+
(f as { url: string }).url.trim().length > 0)),
|
|
41
|
+
);
|
|
42
|
+
}
|