@relaymessenger/openclaw-plugin 0.4.7-staging.14 → 0.4.7-staging.16

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 CHANGED
@@ -12,6 +12,22 @@ delivers events over its v1 WebSocket, and the plugin sends replies through
12
12
  the Relay v1 REST Message API. The plugin imports `@relaymessenger/sdk`; it
13
13
  does not contain a copied Relay client or protocol implementation.
14
14
 
15
+ ## Selection, coming soon
16
+
17
+ These are local candidate sources, not a claim that the published package or
18
+ hosted API supports selection yet.
19
+
20
+ End the final answer with a `selection` JSON fence after the question.
21
+ `BodyForAgent` carries structured response and rich-message JSON; `RawBody` and
22
+ `CommandBody` retain readable text. Stable values are not executable commands.
23
+
24
+ New human reply text is literal `• ` + each selected source label joined with
25
+ `\n`, followed by `selection_response` metadata in source-option order. Dispatch
26
+ with `selected_values` and the explicit source target, never label parsing.
27
+ Exact legacy comma-joined text remains a server compatibility input. Tapping a
28
+ selected option deselects it locally; the sole submit action is a centered
29
+ compact light-blue Send button. iOS checked circles are presentation only.
30
+
15
31
  ## Install
16
32
 
17
33
  ```bash
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "relayServer": {
3
3
  "repository": "RelayMessenger/Relay-Server",
4
- "commit": "a25111520f7fc92c25ecd945d1dfc9afa9f60a1f",
4
+ "commit": "b89d90c0b325a94402456de2e91a59b66c7d9603",
5
5
  "openapiPath": "contracts/developer/openapi.yaml",
6
- "sha256": "9f3e662a13cd0e6b16a52fba4b53c75fe5817d134dcf152e00b054699c37839c"
6
+ "sha256": "503646bcd2b3fbc1170046880ef39f4219592fb9e5d9d00cef022b801abbb4bb"
7
7
  },
8
8
  "relaySdk": {
9
9
  "package": "@relaymessenger/sdk",
10
- "version": "0.3.6-staging.12",
11
- "integrity": "sha512-Om/9eiTFnXuiBUIBbA85hahE4f+ry7sOKq/ZXGJ208BoqXPKD5+LZkCQ4NG08kzDkXEbwc5Lsldk0GCpXDNnLQ==",
10
+ "version": "0.3.6-staging.14",
11
+ "integrity": "sha512-0zUTLWzkFoW24w62yeSknuSKg27EeM8fmemxAsoyrWtCv5myxTm5h/FFFbnv8Guie2XxNXnwrMix6ullU/8FLw==",
12
12
  "operationsSha256": "1691197e8db0705bcad5d0704042e98ed8c3f2262875b5e773b0e12ae0a33022",
13
- "workspaceOpenapiSha256": "9f3e662a13cd0e6b16a52fba4b53c75fe5817d134dcf152e00b054699c37839c",
13
+ "workspaceOpenapiSha256": "503646bcd2b3fbc1170046880ef39f4219592fb9e5d9d00cef022b801abbb4bb",
14
14
  "usedOperations": [
15
15
  {
16
16
  "method": "GET",
@@ -249,7 +249,7 @@ export const relayChannelPlugin = createChatChannelPlugin({
249
249
  deliveryQueueId: ctx.deliveryQueueId,
250
250
  deliveryPartIndex: ctx.deliveryPartIndex,
251
251
  }),
252
- onButtonsError: (error) => ctx.log?.warn?.(`relay: buttons block left as text: ${error}`),
252
+ onButtonsError: (error) => ctx.log?.warn?.(`relay: component block left as text: ${error}`),
253
253
  ...(ctx.onPlatformSendDispatch
254
254
  ? { onPlatformSendDispatch: ctx.onPlatformSendDispatch }
255
255
  : {}),
@@ -1,3 +1,4 @@
1
+ import { BUTTONS_GUIDANCE, BUTTONS_BLOCK_INSTRUCTION, LINK_LINE_INSTRUCTION, SELECTION_GUIDANCE, SELECTION_BLOCK_INSTRUCTION, selectionReplyContext } from "@relaymessenger/sdk";
1
2
  import { buildChannelInboundEventContext, resolveChannelInboundRouteEnvelope, } from "openclaw/plugin-sdk/channel-inbound";
2
3
  import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
3
4
  import { bindIngressLifecycleToReplyOptions } from "openclaw/plugin-sdk/channel-outbound";
@@ -177,7 +178,9 @@ export async function dispatchRelayEvent(params) {
177
178
  message: {
178
179
  inboundEventKind: "user_request",
179
180
  body,
180
- bodyForAgent: facts.text,
181
+ bodyForAgent: [facts.text, selectionReplyContext(facts.selection, facts.richMessage),
182
+ `${BUTTONS_BLOCK_INSTRUCTION} ${LINK_LINE_INSTRUCTION} ${BUTTONS_GUIDANCE} ${SELECTION_BLOCK_INSTRUCTION} ${SELECTION_GUIDANCE}`,
183
+ ].filter(Boolean).join("\n\n"),
181
184
  rawBody: facts.text,
182
185
  commandBody: facts.text,
183
186
  },
@@ -1,3 +1,4 @@
1
+ import { selectionReply, selectionReplyContext } from "@relaymessenger/sdk";
1
2
  function renderPart(part) {
2
3
  switch (part.type) {
3
4
  case "text":
@@ -11,6 +12,8 @@ function renderPart(part) {
11
12
  // The agent's own buttons part reads as nothing, as on the server; its
12
13
  // question is the text beside it. A tap arrives as ordinary text.
13
14
  case "buttons":
15
+ case "selection":
16
+ case "selection_response":
14
17
  return undefined;
15
18
  }
16
19
  }
@@ -41,7 +44,9 @@ export function buildRelayInboundFacts(event) {
41
44
  event.data.sender_handle.kind !== "agent")
42
45
  return null;
43
46
  const text = renderRelayMessageParts(event.data.parts);
44
- if (!text.trim())
47
+ const message = { parts: event.data.parts, ...(event.data.reply_to ? { reply_to: event.data.reply_to } : {}) };
48
+ const richMessage = selectionReplyContext(undefined, message) ? message : undefined;
49
+ if (!text.trim() && !richMessage)
45
50
  return null;
46
51
  const mentionHandles = event.data.parts.flatMap((part) => part.type === "text" &&
47
52
  typeof part.mention === "string" &&
@@ -50,7 +55,10 @@ export function buildRelayInboundFacts(event) {
50
55
  : []);
51
56
  const timestampValue = event.data.sent_at ?? event.created_at;
52
57
  const timestamp = Date.parse(timestampValue);
58
+ const selection = selectionReply(event.data.parts, event.data.reply_to);
53
59
  return {
60
+ ...(selection ? { selection } : {}),
61
+ ...(richMessage ? { richMessage } : {}),
54
62
  eventId: event.event_id,
55
63
  messageId: event.data.id,
56
64
  chatId: event.data.chat.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relaymessenger/openclaw-plugin",
3
- "version": "0.4.7-staging.14",
3
+ "version": "0.4.7-staging.16",
4
4
  "description": "Native Relay channel plugin for OpenClaw",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,7 +37,7 @@
37
37
  "prepack": "npm run build"
38
38
  },
39
39
  "dependencies": {
40
- "@relaymessenger/sdk": "0.3.6-staging.12"
40
+ "@relaymessenger/sdk": "0.3.6-staging.14"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^26.6.2",
package/src/channel.ts CHANGED
@@ -331,7 +331,7 @@ export const relayChannelPlugin: ChannelPlugin<ResolvedRelayAccount> =
331
331
  }),
332
332
  onButtonsError: (error) =>
333
333
  (ctx as { log?: { warn?: (message: string) => void } }).log?.warn?.(
334
- `relay: buttons block left as text: ${error}`,
334
+ `relay: component block left as text: ${error}`,
335
335
  ),
336
336
  ...(ctx.onPlatformSendDispatch
337
337
  ? { onPlatformSendDispatch: ctx.onPlatformSendDispatch }
package/src/dispatch.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { BUTTONS_GUIDANCE, BUTTONS_BLOCK_INSTRUCTION, LINK_LINE_INSTRUCTION, SELECTION_GUIDANCE, SELECTION_BLOCK_INSTRUCTION, selectionReplyContext } from "@relaymessenger/sdk";
1
2
  import type {
2
3
  Message,
3
4
  Relay,
@@ -243,7 +244,9 @@ export async function dispatchRelayEvent(params: {
243
244
  message: {
244
245
  inboundEventKind: "user_request",
245
246
  body,
246
- bodyForAgent: facts.text,
247
+ bodyForAgent: [facts.text, selectionReplyContext(facts.selection, facts.richMessage),
248
+ `${BUTTONS_BLOCK_INSTRUCTION} ${LINK_LINE_INSTRUCTION} ${BUTTONS_GUIDANCE} ${SELECTION_BLOCK_INSTRUCTION} ${SELECTION_GUIDANCE}`,
249
+ ].filter(Boolean).join("\n\n"),
247
250
  rawBody: facts.text,
248
251
  commandBody: facts.text,
249
252
  },
package/src/inbound.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { selectionReply, selectionReplyContext } from "@relaymessenger/sdk";
1
2
  import type {
2
3
  MessagePartResponse,
3
4
  RelayWebhookEvent,
@@ -20,6 +21,8 @@ function renderPart(part: MessagePartResponse): string | undefined {
20
21
  // The agent's own buttons part reads as nothing, as on the server; its
21
22
  // question is the text beside it. A tap arrives as ordinary text.
22
23
  case "buttons":
24
+ case "selection":
25
+ case "selection_response":
23
26
  return undefined;
24
27
  }
25
28
  }
@@ -61,7 +64,9 @@ export function buildRelayInboundFacts(
61
64
  ) return null;
62
65
 
63
66
  const text = renderRelayMessageParts(event.data.parts);
64
- if (!text.trim()) return null;
67
+ const message = { parts: event.data.parts, ...(event.data.reply_to ? { reply_to: event.data.reply_to } : {}) };
68
+ const richMessage = selectionReplyContext(undefined, message) ? message : undefined;
69
+ if (!text.trim() && !richMessage) return null;
65
70
 
66
71
  const mentionHandles = event.data.parts.flatMap((part) =>
67
72
  part.type === "text" &&
@@ -72,7 +77,10 @@ export function buildRelayInboundFacts(
72
77
  );
73
78
  const timestampValue = event.data.sent_at ?? event.created_at;
74
79
  const timestamp = Date.parse(timestampValue);
80
+ const selection = selectionReply(event.data.parts, event.data.reply_to);
75
81
  return {
82
+ ...(selection ? { selection } : {}),
83
+ ...(richMessage ? { richMessage } : {}),
76
84
  eventId: event.event_id,
77
85
  messageId: event.data.id,
78
86
  chatId: event.data.chat.id,
package/src/types.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import type {
2
2
  Chat,
3
+ SelectionReply,
4
+ MessagePartResponse,
5
+ ReplyTo,
3
6
  ChatHandle,
4
7
  MessageWebhookData,
5
8
  Message,
@@ -61,6 +64,8 @@ export type RelayMessageReceivedEvent = RelayWebhookEnvelope<
61
64
  >;
62
65
 
63
66
  export type RelayInboundFacts = {
67
+ selection?: SelectionReply;
68
+ richMessage?: { parts: MessagePartResponse[]; reply_to?: ReplyTo | null };
64
69
  eventId: string;
65
70
  messageId: string;
66
71
  chatId: string;