agent-messenger 2.23.2 → 2.23.4

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.
Files changed (95) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/package.json +1 -1
  3. package/dist/src/platforms/webex/client.d.ts.map +1 -1
  4. package/dist/src/platforms/webex/client.js +28 -16
  5. package/dist/src/platforms/webex/client.js.map +1 -1
  6. package/dist/src/platforms/webex/commands/auth.js +1 -2
  7. package/dist/src/platforms/webex/commands/auth.js.map +1 -1
  8. package/dist/src/platforms/webex/commands/member.d.ts.map +1 -1
  9. package/dist/src/platforms/webex/commands/member.js +2 -3
  10. package/dist/src/platforms/webex/commands/member.js.map +1 -1
  11. package/dist/src/platforms/webex/commands/message.d.ts.map +1 -1
  12. package/dist/src/platforms/webex/commands/message.js +2 -3
  13. package/dist/src/platforms/webex/commands/message.js.map +1 -1
  14. package/dist/src/platforms/webex/commands/whoami.d.ts.map +1 -1
  15. package/dist/src/platforms/webex/commands/whoami.js +2 -3
  16. package/dist/src/platforms/webex/commands/whoami.js.map +1 -1
  17. package/dist/src/platforms/webex/id-normalizer.d.ts +9 -5
  18. package/dist/src/platforms/webex/id-normalizer.d.ts.map +1 -1
  19. package/dist/src/platforms/webex/id-normalizer.js +87 -21
  20. package/dist/src/platforms/webex/id-normalizer.js.map +1 -1
  21. package/dist/src/platforms/webex/index.d.ts +1 -1
  22. package/dist/src/platforms/webex/index.d.ts.map +1 -1
  23. package/dist/src/platforms/webex/index.js.map +1 -1
  24. package/dist/src/platforms/webex/listener.d.ts +10 -9
  25. package/dist/src/platforms/webex/listener.d.ts.map +1 -1
  26. package/dist/src/platforms/webex/listener.js.map +1 -1
  27. package/dist/src/platforms/webex/types.d.ts +51 -0
  28. package/dist/src/platforms/webex/types.d.ts.map +1 -1
  29. package/dist/src/platforms/webex/types.js +10 -0
  30. package/dist/src/platforms/webex/types.js.map +1 -1
  31. package/dist/src/platforms/webexbot/commands/file.d.ts.map +1 -1
  32. package/dist/src/platforms/webexbot/commands/file.js +2 -3
  33. package/dist/src/platforms/webexbot/commands/file.js.map +1 -1
  34. package/dist/src/platforms/webexbot/commands/member.d.ts.map +1 -1
  35. package/dist/src/platforms/webexbot/commands/member.js +2 -3
  36. package/dist/src/platforms/webexbot/commands/member.js.map +1 -1
  37. package/dist/src/platforms/webexbot/commands/message.d.ts.map +1 -1
  38. package/dist/src/platforms/webexbot/commands/message.js +6 -7
  39. package/dist/src/platforms/webexbot/commands/message.js.map +1 -1
  40. package/dist/src/platforms/webexbot/commands/snapshot.js +1 -1
  41. package/dist/src/platforms/webexbot/commands/snapshot.js.map +1 -1
  42. package/dist/src/platforms/webexbot/commands/user.d.ts.map +1 -1
  43. package/dist/src/platforms/webexbot/commands/user.js +3 -4
  44. package/dist/src/platforms/webexbot/commands/user.js.map +1 -1
  45. package/dist/src/platforms/webexbot/commands/whoami.d.ts.map +1 -1
  46. package/dist/src/platforms/webexbot/commands/whoami.js +2 -3
  47. package/dist/src/platforms/webexbot/commands/whoami.js.map +1 -1
  48. package/dist/src/platforms/webexbot/index.d.ts +1 -1
  49. package/dist/src/platforms/webexbot/index.d.ts.map +1 -1
  50. package/dist/src/platforms/webexbot/index.js.map +1 -1
  51. package/dist/src/tui/adapters/webex-adapter.js +2 -2
  52. package/dist/src/tui/adapters/webex-adapter.js.map +1 -1
  53. package/package.json +1 -1
  54. package/skills/agent-channeltalk/SKILL.md +1 -1
  55. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  56. package/skills/agent-discord/SKILL.md +1 -1
  57. package/skills/agent-discordbot/SKILL.md +1 -1
  58. package/skills/agent-instagram/SKILL.md +1 -1
  59. package/skills/agent-kakaotalk/SKILL.md +1 -1
  60. package/skills/agent-line/SKILL.md +1 -1
  61. package/skills/agent-slack/SKILL.md +1 -1
  62. package/skills/agent-slackbot/SKILL.md +1 -1
  63. package/skills/agent-teams/SKILL.md +1 -1
  64. package/skills/agent-telegram/SKILL.md +1 -1
  65. package/skills/agent-telegrambot/SKILL.md +1 -1
  66. package/skills/agent-webex/SKILL.md +1 -1
  67. package/skills/agent-webexbot/SKILL.md +1 -1
  68. package/skills/agent-wechatbot/SKILL.md +1 -1
  69. package/skills/agent-whatsapp/SKILL.md +1 -1
  70. package/skills/agent-whatsappbot/SKILL.md +1 -1
  71. package/src/platforms/webex/client.ts +36 -16
  72. package/src/platforms/webex/commands/auth.ts +3 -3
  73. package/src/platforms/webex/commands/member.test.ts +6 -0
  74. package/src/platforms/webex/commands/member.ts +2 -3
  75. package/src/platforms/webex/commands/message.test.ts +6 -0
  76. package/src/platforms/webex/commands/message.ts +2 -3
  77. package/src/platforms/webex/commands/whoami.test.ts +2 -0
  78. package/src/platforms/webex/commands/whoami.ts +2 -3
  79. package/src/platforms/webex/id-normalizer.test.ts +245 -2
  80. package/src/platforms/webex/id-normalizer.ts +106 -26
  81. package/src/platforms/webex/index.ts +13 -1
  82. package/src/platforms/webex/listener.test.ts +3 -0
  83. package/src/platforms/webex/listener.ts +16 -8
  84. package/src/platforms/webex/types.test.ts +20 -0
  85. package/src/platforms/webex/types.ts +68 -0
  86. package/src/platforms/webex/typings/webex-message-handler.d.ts +40 -2
  87. package/src/platforms/webexbot/commands/file.ts +2 -3
  88. package/src/platforms/webexbot/commands/member.ts +2 -3
  89. package/src/platforms/webexbot/commands/message.ts +6 -7
  90. package/src/platforms/webexbot/commands/snapshot.ts +1 -1
  91. package/src/platforms/webexbot/commands/user.test.ts +4 -0
  92. package/src/platforms/webexbot/commands/user.ts +3 -4
  93. package/src/platforms/webexbot/commands/whoami.ts +2 -3
  94. package/src/platforms/webexbot/index.ts +12 -1
  95. package/src/tui/adapters/webex-adapter.ts +2 -2
@@ -11,22 +11,28 @@ const personId = toRestId('person_789', 'PEOPLE')
11
11
 
12
12
  const mockMessage = {
13
13
  id: messageId,
14
+ ref: 'msg_123',
14
15
  roomId,
16
+ roomRef: 'space_456',
15
17
  roomType: 'group' as const,
16
18
  text: 'Hello world',
17
19
  html: '<p>Hello <a href="https://example.com">world</a></p>',
18
20
  personId,
21
+ personRef: 'person_789',
19
22
  personEmail: 'user@example.com',
20
23
  created: '2025-01-29T10:00:00.000Z',
21
24
  }
22
25
 
23
26
  const mockMessage2 = {
24
27
  id: message2Id,
28
+ ref: 'msg_124',
25
29
  roomId,
30
+ roomRef: 'space_456',
26
31
  roomType: 'group' as const,
27
32
  text: 'Second message',
28
33
  html: '<p>Second message</p>',
29
34
  personId,
35
+ personRef: 'person_789',
30
36
  personEmail: 'user@example.com',
31
37
  created: '2025-01-29T10:01:00.000Z',
32
38
  }
@@ -4,15 +4,14 @@ import { handleError } from '@/shared/utils/error-handler'
4
4
  import { formatOutput } from '@/shared/utils/output'
5
5
 
6
6
  import { WebexClient } from '../client'
7
- import { toRef } from '../id-normalizer'
8
7
  import type { WebexMessage } from '../types'
9
8
 
10
9
  function formatMessageOutput(message: WebexMessage) {
11
10
  return {
12
11
  id: message.id,
13
- ref: toRef(message.id),
12
+ ref: message.ref,
14
13
  roomId: message.roomId,
15
- roomRef: toRef(message.roomId),
14
+ roomRef: message.roomRef,
16
15
  text: message.text,
17
16
  html: message.html,
18
17
  personEmail: message.personEmail,
@@ -10,6 +10,7 @@ const personId = toRestId('person-123', 'PEOPLE')
10
10
 
11
11
  const mockUser = {
12
12
  id: personId,
13
+ ref: 'person-123',
13
14
  emails: ['test@example.com'],
14
15
  displayName: 'Test User',
15
16
  nickName: 'Testy',
@@ -17,6 +18,7 @@ const mockUser = {
17
18
  lastName: 'User',
18
19
  avatar: 'https://example.com/avatar.jpg',
19
20
  orgId,
21
+ orgRef: 'org-123',
20
22
  type: 'person' as const,
21
23
  created: '2024-01-01T00:00:00.000Z',
22
24
  }
@@ -4,7 +4,6 @@ import { handleError } from '@/shared/utils/error-handler'
4
4
  import { formatOutput } from '@/shared/utils/output'
5
5
 
6
6
  import { WebexClient } from '../client'
7
- import { toRef } from '../id-normalizer'
8
7
 
9
8
  export async function whoamiAction(options: { pretty?: boolean }): Promise<void> {
10
9
  try {
@@ -13,7 +12,7 @@ export async function whoamiAction(options: { pretty?: boolean }): Promise<void>
13
12
 
14
13
  const output = {
15
14
  id: user.id,
16
- ref: toRef(user.id),
15
+ ref: user.ref,
17
16
  emails: user.emails,
18
17
  displayName: user.displayName,
19
18
  nickName: user.nickName,
@@ -21,7 +20,7 @@ export async function whoamiAction(options: { pretty?: boolean }): Promise<void>
21
20
  lastName: user.lastName,
22
21
  avatar: user.avatar,
23
22
  orgId: user.orgId,
24
- orgRef: toRef(user.orgId),
23
+ orgRef: user.orgRef,
25
24
  type: user.type,
26
25
  }
27
26
  console.log(formatOutput(output, options.pretty))
@@ -16,9 +16,13 @@ import {
16
16
  normalizeMembership,
17
17
  normalizeMessage,
18
18
  normalizeRoomActivity,
19
+ normalizeSdkMembership,
20
+ normalizeSdkMessage,
21
+ normalizeSdkPerson,
19
22
  toRestId,
20
23
  toRef,
21
24
  } from './id-normalizer'
25
+ import type { WebexMembership, WebexMessage, WebexPerson } from './types'
22
26
 
23
27
  const RAW: MercuryActivity = {
24
28
  id: 'activity-uuid',
@@ -122,6 +126,23 @@ describe('normalizeMessage', () => {
122
126
  expect(result.mentionedPeople).toEqual([toRestId('mention-uuid-1', 'PEOPLE'), toRestId('mention-uuid-2', 'PEOPLE')])
123
127
  })
124
128
 
129
+ it('adds a raw uuid ref alongside every id', () => {
130
+ const result = normalizeMessage(message)
131
+
132
+ expect(result.ref).toBe('msg-uuid')
133
+ expect(result.parentRef).toBe('parent-uuid')
134
+ expect(result.roomRef).toBe('room-uuid')
135
+ expect(result.personRef).toBe('person-uuid')
136
+ expect(result.mentionedPeopleRefs).toEqual(['mention-uuid-1', 'mention-uuid-2'])
137
+ })
138
+
139
+ it('omits parentRef when parentId is absent', () => {
140
+ const { parentId: _omit, ...withoutParent } = message
141
+ const result = normalizeMessage(withoutParent)
142
+
143
+ expect(result.parentRef).toBeUndefined()
144
+ })
145
+
125
146
  it('leaves non-id fields and raw untouched', () => {
126
147
  const result = normalizeMessage(message)
127
148
 
@@ -157,8 +178,11 @@ describe('normalizeDeletedMessage', () => {
157
178
 
158
179
  expect(normalizeDeletedMessage(deleted)).toEqual({
159
180
  messageId: toRestId('msg-uuid', 'MESSAGE'),
181
+ messageRef: 'msg-uuid',
160
182
  roomId: toRestId('room-uuid', 'ROOM'),
183
+ roomRef: 'room-uuid',
161
184
  personId: toRestId('person-uuid', 'PEOPLE'),
185
+ personRef: 'person-uuid',
162
186
  })
163
187
  })
164
188
  })
@@ -183,6 +207,25 @@ describe('normalizeMembership', () => {
183
207
  expect(result.roomId).toBe(toRestId('room-uuid', 'ROOM'))
184
208
  expect(result.raw).toBe(RAW)
185
209
  })
210
+
211
+ it('sets ref to the raw activity id and adds refs for the rest', () => {
212
+ const membership: MembershipActivity = {
213
+ id: 'activity-uuid',
214
+ actorId: 'actor-uuid',
215
+ personId: 'member-uuid',
216
+ roomId: 'room-uuid',
217
+ action: 'add',
218
+ created: '2024-01-01T00:00:00Z',
219
+ raw: RAW,
220
+ }
221
+
222
+ const result = normalizeMembership(membership)
223
+
224
+ expect(result.ref).toBe('activity-uuid')
225
+ expect(result.actorRef).toBe('actor-uuid')
226
+ expect(result.personRef).toBe('member-uuid')
227
+ expect(result.roomRef).toBe('room-uuid')
228
+ })
186
229
  })
187
230
 
188
231
  describe('normalizeAttachmentAction', () => {
@@ -207,7 +250,27 @@ describe('normalizeAttachmentAction', () => {
207
250
  expect(result.inputs).toEqual({ choice: 'yes' })
208
251
  })
209
252
 
210
- it('preserves an empty messageId', () => {
253
+ it('adds a raw uuid ref alongside every id', () => {
254
+ const action: AttachmentAction = {
255
+ id: 'action-uuid',
256
+ messageId: 'msg-uuid',
257
+ personId: 'person-uuid',
258
+ personEmail: 'user@example.com',
259
+ roomId: 'room-uuid',
260
+ inputs: { choice: 'yes' },
261
+ created: '2024-01-01T00:00:00Z',
262
+ raw: RAW,
263
+ }
264
+
265
+ const result = normalizeAttachmentAction(action)
266
+
267
+ expect(result.ref).toBe('action-uuid')
268
+ expect(result.messageRef).toBe('msg-uuid')
269
+ expect(result.personRef).toBe('person-uuid')
270
+ expect(result.roomRef).toBe('room-uuid')
271
+ })
272
+
273
+ it('preserves an empty messageId and its ref', () => {
211
274
  const action: AttachmentAction = {
212
275
  id: 'action-uuid',
213
276
  messageId: '',
@@ -219,7 +282,9 @@ describe('normalizeAttachmentAction', () => {
219
282
  raw: RAW,
220
283
  }
221
284
 
222
- expect(normalizeAttachmentAction(action).messageId).toBe('')
285
+ const result = normalizeAttachmentAction(action)
286
+ expect(result.messageId).toBe('')
287
+ expect(result.messageRef).toBe('')
223
288
  })
224
289
  })
225
290
 
@@ -241,4 +306,182 @@ describe('normalizeRoomActivity', () => {
241
306
  expect(result.actorId).toBe(toRestId('actor-uuid', 'PEOPLE'))
242
307
  expect(result.raw).toBe(RAW)
243
308
  })
309
+
310
+ it('sets ref to the raw activity id and adds refs for the rest', () => {
311
+ const room: RoomActivity = {
312
+ id: 'activity-uuid',
313
+ roomId: 'room-uuid',
314
+ actorId: 'actor-uuid',
315
+ action: 'created',
316
+ created: '2024-01-01T00:00:00Z',
317
+ raw: RAW,
318
+ }
319
+
320
+ const result = normalizeRoomActivity(room)
321
+
322
+ expect(result.ref).toBe('activity-uuid')
323
+ expect(result.roomRef).toBe('room-uuid')
324
+ expect(result.actorRef).toBe('actor-uuid')
325
+ })
326
+ })
327
+
328
+ describe('normalizeSdkPerson', () => {
329
+ const person: WebexPerson = {
330
+ id: restId('PEOPLE', 'person-uuid'),
331
+ ref: '',
332
+ emails: ['user@example.com'],
333
+ displayName: 'User',
334
+ orgId: restId('ORGANIZATION', 'org-uuid'),
335
+ orgRef: '',
336
+ type: 'person',
337
+ created: '2024-01-01T00:00:00Z',
338
+ }
339
+
340
+ it('decodes the bot identity id and orgId into raw uuid refs', () => {
341
+ const result = normalizeSdkPerson(person)
342
+
343
+ expect(result.ref).toBe('person-uuid')
344
+ expect(result.orgRef).toBe('org-uuid')
345
+ })
346
+
347
+ it('does not re-encode the already-REST ids', () => {
348
+ const result = normalizeSdkPerson(person)
349
+
350
+ expect(result.id).toBe(restId('PEOPLE', 'person-uuid'))
351
+ expect(result.orgId).toBe(restId('ORGANIZATION', 'org-uuid'))
352
+ })
353
+
354
+ it('leaves empty ids and refs empty', () => {
355
+ const result = normalizeSdkPerson({ ...person, id: '', orgId: '' })
356
+
357
+ expect(result.ref).toBe('')
358
+ expect(result.orgRef).toBe('')
359
+ })
360
+
361
+ it('returns a new object without mutating the input', () => {
362
+ const result = normalizeSdkPerson(person)
363
+
364
+ expect(result).not.toBe(person)
365
+ expect(person.ref).toBe('')
366
+ })
367
+ })
368
+
369
+ describe('normalizeSdkMessage', () => {
370
+ const message: WebexMessage = {
371
+ id: restId('MESSAGE', 'msg-uuid'),
372
+ ref: '',
373
+ roomId: restId('ROOM', 'room-uuid'),
374
+ roomRef: '',
375
+ roomType: 'group',
376
+ text: 'hello',
377
+ personId: restId('PEOPLE', 'person-uuid'),
378
+ personRef: '',
379
+ personEmail: 'user@example.com',
380
+ created: '2024-01-01T00:00:00Z',
381
+ parentId: restId('MESSAGE', 'parent-uuid'),
382
+ mentionedPeople: [restId('PEOPLE', 'mention-1'), restId('PEOPLE', 'mention-2')],
383
+ }
384
+
385
+ it('decodes id, roomId, personId and parentId into raw uuid refs', () => {
386
+ const result = normalizeSdkMessage(message)
387
+
388
+ expect(result.ref).toBe('msg-uuid')
389
+ expect(result.roomRef).toBe('room-uuid')
390
+ expect(result.personRef).toBe('person-uuid')
391
+ expect(result.parentRef).toBe('parent-uuid')
392
+ })
393
+
394
+ it('adds a mentionedPeopleRefs array of raw uuids', () => {
395
+ const result = normalizeSdkMessage(message)
396
+
397
+ expect(result.mentionedPeopleRefs).toEqual(['mention-1', 'mention-2'])
398
+ })
399
+
400
+ it('omits parentRef when parentId is absent', () => {
401
+ const { parentId: _omit, ...withoutParent } = message
402
+ const result = normalizeSdkMessage(withoutParent)
403
+
404
+ expect(result.parentRef).toBeUndefined()
405
+ })
406
+
407
+ it('omits mentionedPeopleRefs when mentionedPeople is absent', () => {
408
+ const { mentionedPeople: _omit, ...withoutMentions } = message
409
+ const result = normalizeSdkMessage(withoutMentions)
410
+
411
+ expect(result.mentionedPeopleRefs).toBeUndefined()
412
+ })
413
+
414
+ it('does not re-encode the already-REST ids', () => {
415
+ const result = normalizeSdkMessage(message)
416
+
417
+ expect(result.id).toBe(restId('MESSAGE', 'msg-uuid'))
418
+ expect(result.roomId).toBe(restId('ROOM', 'room-uuid'))
419
+ })
420
+ })
421
+
422
+ describe('normalizeSdkMembership', () => {
423
+ const membership: WebexMembership = {
424
+ id: restId('MEMBERSHIP', 'membership-uuid'),
425
+ ref: '',
426
+ roomId: restId('ROOM', 'room-uuid'),
427
+ roomRef: '',
428
+ personId: restId('PEOPLE', 'person-uuid'),
429
+ personRef: '',
430
+ personEmail: 'user@example.com',
431
+ personDisplayName: 'User',
432
+ isModerator: false,
433
+ created: '2024-01-01T00:00:00Z',
434
+ }
435
+
436
+ it('decodes id, roomId and personId into raw uuid refs', () => {
437
+ const result = normalizeSdkMembership(membership)
438
+
439
+ expect(result.ref).toBe('membership-uuid')
440
+ expect(result.roomRef).toBe('room-uuid')
441
+ expect(result.personRef).toBe('person-uuid')
442
+ })
443
+
444
+ it('does not re-encode the already-REST ids', () => {
445
+ const result = normalizeSdkMembership(membership)
446
+
447
+ expect(result.id).toBe(restId('MEMBERSHIP', 'membership-uuid'))
448
+ expect(result.personId).toBe(restId('PEOPLE', 'person-uuid'))
449
+ })
450
+ })
451
+
452
+ describe('SDK and event refs agree for the same person', () => {
453
+ it('matches the bot identity ref against an event mention ref so self/mention detection holds', () => {
454
+ const personUuid = 'bot-person-uuid'
455
+
456
+ // given: the bot identity from a REST response (testAuth path)
457
+ const bot = normalizeSdkPerson({
458
+ id: restId('PEOPLE', personUuid),
459
+ ref: '',
460
+ emails: ['bot@webex.bot'],
461
+ displayName: 'Bot',
462
+ orgId: restId('ORGANIZATION', 'org-uuid'),
463
+ orgRef: '',
464
+ type: 'bot',
465
+ created: '2024-01-01T00:00:00Z',
466
+ })
467
+
468
+ // when: an event carries the same person as a raw Mercury uuid
469
+ const event = normalizeMessage({
470
+ id: 'msg-uuid',
471
+ roomId: 'room-uuid',
472
+ personId: personUuid,
473
+ personEmail: 'bot@webex.bot',
474
+ text: 'hi',
475
+ created: '2024-01-01T00:00:00Z',
476
+ mentionedPeople: [personUuid],
477
+ mentionedGroups: [],
478
+ files: [],
479
+ raw: RAW,
480
+ })
481
+
482
+ // then: both paths decode to the same raw uuid ref
483
+ expect(bot.ref).toBe(personUuid)
484
+ expect(event.personRef).toBe(personUuid)
485
+ expect(event.mentionedPeopleRefs.includes(bot.ref)).toBe(true)
486
+ })
244
487
  })
@@ -7,6 +7,17 @@ import type {
7
7
  RoomActivity,
8
8
  } from 'webex-message-handler'
9
9
 
10
+ import type {
11
+ WebexAttachmentActionEvent,
12
+ WebexDeletedMessageEvent,
13
+ WebexMembership,
14
+ WebexMembershipEvent,
15
+ WebexMessage,
16
+ WebexMessageEvent,
17
+ WebexPerson,
18
+ WebexRoomEvent,
19
+ } from './types'
20
+
10
21
  export { fromRestId }
11
22
 
12
23
  // Superset of webex-message-handler's toRestId union, which omits ATTACHMENT_ACTION
@@ -20,7 +31,9 @@ export interface DecodedWebexId {
20
31
  }
21
32
 
22
33
  export function toRef(id: string): string {
23
- if (!id) return id
34
+ // fromRestId throws on values that are not base64-encoded ciscospark ids; fail
35
+ // open so a non-REST id (legacy/sentinel) yields the id itself instead of crashing.
36
+ if (!id || !decodeWebexId(id)) return id
24
37
  return fromRestId(id)
25
38
  }
26
39
 
@@ -47,51 +60,118 @@ export function toRestId(uuid: string, type: WebexRestIdType): string {
47
60
  return Buffer.from(`ciscospark://us/${type}/${uuid}`).toString('base64url')
48
61
  }
49
62
 
50
- export function normalizeMessage(message: DecryptedMessage): DecryptedMessage {
63
+ export function normalizeMessage(message: DecryptedMessage): WebexMessageEvent {
64
+ const id = toRestId(message.id, 'MESSAGE')
65
+ const parentId = message.parentId ? toRestId(message.parentId, 'MESSAGE') : message.parentId
66
+ const roomId = toRestId(message.roomId, 'ROOM')
67
+ const personId = toRestId(message.personId, 'PEOPLE')
68
+ const mentionedPeople = message.mentionedPeople.map((person) => toRestId(person, 'PEOPLE'))
51
69
  return {
52
70
  ...message,
53
- id: toRestId(message.id, 'MESSAGE'),
54
- parentId: message.parentId ? toRestId(message.parentId, 'MESSAGE') : message.parentId,
55
- roomId: toRestId(message.roomId, 'ROOM'),
56
- personId: toRestId(message.personId, 'PEOPLE'),
57
- mentionedPeople: message.mentionedPeople.map((id) => toRestId(id, 'PEOPLE')),
71
+ id,
72
+ ref: toRef(id),
73
+ parentId,
74
+ parentRef: parentId ? toRef(parentId) : parentId,
75
+ roomId,
76
+ roomRef: toRef(roomId),
77
+ personId,
78
+ personRef: toRef(personId),
79
+ mentionedPeople,
80
+ mentionedPeopleRefs: mentionedPeople.map(toRef),
58
81
  }
59
82
  }
60
83
 
61
- export function normalizeDeletedMessage(message: DeletedMessage): DeletedMessage {
84
+ export function normalizeDeletedMessage(message: DeletedMessage): WebexDeletedMessageEvent {
85
+ const messageId = toRestId(message.messageId, 'MESSAGE')
86
+ const roomId = toRestId(message.roomId, 'ROOM')
87
+ const personId = toRestId(message.personId, 'PEOPLE')
62
88
  return {
63
- messageId: toRestId(message.messageId, 'MESSAGE'),
64
- roomId: toRestId(message.roomId, 'ROOM'),
65
- personId: toRestId(message.personId, 'PEOPLE'),
89
+ messageId,
90
+ messageRef: toRef(messageId),
91
+ roomId,
92
+ roomRef: toRef(roomId),
93
+ personId,
94
+ personRef: toRef(personId),
66
95
  }
67
96
  }
68
97
 
69
- export function normalizeMembership(activity: MembershipActivity): MembershipActivity {
70
- // `id` stays raw: it is a Mercury activity UUID, not a REST membership ID.
98
+ export function normalizeMembership(activity: MembershipActivity): WebexMembershipEvent {
99
+ // `id` stays raw: it is a Mercury activity UUID, not a REST membership ID, so
100
+ // its ref is the id itself rather than a decoded REST id.
101
+ const actorId = toRestId(activity.actorId, 'PEOPLE')
102
+ const personId = toRestId(activity.personId, 'PEOPLE')
103
+ const roomId = toRestId(activity.roomId, 'ROOM')
71
104
  return {
72
105
  ...activity,
73
- actorId: toRestId(activity.actorId, 'PEOPLE'),
74
- personId: toRestId(activity.personId, 'PEOPLE'),
75
- roomId: toRestId(activity.roomId, 'ROOM'),
106
+ ref: activity.id,
107
+ actorId,
108
+ actorRef: toRef(actorId),
109
+ personId,
110
+ personRef: toRef(personId),
111
+ roomId,
112
+ roomRef: toRef(roomId),
76
113
  }
77
114
  }
78
115
 
79
- export function normalizeAttachmentAction(action: AttachmentAction): AttachmentAction {
116
+ export function normalizeAttachmentAction(action: AttachmentAction): WebexAttachmentActionEvent {
117
+ const id = toRestId(action.id, 'ATTACHMENT_ACTION')
118
+ const messageId = action.messageId ? toRestId(action.messageId, 'MESSAGE') : action.messageId
119
+ const personId = toRestId(action.personId, 'PEOPLE')
120
+ const roomId = toRestId(action.roomId, 'ROOM')
80
121
  return {
81
122
  ...action,
82
- id: toRestId(action.id, 'ATTACHMENT_ACTION'),
83
- messageId: action.messageId ? toRestId(action.messageId, 'MESSAGE') : action.messageId,
84
- personId: toRestId(action.personId, 'PEOPLE'),
85
- roomId: toRestId(action.roomId, 'ROOM'),
123
+ id,
124
+ ref: toRef(id),
125
+ messageId,
126
+ messageRef: toRef(messageId),
127
+ personId,
128
+ personRef: toRef(personId),
129
+ roomId,
130
+ roomRef: toRef(roomId),
86
131
  }
87
132
  }
88
133
 
89
- export function normalizeRoomActivity(activity: RoomActivity): RoomActivity {
90
- // `id` stays raw: the Mercury conversation activity UUID has no
91
- // consumer-facing REST resource (the comparable REST ID is `roomId`).
134
+ export function normalizeRoomActivity(activity: RoomActivity): WebexRoomEvent {
135
+ // `id` stays raw: the Mercury conversation activity UUID has no consumer-facing
136
+ // REST resource, so its ref is the id itself (the comparable REST id is `roomId`).
137
+ const roomId = toRestId(activity.roomId, 'ROOM')
138
+ const actorId = toRestId(activity.actorId, 'PEOPLE')
92
139
  return {
93
140
  ...activity,
94
- roomId: toRestId(activity.roomId, 'ROOM'),
95
- actorId: toRestId(activity.actorId, 'PEOPLE'),
141
+ ref: activity.id,
142
+ roomId,
143
+ roomRef: toRef(roomId),
144
+ actorId,
145
+ actorRef: toRef(actorId),
146
+ }
147
+ }
148
+
149
+ // SDK REST responses (people/messages/memberships) already carry REST-encoded ids,
150
+ // so unlike the event normalizers above we only attach raw uuid refs — no re-encoding.
151
+ export function normalizeSdkPerson(person: WebexPerson): WebexPerson {
152
+ return {
153
+ ...person,
154
+ ref: toRef(person.id),
155
+ orgRef: toRef(person.orgId),
156
+ }
157
+ }
158
+
159
+ export function normalizeSdkMessage(message: WebexMessage): WebexMessage {
160
+ return {
161
+ ...message,
162
+ ref: toRef(message.id),
163
+ roomRef: toRef(message.roomId),
164
+ personRef: toRef(message.personId),
165
+ parentRef: message.parentId ? toRef(message.parentId) : message.parentId,
166
+ mentionedPeopleRefs: message.mentionedPeople?.map(toRef),
167
+ }
168
+ }
169
+
170
+ export function normalizeSdkMembership(membership: WebexMembership): WebexMembership {
171
+ return {
172
+ ...membership,
173
+ ref: toRef(membership.id),
174
+ roomRef: toRef(membership.roomId),
175
+ personRef: toRef(membership.personId),
96
176
  }
97
177
  }
@@ -9,7 +9,19 @@ export type { PasswordLoginOptions, PasswordLoginResult } from './password-login
9
9
  export { WebexTokenExtractor } from './token-extractor'
10
10
  export type { ExtractedWebexToken } from './token-extractor'
11
11
  export { WebexError } from './types'
12
- export type { WebexConfig, WebexMembership, WebexMessage, WebexPerson, WebexSpace } from './types'
12
+ export type {
13
+ WebexAttachmentActionEvent,
14
+ WebexConfig,
15
+ WebexDeletedMessageEvent,
16
+ WebexMembership,
17
+ WebexMembershipEvent,
18
+ WebexMessage,
19
+ WebexMessageEvent,
20
+ WebexPerson,
21
+ WebexRealtimeEvent,
22
+ WebexRoomEvent,
23
+ WebexSpace,
24
+ } from './types'
13
25
  export {
14
26
  WebexConfigSchema,
15
27
  WebexMembershipSchema,
@@ -81,8 +81,11 @@ describe('WebexListener', () => {
81
81
 
82
82
  const expected = expect.objectContaining({
83
83
  id: toRestId('message-123', 'MESSAGE'),
84
+ ref: 'message-123',
84
85
  roomId: toRestId('room-123', 'ROOM'),
86
+ roomRef: 'room-123',
85
87
  personId: toRestId('person-123', 'PEOPLE'),
88
+ personRef: 'person-123',
86
89
  personEmail: 'user@example.com',
87
90
  text: 'hello',
88
91
  raw: RAW_ACTIVITY,
@@ -21,6 +21,14 @@ import {
21
21
  normalizeMessage,
22
22
  normalizeRoomActivity,
23
23
  } from './id-normalizer'
24
+ import type {
25
+ WebexAttachmentActionEvent,
26
+ WebexDeletedMessageEvent,
27
+ WebexMembershipEvent,
28
+ WebexMessageEvent,
29
+ WebexRealtimeEvent,
30
+ WebexRoomEvent,
31
+ } from './types'
24
32
  import { createWdmRewriteFetch, discoverWdmDevicesUrl } from './wdm-discovery'
25
33
 
26
34
  type EventKey = keyof WebexListenerEventMap
@@ -49,14 +57,14 @@ export interface WebexListenerOptions {
49
57
  }
50
58
 
51
59
  export interface WebexListenerEventMap {
52
- message_created: [event: DecryptedMessage]
53
- message_updated: [event: DecryptedMessage]
54
- message_deleted: [event: DeletedMessage]
55
- membership_created: [event: MembershipActivity]
56
- attachment_action: [event: AttachmentAction]
57
- room_created: [event: RoomActivity]
58
- room_updated: [event: RoomActivity]
59
- webex_event: [event: DecryptedMessage | DeletedMessage | MembershipActivity | AttachmentAction | RoomActivity]
60
+ message_created: [event: WebexMessageEvent]
61
+ message_updated: [event: WebexMessageEvent]
62
+ message_deleted: [event: WebexDeletedMessageEvent]
63
+ membership_created: [event: WebexMembershipEvent]
64
+ attachment_action: [event: WebexAttachmentActionEvent]
65
+ room_created: [event: WebexRoomEvent]
66
+ room_updated: [event: WebexRoomEvent]
67
+ webex_event: [event: WebexRealtimeEvent]
60
68
  connected: [info: { connected: boolean; status: HandlerStatus }]
61
69
  reconnecting: [attempt: number]
62
70
  disconnected: [reason: string]