@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.
@@ -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: 'sess-1'})
79
+ await handler({}, {sessionKey: 'agent:clawly:main'})
80
80
 
81
81
  expect(logs).toContainEqual({
82
82
  level: 'info',
83
- msg: expect.stringContaining('notified (session=sess-1)'),
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: 'sess-1'})
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: 'sess-1'})
105
- await handler({}, {sessionKey: 'sess-1'})
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: 'sess-1', agentId: 'luna'})
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: 'sess-1'})
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: 'sess-1'},
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: 'sess-1'})
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: 'sess-1'},
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: 'sess-1'},
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: 'sess-1'},
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: 'sess-1'},
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: 'sess-1'},
354
+ {sessionKey: 'agent:clawly:main'},
342
355
  )
343
356
 
344
357
  expect(logs).toContainEqual({
345
358
  level: 'info',
346
- msg: expect.stringContaining('notified (session=sess-1)'),
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: 'sess-1'})
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=sess-1)'),
371
+ msg: expect.stringContaining('notified (session=agent:clawly:main)'),
359
372
  })
360
373
  })
361
374
  })
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2en/clawly-plugins",
3
- "version": "1.21.3",
3
+ "version": "1.21.5",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "repository": {
package/session-setup.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  /**
2
- * On plugin init, patches openclaw.json to set `session.dmScope` to
3
- * "per-channel-peer". This isolates Telegram (and other channel) DMs from the
4
- * Clawly mobile session so messages don't leak across channels.
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
- if (session.dmScope === DESIRED_DM_SCOPE) {
30
- return
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
- session.dmScope = DESIRED_DM_SCOPE
34
- config.session = session
51
+ if (!dirty) return
35
52
 
36
53
  try {
37
54
  writeOpenclawConfig(configPath, config)
38
- api.logger.info(`Session: set dmScope to "${DESIRED_DM_SCOPE}".`)
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
  }