@relaymessenger/openclaw-plugin 0.4.7-staging.40 → 0.4.7-staging.42
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/contracts/relay-v1.lock.json +5 -5
- package/dist/src/dispatch.js +24 -7
- package/package.json +2 -2
- package/src/dispatch.ts +27 -7
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"relayServer": {
|
|
3
3
|
"repository": "RelayMessenger/Relay-Server",
|
|
4
|
-
"commit": "
|
|
4
|
+
"commit": "42356390fcfa2a198da1dc35e753e72bb71b2b8d",
|
|
5
5
|
"openapiPath": "contracts/developer/openapi.yaml",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "fc4b42294e67a93d4e92e7cddced157bedcfff6e6c163aeed8b443d194c1cf1d"
|
|
7
7
|
},
|
|
8
8
|
"relaySdk": {
|
|
9
9
|
"package": "@relaymessenger/sdk",
|
|
10
|
-
"version": "0.3.6-staging.
|
|
11
|
-
"integrity": "sha512-
|
|
10
|
+
"version": "0.3.6-staging.49",
|
|
11
|
+
"integrity": "sha512-oDAYkxqXDhXcVlWS5asq3Bdlt5GAsQgvZv9/e25wFgr5J5Arr3lUjQdjvN2ZeQLDZ5h7sF39OwmPGjYDlTvQMg==",
|
|
12
12
|
"operationsSha256": "3e09d345c249709d942d2efb89ed7e84934c19552767d8f4438eb086dd917e1c",
|
|
13
|
-
"workspaceOpenapiSha256": "
|
|
13
|
+
"workspaceOpenapiSha256": "fc4b42294e67a93d4e92e7cddced157bedcfff6e6c163aeed8b443d194c1cf1d",
|
|
14
14
|
"usedOperations": [
|
|
15
15
|
{
|
|
16
16
|
"method": "GET",
|
package/dist/src/dispatch.js
CHANGED
|
@@ -60,8 +60,10 @@ async function readReplyTarget(params) {
|
|
|
60
60
|
* OpenClaw's own reply context, `supplemental.quote`, which it renders to the
|
|
61
61
|
* model as "Reply target of current user message" (id, sender, body), as its
|
|
62
62
|
* Telegram channel fills it from Telegram's `reply_to_message`. A reply names
|
|
63
|
-
* one bubble:
|
|
64
|
-
*
|
|
63
|
+
* one bubble: a multipart target is narrowed to the swiped part, the rule
|
|
64
|
+
* Relay's iOS app uses to draw the quote (the SDK's `replyTargetParts`); a
|
|
65
|
+
* tap or a selection answer names a part with no words, so it keeps the
|
|
66
|
+
* whole Message.
|
|
65
67
|
*/
|
|
66
68
|
export function relayReplyQuote(facts, target) {
|
|
67
69
|
if (!target || target.chat_id !== facts.chatId)
|
|
@@ -70,7 +72,7 @@ export function relayReplyQuote(facts, target) {
|
|
|
70
72
|
const swiped = parts.length > 1 && facts.replyToPartIndex !== undefined
|
|
71
73
|
? parts[facts.replyToPartIndex]
|
|
72
74
|
: undefined;
|
|
73
|
-
const body = renderRelayMessageParts(swiped ? [swiped] : parts);
|
|
75
|
+
const body = renderRelayMessageParts(swiped && swiped.type !== "buttons" && swiped.type !== "selection" ? [swiped] : parts);
|
|
74
76
|
const sender = target.from_handle?.display_name?.trim()
|
|
75
77
|
|| target.from_handle?.handle
|
|
76
78
|
|| target.from
|
|
@@ -82,6 +84,16 @@ export function relayReplyQuote(facts, target) {
|
|
|
82
84
|
senderAllowed: true,
|
|
83
85
|
};
|
|
84
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Whether the part a reply names may itself be replied to. A tap names the
|
|
89
|
+
* agent's buttons part and a selection answer its selection part; no reply may
|
|
90
|
+
* point at those (Relay v1 ReplyTo.part_index), so a person's tap keeps its
|
|
91
|
+
* own Message as the reply target.
|
|
92
|
+
*/
|
|
93
|
+
function repliable(target, partIndex) {
|
|
94
|
+
const part = target?.parts?.[partIndex ?? 0];
|
|
95
|
+
return part !== undefined && part.type !== "buttons" && part.type !== "selection";
|
|
96
|
+
}
|
|
85
97
|
/**
|
|
86
98
|
* The answer to another agent names the Message it answers: the model's own
|
|
87
99
|
* reply target when it chose one, else the agent's Message. Where no reply may
|
|
@@ -202,18 +214,23 @@ export async function dispatchRelayEvent(params) {
|
|
|
202
214
|
params.warn?.(`relay: Contact @${facts.handle} did not pass OpenClaw ingress (${access.ingress.decision}:${access.ingress.reasonCode})`);
|
|
203
215
|
return;
|
|
204
216
|
}
|
|
205
|
-
// An agent's reply target is only its own Message (agentReplyLink)
|
|
206
|
-
// person's is the Message they replied
|
|
217
|
+
// An agent's reply target is only its own Message (agentReplyLink). A
|
|
218
|
+
// person's is the Message they replied to, OpenClaw's ReplyToId, as its
|
|
219
|
+
// Telegram channel sets `reply.replyToId` to Telegram's reply_to_message:
|
|
220
|
+
// the prompt names it beside the quote. A tap or a selection answer names a
|
|
221
|
+
// part no reply may target, so it keeps the person's own Message.
|
|
222
|
+
const quote = relayReplyQuote(facts, repliedTo);
|
|
207
223
|
const replyTarget = facts.fromAgent
|
|
208
224
|
? facts.agentReplyLink
|
|
209
|
-
:
|
|
225
|
+
: quote && repliable(repliedTo, facts.replyToPartIndex)
|
|
226
|
+
? quote.id
|
|
227
|
+
: facts.replyAnchorId ?? facts.replyToId;
|
|
210
228
|
const body = buildEnvelope({
|
|
211
229
|
channel: "Relay",
|
|
212
230
|
from: `${facts.displayName} (@${facts.handle})`,
|
|
213
231
|
...(facts.timestamp ? { timestamp: facts.timestamp } : {}),
|
|
214
232
|
body: facts.text,
|
|
215
233
|
});
|
|
216
|
-
const quote = relayReplyQuote(facts, repliedTo);
|
|
217
234
|
const ctxPayload = buildChannelInboundEventContext({
|
|
218
235
|
channel: "relay",
|
|
219
236
|
accountId: route.accountId ?? params.account.accountId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relaymessenger/openclaw-plugin",
|
|
3
|
-
"version": "0.4.7-staging.
|
|
3
|
+
"version": "0.4.7-staging.42",
|
|
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.
|
|
40
|
+
"@relaymessenger/sdk": "0.3.6-staging.49"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^26.6.2",
|
package/src/dispatch.ts
CHANGED
|
@@ -114,8 +114,10 @@ async function readReplyTarget(params: {
|
|
|
114
114
|
* OpenClaw's own reply context, `supplemental.quote`, which it renders to the
|
|
115
115
|
* model as "Reply target of current user message" (id, sender, body), as its
|
|
116
116
|
* Telegram channel fills it from Telegram's `reply_to_message`. A reply names
|
|
117
|
-
* one bubble:
|
|
118
|
-
*
|
|
117
|
+
* one bubble: a multipart target is narrowed to the swiped part, the rule
|
|
118
|
+
* Relay's iOS app uses to draw the quote (the SDK's `replyTargetParts`); a
|
|
119
|
+
* tap or a selection answer names a part with no words, so it keeps the
|
|
120
|
+
* whole Message.
|
|
119
121
|
*/
|
|
120
122
|
export function relayReplyQuote(
|
|
121
123
|
facts: Pick<RelayInboundFacts, "chatId" | "replyToPartIndex">,
|
|
@@ -126,7 +128,9 @@ export function relayReplyQuote(
|
|
|
126
128
|
const swiped = parts.length > 1 && facts.replyToPartIndex !== undefined
|
|
127
129
|
? parts[facts.replyToPartIndex]
|
|
128
130
|
: undefined;
|
|
129
|
-
const body = renderRelayMessageParts(
|
|
131
|
+
const body = renderRelayMessageParts(
|
|
132
|
+
swiped && swiped.type !== "buttons" && swiped.type !== "selection" ? [swiped] : parts,
|
|
133
|
+
);
|
|
130
134
|
const sender = target.from_handle?.display_name?.trim()
|
|
131
135
|
|| target.from_handle?.handle
|
|
132
136
|
|| target.from
|
|
@@ -139,6 +143,17 @@ export function relayReplyQuote(
|
|
|
139
143
|
};
|
|
140
144
|
}
|
|
141
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Whether the part a reply names may itself be replied to. A tap names the
|
|
148
|
+
* agent's buttons part and a selection answer its selection part; no reply may
|
|
149
|
+
* point at those (Relay v1 ReplyTo.part_index), so a person's tap keeps its
|
|
150
|
+
* own Message as the reply target.
|
|
151
|
+
*/
|
|
152
|
+
function repliable(target: Message | undefined, partIndex: number | undefined): boolean {
|
|
153
|
+
const part = target?.parts?.[partIndex ?? 0];
|
|
154
|
+
return part !== undefined && part.type !== "buttons" && part.type !== "selection";
|
|
155
|
+
}
|
|
156
|
+
|
|
142
157
|
/**
|
|
143
158
|
* The answer to another agent names the Message it answers: the model's own
|
|
144
159
|
* reply target when it chose one, else the agent's Message. Where no reply may
|
|
@@ -281,18 +296,23 @@ export async function dispatchRelayEvent(params: {
|
|
|
281
296
|
return;
|
|
282
297
|
}
|
|
283
298
|
|
|
284
|
-
// An agent's reply target is only its own Message (agentReplyLink)
|
|
285
|
-
// person's is the Message they replied
|
|
299
|
+
// An agent's reply target is only its own Message (agentReplyLink). A
|
|
300
|
+
// person's is the Message they replied to, OpenClaw's ReplyToId, as its
|
|
301
|
+
// Telegram channel sets `reply.replyToId` to Telegram's reply_to_message:
|
|
302
|
+
// the prompt names it beside the quote. A tap or a selection answer names a
|
|
303
|
+
// part no reply may target, so it keeps the person's own Message.
|
|
304
|
+
const quote = relayReplyQuote(facts, repliedTo);
|
|
286
305
|
const replyTarget = facts.fromAgent
|
|
287
306
|
? facts.agentReplyLink
|
|
288
|
-
:
|
|
307
|
+
: quote && repliable(repliedTo, facts.replyToPartIndex)
|
|
308
|
+
? quote.id
|
|
309
|
+
: facts.replyAnchorId ?? facts.replyToId;
|
|
289
310
|
const body = buildEnvelope({
|
|
290
311
|
channel: "Relay",
|
|
291
312
|
from: `${facts.displayName} (@${facts.handle})`,
|
|
292
313
|
...(facts.timestamp ? { timestamp: facts.timestamp } : {}),
|
|
293
314
|
body: facts.text,
|
|
294
315
|
});
|
|
295
|
-
const quote = relayReplyQuote(facts, repliedTo);
|
|
296
316
|
const ctxPayload = buildChannelInboundEventContext({
|
|
297
317
|
channel: "relay",
|
|
298
318
|
accountId: route.accountId ?? params.account.accountId,
|