@goodandready/dsh-messenger-gateway 0.4.4 → 0.4.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/lib/client.js CHANGED
@@ -430,14 +430,18 @@ window.__ModuleLoader__.load({
430
430
  }
431
431
  const ErrorBoundary = createErrorBoundary()
432
432
 
433
- function FallbackChevron() {
433
+ function FallbackChevron(props) {
434
434
  return React.createElement(
435
435
  'svg',
436
- { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
436
+ { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', ...props },
437
437
  React.createElement('polyline', { points: '6 9 12 15 18 9' })
438
438
  )
439
439
  }
440
- const Chevron = FallbackChevron
440
+ function Chevron(props) {
441
+ const CoreChevron = props?.ctx?.get?.('icons')?.IconChevronDownOutline14
442
+ if (CoreChevron) return React.createElement(CoreChevron, props)
443
+ return React.createElement(FallbackChevron, props)
444
+ }
441
445
 
442
446
  function idsToText(ids) { return (Array.isArray(ids) ? ids : []).map(String).filter(Boolean).join(', ') }
443
447
  function textToIds(text) {
@@ -1512,6 +1516,7 @@ window.__ModuleLoader__.load({
1512
1516
 
1513
1517
  function PluginCard(props) {
1514
1518
  const [open, setOpen] = React.useState(false)
1519
+ const cardCtx = (props && props.ctx) || (typeof ctx !== 'undefined' ? ctx : undefined)
1515
1520
  const t = props?.t || makeT(props?.locale)
1516
1521
  React.useEffect(() => {
1517
1522
  ensureCss()
@@ -1529,7 +1534,7 @@ window.__ModuleLoader__.load({
1529
1534
  'div',
1530
1535
  { className: 'msgw-page-seat' },
1531
1536
  React.createElement(ErrorBoundary, null,
1532
- React.createElement(SettingsPage, { ...props, ctx: (props && props.ctx) || ctx, t })
1537
+ React.createElement(SettingsPage, { ...props, ctx: cardCtx, t })
1533
1538
  )
1534
1539
  )
1535
1540
  }
@@ -1564,7 +1569,7 @@ window.__ModuleLoader__.load({
1564
1569
  React.createElement(
1565
1570
  'span',
1566
1571
  { className: 'msgw-chev', style: { transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .16s' } },
1567
- React.createElement(Chevron)
1572
+ React.createElement(Chevron, { ctx: cardCtx })
1568
1573
  )
1569
1574
  ),
1570
1575
  open
@@ -1574,7 +1579,7 @@ window.__ModuleLoader__.load({
1574
1579
  React.createElement(
1575
1580
  ErrorBoundary,
1576
1581
  null,
1577
- React.createElement(SettingsPage, { ...props, ctx: (props && props.ctx) || ctx, t })
1582
+ React.createElement(SettingsPage, { ...props, ctx: cardCtx, t })
1578
1583
  )
1579
1584
  )
1580
1585
  : null
@@ -1,105 +1,105 @@
1
- import { mergeDynamicCommands } from './commands.js'
2
- import { t } from './locales/index.js'
3
- import { stripReasoningPreamble, splitText } from './text.js'
4
- import { stripImageUrls } from './outbound.js'
5
-
6
- export function collectDynamicSkills(ctx) {
7
- const skills = []
8
- try {
9
- const skillsService = ctx.get?.('skills') || gw.ctx.skills
10
- const allSkills = skillsService?.list?.() || []
11
- for (const s of allSkills) {
12
- if (!s || !s.name) continue
13
- if (s.userInvocable === false) continue
14
- skills.push({
15
- name: String(s.name),
16
- description: String(s.description || s.title || `Skill ${s.name}`).slice(0, 256),
17
- })
18
- }
19
- } catch (err) {
20
- ctx.logger?.debug?.(`Failed to collect dynamic skills: ${err?.message || err}`)
21
- }
22
- return skills
23
- }
24
- export async function syncTelegramCommands(gw) {
25
- const tgAdapter = gw.adapters.get('telegram')
26
- if (!tgAdapter || typeof tgAdapter.registerCommands !== 'function') return
27
- const baseCommands = gw.config.telegram?.commands || []
28
- const dynamicSkills = collectDynamicSkills(gw.ctx)
29
- const merged = mergeDynamicCommands(baseCommands, dynamicSkills, 100)
30
- try {
31
- await tgAdapter.registerCommands(merged)
32
- ctx.logger?.info?.(`Synced ${merged.length} Telegram commands (including ${dynamicSkills.length} dynamic skills)`)
33
- } catch (err) {
34
- ctx.logger?.warn?.(`Failed to sync Telegram commands: ${err?.message || err}`)
35
- }
36
- }
37
- export async function mirrorSessionToForumTopic(gw, session) {
38
- if (!session || !session.id) return
39
- const sessionId = String(session.id)
40
- if (sessionId.startsWith('msgw-')) return
41
- if (gw.sessionToThread.has(sessionId)) return
42
-
43
- const tgCfg = gw.config.telegram || {}
44
- if (!tgCfg.forumMirrorEnabled || !tgCfg.forumMirrorChatId) return
45
-
46
- const tgAdapter = gw.adapters.get('telegram')
47
- if (!tgAdapter || typeof tgAdapter.createForumTopic !== 'function') return
48
-
49
- const forumChatId = tgCfg.forumMirrorChatId
50
- const title = String(session.title || session.meta?.name || `Session ${sessionId.slice(0, 8)}`).slice(0, 120)
51
-
52
- try {
53
- const topic = await tgAdapter.createForumTopic(forumChatId, title)
54
- const threadId = topic?.message_thread_id
55
- if (!threadId) return
56
-
57
- const threadKey = `${forumChatId}:${threadId}`
58
- gw.sessionToThread.set(sessionId, { chatId: forumChatId, threadId })
59
- gw.threadToSession.set(threadKey, sessionId)
60
-
61
- const text = t('mirror.created', { sessionId, title }, 'en')
62
- await tgAdapter.sendTo(forumChatId, { text }, { threadId })
63
- ctx.logger?.info?.(`Mirrored session ${sessionId} to Telegram forum topic ${threadId} in ${forumChatId}`)
64
- } catch (err) {
65
- ctx.logger?.warn?.(`Failed to mirror session ${sessionId} to forum topic: ${err?.message || err}`)
66
- }
67
- }
68
- export async function relayTurnToForumMirror(gw, session, event) {
69
- if (!session || !session.id) return
70
- const sessionId = String(session.id)
71
- const threadInfo = gw.sessionToThread.get(sessionId)
72
- if (!threadInfo) return
73
- if (gw.pending.has(sessionId)) return
74
-
75
- const tgAdapter = gw.adapters.get('telegram')
76
- if (!tgAdapter) return
77
-
78
- const messages = Array.isArray(session.messages)
79
- ? session.messages
80
- : (Array.isArray(session.history) ? session.history : [])
81
-
82
- for (let i = messages.length - 1; i >= 0; i--) {
83
- const msg = messages[i]
84
- const role = msg.role || (msg.type === 'user' ? 'user' : 'assistant')
85
- if (role === 'assistant') {
86
- let text = ''
87
- if (typeof msg.content === 'string') text = msg.content
88
- else if (Array.isArray(msg.content)) {
89
- text = msg.content
90
- .map((p) => (typeof p === 'string' ? p : (p?.text || '')))
91
- .filter(Boolean)
92
- .join('\n')
93
- } else if (msg.text) text = msg.text
94
- const clean = stripReasoningPreamble(stripImageUrls(text)).trim()
95
- if (clean) {
96
- const maxLen = Number(gw.config.agent?.maxMessageLength) || 4000
97
- const chunks = splitText(clean, maxLen)
98
- for (const chunk of chunks) {
99
- await tgAdapter.sendTo(threadInfo.chatId, { text: chunk }, { threadId: threadInfo.threadId })
100
- }
101
- }
102
- break
103
- }
104
- }
105
- }
1
+ import { mergeDynamicCommands } from './commands.js'
2
+ import { t } from './locales/index.js'
3
+ import { stripReasoningPreamble, splitText } from './text.js'
4
+ import { stripImageUrls } from './outbound.js'
5
+
6
+ export function collectDynamicSkills(ctx) {
7
+ const skills = []
8
+ try {
9
+ const skillsService = ctx.get?.('skills') || ctx?.skills
10
+ const allSkills = skillsService?.list?.() || []
11
+ for (const s of allSkills) {
12
+ if (!s || !s.name) continue
13
+ if (s.userInvocable === false) continue
14
+ skills.push({
15
+ name: String(s.name),
16
+ description: String(s.description || s.title || `Skill ${s.name}`).slice(0, 256),
17
+ })
18
+ }
19
+ } catch (err) {
20
+ ctx?.logger?.debug?.(`Failed to collect dynamic skills: ${err?.message || err}`)
21
+ }
22
+ return skills
23
+ }
24
+ export async function syncTelegramCommands(gw) {
25
+ const tgAdapter = gw.adapters.get('telegram')
26
+ if (!tgAdapter || typeof tgAdapter.registerCommands !== 'function') return
27
+ const baseCommands = gw.config.telegram?.commands || []
28
+ const dynamicSkills = collectDynamicSkills(gw.ctx)
29
+ const merged = mergeDynamicCommands(baseCommands, dynamicSkills, 100)
30
+ try {
31
+ await tgAdapter.registerCommands(merged)
32
+ gw.ctx?.logger?.info?.(`Synced ${merged.length} Telegram commands (including ${dynamicSkills.length} dynamic skills)`)
33
+ } catch (err) {
34
+ gw.ctx?.logger?.warn?.(`Failed to sync Telegram commands: ${err?.message || err}`)
35
+ }
36
+ }
37
+ export async function mirrorSessionToForumTopic(gw, session) {
38
+ if (!session || !session.id) return
39
+ const sessionId = String(session.id)
40
+ if (sessionId.startsWith('msgw-')) return
41
+ if (gw.sessionToThread.has(sessionId)) return
42
+
43
+ const tgCfg = gw.config.telegram || {}
44
+ if (!tgCfg.forumMirrorEnabled || !tgCfg.forumMirrorChatId) return
45
+
46
+ const tgAdapter = gw.adapters.get('telegram')
47
+ if (!tgAdapter || typeof tgAdapter.createForumTopic !== 'function') return
48
+
49
+ const forumChatId = tgCfg.forumMirrorChatId
50
+ const title = String(session.title || session.meta?.name || `Session ${sessionId.slice(0, 8)}`).slice(0, 120)
51
+
52
+ try {
53
+ const topic = await tgAdapter.createForumTopic(forumChatId, title)
54
+ const threadId = topic?.message_thread_id
55
+ if (!threadId) return
56
+
57
+ const threadKey = `${forumChatId}:${threadId}`
58
+ gw.sessionToThread.set(sessionId, { chatId: forumChatId, threadId })
59
+ gw.threadToSession.set(threadKey, sessionId)
60
+
61
+ const text = t('mirror.created', { sessionId, title }, 'en')
62
+ await tgAdapter.sendTo(forumChatId, { text }, { threadId })
63
+ gw.ctx?.logger?.info?.(`Mirrored session ${sessionId} to Telegram forum topic ${threadId} in ${forumChatId}`)
64
+ } catch (err) {
65
+ gw.ctx?.logger?.warn?.(`Failed to mirror session ${sessionId} to forum topic: ${err?.message || err}`)
66
+ }
67
+ }
68
+ export async function relayTurnToForumMirror(gw, session, event) {
69
+ if (!session || !session.id) return
70
+ const sessionId = String(session.id)
71
+ const threadInfo = gw.sessionToThread.get(sessionId)
72
+ if (!threadInfo) return
73
+ if (gw.pending.has(sessionId)) return
74
+
75
+ const tgAdapter = gw.adapters.get('telegram')
76
+ if (!tgAdapter) return
77
+
78
+ const messages = Array.isArray(session.messages)
79
+ ? session.messages
80
+ : (Array.isArray(session.history) ? session.history : [])
81
+
82
+ for (let i = messages.length - 1; i >= 0; i--) {
83
+ const msg = messages[i]
84
+ const role = msg.role || (msg.type === 'user' ? 'user' : 'assistant')
85
+ if (role === 'assistant') {
86
+ let text = ''
87
+ if (typeof msg.content === 'string') text = msg.content
88
+ else if (Array.isArray(msg.content)) {
89
+ text = msg.content
90
+ .map((p) => (typeof p === 'string' ? p : (p?.text || '')))
91
+ .filter(Boolean)
92
+ .join('\n')
93
+ } else if (msg.text) text = msg.text
94
+ const clean = stripReasoningPreamble(stripImageUrls(text)).trim()
95
+ if (clean) {
96
+ const maxLen = Number(gw.config.agent?.maxMessageLength) || 4000
97
+ const chunks = splitText(clean, maxLen)
98
+ for (const chunk of chunks) {
99
+ await tgAdapter.sendTo(threadInfo.chatId, { text: chunk }, { threadId: threadInfo.threadId })
100
+ }
101
+ }
102
+ break
103
+ }
104
+ }
105
+ }