@2en/clawly-plugins 1.21.3 → 1.21.5
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/gateway/offline-push.test.ts +30 -17
- package/gateway/offline-push.ts +7 -0
- package/package.json +1 -1
- package/session-setup.ts +27 -8
|
@@ -76,11 +76,11 @@ describe('offline-push', () => {
|
|
|
76
76
|
registerOfflinePush(api)
|
|
77
77
|
|
|
78
78
|
const handler = handlers.get('agent_end')!
|
|
79
|
-
await handler({}, {sessionKey: '
|
|
79
|
+
await handler({}, {sessionKey: 'agent:clawly:main'})
|
|
80
80
|
|
|
81
81
|
expect(logs).toContainEqual({
|
|
82
82
|
level: 'info',
|
|
83
|
-
msg: expect.stringContaining('notified (session=
|
|
83
|
+
msg: expect.stringContaining('notified (session=agent:clawly:main)'),
|
|
84
84
|
})
|
|
85
85
|
})
|
|
86
86
|
|
|
@@ -89,7 +89,7 @@ describe('offline-push', () => {
|
|
|
89
89
|
const {api, logs, handlers} = createMockApi()
|
|
90
90
|
registerOfflinePush(api)
|
|
91
91
|
|
|
92
|
-
await handlers.get('agent_end')!({}, {sessionKey: '
|
|
92
|
+
await handlers.get('agent_end')!({}, {sessionKey: 'agent:clawly:main'})
|
|
93
93
|
|
|
94
94
|
expect(logs.filter((l) => l.msg.includes('notified'))).toHaveLength(0)
|
|
95
95
|
expect(logs.filter((l) => l.msg.includes('skipped'))).toHaveLength(0)
|
|
@@ -101,12 +101,25 @@ describe('offline-push', () => {
|
|
|
101
101
|
|
|
102
102
|
const handler = handlers.get('agent_end')!
|
|
103
103
|
|
|
104
|
-
await handler({}, {sessionKey: '
|
|
105
|
-
await handler({}, {sessionKey: '
|
|
104
|
+
await handler({}, {sessionKey: 'agent:clawly:main'})
|
|
105
|
+
await handler({}, {sessionKey: 'agent:clawly:main'})
|
|
106
106
|
|
|
107
107
|
expect(logs.filter((l) => l.msg.includes('notified'))).toHaveLength(2)
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
+
test('skips push for non-main session (e.g. telegram)', async () => {
|
|
111
|
+
const {api, logs, handlers} = createMockApi()
|
|
112
|
+
registerOfflinePush(api)
|
|
113
|
+
|
|
114
|
+
await handlers.get('agent_end')!({}, {sessionKey: 'agent:clawly:telegram:12345'})
|
|
115
|
+
|
|
116
|
+
expect(logs).toContainEqual({
|
|
117
|
+
level: 'info',
|
|
118
|
+
msg: expect.stringContaining('skipped (non-main session: agent:clawly:telegram:12345)'),
|
|
119
|
+
})
|
|
120
|
+
expect(logs.filter((l) => l.msg.includes('notified'))).toHaveLength(0)
|
|
121
|
+
})
|
|
122
|
+
|
|
110
123
|
test('sends push when ctx is missing', async () => {
|
|
111
124
|
const {api, logs, handlers} = createMockApi()
|
|
112
125
|
registerOfflinePush(api)
|
|
@@ -124,7 +137,7 @@ describe('offline-push', () => {
|
|
|
124
137
|
const {api, handlers} = createMockApi()
|
|
125
138
|
registerOfflinePush(api)
|
|
126
139
|
|
|
127
|
-
await handlers.get('agent_end')!({}, {sessionKey: '
|
|
140
|
+
await handlers.get('agent_end')!({}, {sessionKey: 'agent:clawly:main', agentId: 'luna'})
|
|
128
141
|
|
|
129
142
|
expect(lastPushOpts?.agentId).toBe('luna')
|
|
130
143
|
})
|
|
@@ -133,7 +146,7 @@ describe('offline-push', () => {
|
|
|
133
146
|
const {api, handlers} = createMockApi()
|
|
134
147
|
registerOfflinePush(api)
|
|
135
148
|
|
|
136
|
-
await handlers.get('agent_end')!({}, {sessionKey: '
|
|
149
|
+
await handlers.get('agent_end')!({}, {sessionKey: 'agent:clawly:main'})
|
|
137
150
|
|
|
138
151
|
expect(lastPushOpts?.title).toBeUndefined()
|
|
139
152
|
})
|
|
@@ -149,7 +162,7 @@ describe('offline-push', () => {
|
|
|
149
162
|
{role: 'assistant', content: 'Hi there! How can I help you today?'},
|
|
150
163
|
],
|
|
151
164
|
},
|
|
152
|
-
{sessionKey: '
|
|
165
|
+
{sessionKey: 'agent:clawly:main'},
|
|
153
166
|
)
|
|
154
167
|
|
|
155
168
|
expect(lastPushOpts?.body).toBe('Hi there! How can I help you today?')
|
|
@@ -159,7 +172,7 @@ describe('offline-push', () => {
|
|
|
159
172
|
const {api, handlers} = createMockApi()
|
|
160
173
|
registerOfflinePush(api)
|
|
161
174
|
|
|
162
|
-
await handlers.get('agent_end')!({}, {sessionKey: '
|
|
175
|
+
await handlers.get('agent_end')!({}, {sessionKey: 'agent:clawly:main'})
|
|
163
176
|
|
|
164
177
|
expect(lastPushOpts?.body).toBe('Your response is ready')
|
|
165
178
|
})
|
|
@@ -170,7 +183,7 @@ describe('offline-push', () => {
|
|
|
170
183
|
|
|
171
184
|
await handlers.get('agent_end')!(
|
|
172
185
|
{messages: [{role: 'user', content: 'Hello'}]},
|
|
173
|
-
{sessionKey: '
|
|
186
|
+
{sessionKey: 'agent:clawly:main'},
|
|
174
187
|
)
|
|
175
188
|
|
|
176
189
|
expect(lastPushOpts?.body).toBe('Your response is ready')
|
|
@@ -291,7 +304,7 @@ describe('offline-push with filtered messages', () => {
|
|
|
291
304
|
|
|
292
305
|
await handlers.get('agent_end')!(
|
|
293
306
|
{messages: [{role: 'assistant', content: 'NO_REPLY'}]},
|
|
294
|
-
{sessionKey: '
|
|
307
|
+
{sessionKey: 'agent:clawly:main'},
|
|
295
308
|
)
|
|
296
309
|
|
|
297
310
|
expect(logs).toContainEqual({
|
|
@@ -307,7 +320,7 @@ describe('offline-push with filtered messages', () => {
|
|
|
307
320
|
|
|
308
321
|
await handlers.get('agent_end')!(
|
|
309
322
|
{messages: [{role: 'assistant', content: 'HEARTBEAT_OK'}]},
|
|
310
|
-
{sessionKey: '
|
|
323
|
+
{sessionKey: 'agent:clawly:main'},
|
|
311
324
|
)
|
|
312
325
|
|
|
313
326
|
expect(logs).toContainEqual({
|
|
@@ -323,7 +336,7 @@ describe('offline-push with filtered messages', () => {
|
|
|
323
336
|
|
|
324
337
|
await handlers.get('agent_end')!(
|
|
325
338
|
{messages: [{role: 'assistant', content: ' '}]},
|
|
326
|
-
{sessionKey: '
|
|
339
|
+
{sessionKey: 'agent:clawly:main'},
|
|
327
340
|
)
|
|
328
341
|
|
|
329
342
|
expect(logs).toContainEqual({
|
|
@@ -338,12 +351,12 @@ describe('offline-push with filtered messages', () => {
|
|
|
338
351
|
|
|
339
352
|
await handlers.get('agent_end')!(
|
|
340
353
|
{messages: [{role: 'assistant', content: 'I finished the task you asked about!'}]},
|
|
341
|
-
{sessionKey: '
|
|
354
|
+
{sessionKey: 'agent:clawly:main'},
|
|
342
355
|
)
|
|
343
356
|
|
|
344
357
|
expect(logs).toContainEqual({
|
|
345
358
|
level: 'info',
|
|
346
|
-
msg: expect.stringContaining('notified (session=
|
|
359
|
+
msg: expect.stringContaining('notified (session=agent:clawly:main)'),
|
|
347
360
|
})
|
|
348
361
|
})
|
|
349
362
|
|
|
@@ -351,11 +364,11 @@ describe('offline-push with filtered messages', () => {
|
|
|
351
364
|
const {api, logs, handlers} = createMockApi()
|
|
352
365
|
registerOfflinePush(api)
|
|
353
366
|
|
|
354
|
-
await handlers.get('agent_end')!({}, {sessionKey: '
|
|
367
|
+
await handlers.get('agent_end')!({}, {sessionKey: 'agent:clawly:main'})
|
|
355
368
|
|
|
356
369
|
expect(logs).toContainEqual({
|
|
357
370
|
level: 'info',
|
|
358
|
-
msg: expect.stringContaining('notified (session=
|
|
371
|
+
msg: expect.stringContaining('notified (session=agent:clawly:main)'),
|
|
359
372
|
})
|
|
360
373
|
})
|
|
361
374
|
})
|
package/gateway/offline-push.ts
CHANGED
|
@@ -111,6 +111,13 @@ export function registerOfflinePush(api: PluginApi) {
|
|
|
111
111
|
const sessionKey = typeof ctx?.sessionKey === 'string' ? ctx.sessionKey : undefined
|
|
112
112
|
const agentId = typeof ctx?.agentId === 'string' ? ctx.agentId : undefined
|
|
113
113
|
|
|
114
|
+
// Only send push for the main clawly mobile session — skip channel
|
|
115
|
+
// sessions (telegram, slack, discord, etc.) which have their own delivery.
|
|
116
|
+
if (sessionKey !== undefined && sessionKey !== 'agent:clawly:main') {
|
|
117
|
+
api.logger.info(`offline-push: skipped (non-main session: ${sessionKey})`)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
114
121
|
const preview = fullText && fullText.length > 140 ? `${fullText.slice(0, 140)}…` : fullText
|
|
115
122
|
const body = preview ?? 'Your response is ready'
|
|
116
123
|
|
package/package.json
CHANGED
package/session-setup.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* On plugin init, patches openclaw.json to
|
|
3
|
-
* "per-channel-peer"
|
|
4
|
-
*
|
|
2
|
+
* On plugin init, patches openclaw.json to:
|
|
3
|
+
* 1. Set `session.dmScope` to "per-channel-peer" — isolates Telegram (and
|
|
4
|
+
* other channel) DMs from the Clawly mobile session.
|
|
5
|
+
* 2. Set `tools.sessions.visibility` to "agent" — lets the agent read
|
|
6
|
+
* conversation history from other sessions under the same agent (e.g.
|
|
7
|
+
* reference recent Telegram chats from the mobile session).
|
|
5
8
|
*
|
|
6
9
|
* Runs synchronously during plugin registration, same pattern as
|
|
7
10
|
* model-gateway-setup.ts.
|
|
@@ -13,6 +16,7 @@ import type {PluginApi} from './index'
|
|
|
13
16
|
import {readOpenclawConfig, resolveStateDir, writeOpenclawConfig} from './model-gateway-setup'
|
|
14
17
|
|
|
15
18
|
const DESIRED_DM_SCOPE = 'per-channel-peer'
|
|
19
|
+
const DESIRED_SESSION_VISIBILITY = 'agent'
|
|
16
20
|
|
|
17
21
|
export function setupSession(api: PluginApi): void {
|
|
18
22
|
const stateDir = resolveStateDir(api)
|
|
@@ -24,18 +28,33 @@ export function setupSession(api: PluginApi): void {
|
|
|
24
28
|
const configPath = path.join(stateDir, 'openclaw.json')
|
|
25
29
|
const config = readOpenclawConfig(configPath)
|
|
26
30
|
|
|
31
|
+
let dirty = false
|
|
32
|
+
|
|
33
|
+
// 1. dmScope
|
|
27
34
|
const session = ((config.session as Record<string, unknown>) ?? {}) as Record<string, unknown>
|
|
35
|
+
if (session.dmScope !== DESIRED_DM_SCOPE) {
|
|
36
|
+
session.dmScope = DESIRED_DM_SCOPE
|
|
37
|
+
config.session = session
|
|
38
|
+
dirty = true
|
|
39
|
+
}
|
|
28
40
|
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
// 2. tools.sessions.visibility
|
|
42
|
+
const tools = ((config.tools as Record<string, unknown>) ?? {}) as Record<string, unknown>
|
|
43
|
+
const sessions = ((tools.sessions as Record<string, unknown>) ?? {}) as Record<string, unknown>
|
|
44
|
+
if (sessions.visibility !== DESIRED_SESSION_VISIBILITY) {
|
|
45
|
+
sessions.visibility = DESIRED_SESSION_VISIBILITY
|
|
46
|
+
tools.sessions = sessions
|
|
47
|
+
config.tools = tools
|
|
48
|
+
dirty = true
|
|
31
49
|
}
|
|
32
50
|
|
|
33
|
-
|
|
34
|
-
config.session = session
|
|
51
|
+
if (!dirty) return
|
|
35
52
|
|
|
36
53
|
try {
|
|
37
54
|
writeOpenclawConfig(configPath, config)
|
|
38
|
-
api.logger.info(
|
|
55
|
+
api.logger.info(
|
|
56
|
+
`Session: dmScope="${DESIRED_DM_SCOPE}", tools.sessions.visibility="${DESIRED_SESSION_VISIBILITY}".`,
|
|
57
|
+
)
|
|
39
58
|
} catch (err) {
|
|
40
59
|
api.logger.error(`Failed to update session config: ${(err as Error).message}`)
|
|
41
60
|
}
|