@workclaw/openclaw-workclaw 1.0.25 → 1.0.26

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 (161) hide show
  1. package/dist/api.d.ts +4 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +379 -0
  4. package/dist/channel-DSvR6Irx.js +50017 -0
  5. package/dist/cron-tasks-handler-C-mP3VmV.js +152 -0
  6. package/dist/index.d.ts +12 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +2088 -0
  9. package/dist/jiti-4i2h0FGP.js +4814 -0
  10. package/dist/setup-entry.d.ts +3 -0
  11. package/dist/setup-entry.d.ts.map +1 -0
  12. package/dist/setup-entry.js +182 -0
  13. package/dist/src/accounts.d.ts +67 -0
  14. package/dist/src/accounts.d.ts.map +1 -0
  15. package/dist/src/api/accounts-api.d.ts +3 -0
  16. package/dist/src/api/accounts-api.d.ts.map +1 -0
  17. package/dist/src/api/prompts-api.d.ts +3 -0
  18. package/dist/src/api/prompts-api.d.ts.map +1 -0
  19. package/dist/src/api/session-api.d.ts +3 -0
  20. package/dist/src/api/session-api.d.ts.map +1 -0
  21. package/dist/src/api/skills-api.d.ts +3 -0
  22. package/dist/src/api/skills-api.d.ts.map +1 -0
  23. package/dist/src/api/workspace.d.ts +4 -0
  24. package/dist/src/api/workspace.d.ts.map +1 -0
  25. package/dist/src/channel.d.ts +12 -0
  26. package/dist/src/channel.d.ts.map +1 -0
  27. package/dist/src/config-schema.d.ts +88 -0
  28. package/dist/src/config-schema.d.ts.map +1 -0
  29. package/dist/src/connection/workclaw-client.d.ts +149 -0
  30. package/dist/src/connection/workclaw-client.d.ts.map +1 -0
  31. package/dist/src/gateway/agent-handlers.d.ts +13 -0
  32. package/dist/src/gateway/agent-handlers.d.ts.map +1 -0
  33. package/dist/src/gateway/config-writer.d.ts +72 -0
  34. package/dist/src/gateway/config-writer.d.ts.map +1 -0
  35. package/dist/src/gateway/cron-tasks-handler.d.ts +20 -0
  36. package/dist/src/gateway/cron-tasks-handler.d.ts.map +1 -0
  37. package/dist/src/gateway/message-context.d.ts +81 -0
  38. package/dist/src/gateway/message-context.d.ts.map +1 -0
  39. package/dist/src/gateway/message-dispatcher.d.ts +17 -0
  40. package/dist/src/gateway/message-dispatcher.d.ts.map +1 -0
  41. package/dist/src/gateway/reconnect.d.ts +25 -0
  42. package/dist/src/gateway/reconnect.d.ts.map +1 -0
  43. package/dist/src/gateway/skills-handler.d.ts +30 -0
  44. package/dist/src/gateway/skills-handler.d.ts.map +1 -0
  45. package/dist/src/gateway/skills-list-handler.d.ts +28 -0
  46. package/dist/src/gateway/skills-list-handler.d.ts.map +1 -0
  47. package/dist/src/gateway/tools-list-handler.d.ts +31 -0
  48. package/dist/src/gateway/tools-list-handler.d.ts.map +1 -0
  49. package/dist/src/gateway/workclaw-gateway.d.ts +11 -0
  50. package/dist/src/gateway/workclaw-gateway.d.ts.map +1 -0
  51. package/dist/src/media/upload.d.ts +14 -0
  52. package/dist/src/media/upload.d.ts.map +1 -0
  53. package/dist/src/outbound/index.d.ts +39 -0
  54. package/dist/src/outbound/index.d.ts.map +1 -0
  55. package/dist/src/outbound/workclaw-sender.d.ts +38 -0
  56. package/dist/src/outbound/workclaw-sender.d.ts.map +1 -0
  57. package/dist/src/runtime.d.ts +118 -0
  58. package/dist/src/runtime.d.ts.map +1 -0
  59. package/dist/src/secret-contract-api.d.ts +5 -0
  60. package/dist/src/secret-contract-api.d.ts.map +1 -0
  61. package/{src/send.ts → dist/src/send.d.ts} +2 -1
  62. package/dist/src/send.d.ts.map +1 -0
  63. package/dist/src/setup-api.d.ts +4 -0
  64. package/dist/src/setup-api.d.ts.map +1 -0
  65. package/dist/src/setup-core.d.ts +4 -0
  66. package/dist/src/setup-core.d.ts.map +1 -0
  67. package/dist/src/setup-surface.d.ts +8 -0
  68. package/dist/src/setup-surface.d.ts.map +1 -0
  69. package/dist/src/tools/openclaw-workclaw-cron/api/index.d.ts +91 -0
  70. package/dist/src/tools/openclaw-workclaw-cron/api/index.d.ts.map +1 -0
  71. package/dist/src/tools/openclaw-workclaw-cron/index.d.ts +11 -0
  72. package/dist/src/tools/openclaw-workclaw-cron/index.d.ts.map +1 -0
  73. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.d.ts +14 -0
  74. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.d.ts.map +1 -0
  75. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.d.ts +16 -0
  76. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.d.ts.map +1 -0
  77. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.d.ts +12 -0
  78. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.d.ts.map +1 -0
  79. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.d.ts +12 -0
  80. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.d.ts.map +1 -0
  81. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.d.ts +12 -0
  82. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.d.ts.map +1 -0
  83. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.d.ts +12 -0
  84. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.d.ts.map +1 -0
  85. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.d.ts +13 -0
  86. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.d.ts.map +1 -0
  87. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.d.ts +12 -0
  88. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.d.ts.map +1 -0
  89. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.d.ts +12 -0
  90. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.d.ts.map +1 -0
  91. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.d.ts +15 -0
  92. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.d.ts.map +1 -0
  93. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.d.ts +16 -0
  94. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.d.ts.map +1 -0
  95. package/dist/src/tools/openclaw-workclaw-cron/types/index.d.ts +53 -0
  96. package/dist/src/tools/openclaw-workclaw-cron/types/index.d.ts.map +1 -0
  97. package/dist/src/tools/openclaw-workclaw-cron/utils/index.d.ts +41 -0
  98. package/dist/src/tools/openclaw-workclaw-cron/utils/index.d.ts.map +1 -0
  99. package/dist/src/tools/openclaw-workclaw-system/index.d.ts +11 -0
  100. package/dist/src/tools/openclaw-workclaw-system/index.d.ts.map +1 -0
  101. package/dist/src/tools/openclaw-workclaw-system/src/get/index.d.ts +6 -0
  102. package/dist/src/tools/openclaw-workclaw-system/src/get/index.d.ts.map +1 -0
  103. package/dist/src/tools/openclaw-workclaw-system/src/token/index.d.ts +6 -0
  104. package/dist/src/tools/openclaw-workclaw-system/src/token/index.d.ts.map +1 -0
  105. package/dist/src/types.d.ts +39 -0
  106. package/dist/src/types.d.ts.map +1 -0
  107. package/dist/src/utils/content.d.ts +16 -0
  108. package/dist/src/utils/content.d.ts.map +1 -0
  109. package/openclaw.plugin.json +6 -0
  110. package/package.json +69 -45
  111. package/api.ts +0 -3
  112. package/index.ts +0 -325
  113. package/setup-entry.ts +0 -13
  114. package/src/accounts.ts +0 -360
  115. package/src/api/accounts-api.ts +0 -156
  116. package/src/api/prompts-api.ts +0 -122
  117. package/src/api/session-api.ts +0 -246
  118. package/src/api/skills-api.ts +0 -74
  119. package/src/api/workspace.ts +0 -45
  120. package/src/channel.ts +0 -226
  121. package/src/config-schema.ts +0 -60
  122. package/src/connection/workclaw-client.ts +0 -618
  123. package/src/gateway/agent-handlers.ts +0 -551
  124. package/src/gateway/config-writer.ts +0 -378
  125. package/src/gateway/cron-tasks-handler.ts +0 -230
  126. package/src/gateway/message-context.ts +0 -645
  127. package/src/gateway/message-dispatcher.ts +0 -688
  128. package/src/gateway/reconnect.ts +0 -260
  129. package/src/gateway/skills-handler.ts +0 -805
  130. package/src/gateway/skills-list-handler.ts +0 -332
  131. package/src/gateway/tools-list-handler.ts +0 -161
  132. package/src/gateway/workclaw-gateway.ts +0 -304
  133. package/src/media/upload.ts +0 -168
  134. package/src/outbound/index.ts +0 -191
  135. package/src/outbound/workclaw-sender.ts +0 -161
  136. package/src/runtime.ts +0 -520
  137. package/src/secret-contract-api.ts +0 -4
  138. package/src/setup-api.ts +0 -3
  139. package/src/setup-core.ts +0 -25
  140. package/src/setup-surface.ts +0 -498
  141. package/src/tools/openclaw-workclaw-cron/api/index.ts +0 -326
  142. package/src/tools/openclaw-workclaw-cron/index.ts +0 -39
  143. package/src/tools/openclaw-workclaw-cron/src/add/params.ts +0 -177
  144. package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +0 -188
  145. package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +0 -100
  146. package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +0 -127
  147. package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +0 -100
  148. package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +0 -127
  149. package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +0 -148
  150. package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +0 -109
  151. package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +0 -127
  152. package/src/tools/openclaw-workclaw-cron/src/update/params.ts +0 -195
  153. package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +0 -161
  154. package/src/tools/openclaw-workclaw-cron/types/index.ts +0 -55
  155. package/src/tools/openclaw-workclaw-cron/utils/index.ts +0 -141
  156. package/src/tools/openclaw-workclaw-system/index.ts +0 -17
  157. package/src/tools/openclaw-workclaw-system/src/get/index.ts +0 -77
  158. package/src/tools/openclaw-workclaw-system/src/token/index.ts +0 -93
  159. package/src/types.ts +0 -50
  160. package/src/utils/content.ts +0 -40
  161. package/tsconfig.json +0 -34
@@ -1,688 +0,0 @@
1
- /**
2
- * Message Dispatcher - handles all WorkClaw WebSocket message types
3
- */
4
-
5
- import type { ResolvedWorkclawAccount, WorkClawAccountConfig, WorkClawBaseConfig } from '../types.js'
6
- import type { InboundMessage, ParseWorkClawResult } from './message-context.js'
7
-
8
- import { maybeResolveTextAlias, resolveNativeCommandSessionTargets } from 'openclaw/plugin-sdk/command-auth'
9
- import { request } from 'undici'
10
- import { resolveAccountByUserIdAndAgentId, resolveWorkclawAccountWithCache } from '../accounts.js'
11
- import { getWorkclawAccessToken, getWorkclawModelConfigByAppKey } from '../connection/workclaw-client.js'
12
- import { sendMessageWorkclaw } from '../outbound/index.js'
13
- import { getWorkclawRuntime, getWorkclawWsConnection, setToolContext } from '../runtime.js'
14
-
15
- import { handleAgentCreated, handleAgentDeleted, handleAgentUpdated } from './agent-handlers.js'
16
- import { initWorkclawAgent, saveOpenConversationId } from './config-writer.js'
17
- import { buildInboundContext, parseWorkClawMessage } from './message-context.js'
18
-
19
- import { handleSkillsEvent } from './skills-handler.js'
20
- import { handleSkillsListEvent as doHandleSkillsListEvent } from './skills-list-handler.js'
21
- import { handleToolsListEvent as doHandleToolsListEvent } from './tools-list-handler.js'
22
-
23
- export interface MessageDispatcherContext {
24
- accountId: string
25
- account: ResolvedWorkclawAccount
26
- cfg: any
27
- baseConfig: WorkClawBaseConfig
28
- accountConfig: WorkClawAccountConfig
29
- log?: {
30
- info?: (msg: string) => void
31
- warn?: (msg: string) => void
32
- error?: (msg: string) => void
33
- debug?: (msg: string) => void
34
- }
35
- scheduleReconnect: () => void
36
- }
37
-
38
- /** Log an error safely */
39
- function logError(log: MessageDispatcherContext['log'], context: string, err: unknown): void {
40
- const errAny = err as { stack?: string }
41
- const stack = typeof errAny?.stack === 'string' ? `\nStack: ${errAny.stack}` : ''
42
- log?.error?.(`Dispatcher ${context}: ${String(err)}${stack}`)
43
- }
44
-
45
- // ---------------------------------------------------------------------------
46
- // Individual message handlers
47
- // ---------------------------------------------------------------------------
48
-
49
- async function handlePing(ctx: MessageDispatcherContext, result: ParseWorkClawResult): Promise<void> {
50
- const { accountId, log } = ctx
51
- const ws = getWorkclawWsConnection(accountId)
52
- if (!ws)
53
- return
54
-
55
- try {
56
- const pongResponse = {
57
- code: 200,
58
- message: 'pong',
59
- metadata: { contentType: 'application/json' },
60
- data: JSON.stringify(result.pongData),
61
- }
62
- ws.send(JSON.stringify(pongResponse))
63
- log?.debug?.(`pong`)
64
- }
65
- catch (err) {
66
- logError(log, 'Failed to send pong', err)
67
- }
68
- }
69
-
70
- async function handleDisconnect(ctx: MessageDispatcherContext): Promise<void> {
71
- const { accountId, log, scheduleReconnect } = ctx
72
- const ws = getWorkclawWsConnection(accountId)
73
- if (ws) {
74
- try {
75
- ws.close()
76
- log?.info?.(`Closed WebSocket due to disconnect command`)
77
- }
78
- catch (err) {
79
- logError(log, 'Failed to close WebSocket', err)
80
- }
81
- }
82
- else {
83
- log?.info?.(`WebSocket already cleared, triggering reconnect...`)
84
- scheduleReconnect()
85
- }
86
- }
87
-
88
- async function handleAgentEvent(
89
- ctx: MessageDispatcherContext,
90
- result: ParseWorkClawResult,
91
- ): Promise<void> {
92
- const { cfg, log, baseConfig, accountId } = ctx
93
- const eventData = result.eventData
94
- if (!eventData) {
95
- log?.warn?.(`Received ${result.type} event but no data`)
96
- return
97
- }
98
-
99
- const agentId = String(eventData.id || '')
100
- const userId = String(eventData.futureId || '')
101
-
102
- if (!agentId) {
103
- log?.warn?.(`Received ${result.type} event but missing agent id`)
104
- return
105
- }
106
-
107
- log?.info?.(`Processing ${result.type} event for agent ${agentId}, userId: ${userId}`)
108
-
109
- const accountData = {
110
- agentId,
111
- userId,
112
- nickName: eventData.nickName,
113
- phone: eventData.phone,
114
- status: eventData.status,
115
- introduction: eventData.introduction,
116
- name: eventData.name,
117
- tip: eventData.tip,
118
- characterSettings: eventData.characterSettings,
119
- personFeatures: eventData.personFeatures,
120
- workFeatures: eventData.workFeatures,
121
- learningFeatures: eventData.learningFeatures,
122
- socializeFeatures: eventData.socializeFeatures,
123
- job: eventData.job,
124
- city: eventData.city,
125
- }
126
-
127
- switch (result.type) {
128
- case 'agent_created':
129
- await handleAgentCreated(accountData, cfg, log)
130
- handleAgentCreatedMessage(baseConfig, agentId, accountId, log)
131
- break
132
- case 'agent_updated':
133
- await handleAgentUpdated(accountData, cfg, log)
134
- break
135
- case 'agent_deleted':
136
- await handleAgentDeleted(accountData, cfg, log)
137
- break
138
- }
139
- }
140
-
141
- async function handleSkillsProcessingEvent(ctx: MessageDispatcherContext, result: ParseWorkClawResult): Promise<void> {
142
- const { accountId, baseConfig, log } = ctx
143
- if (!result.eventData)
144
- return
145
- try {
146
- const connConfig = {
147
- baseUrl: baseConfig.baseUrl || '',
148
- websocketUrl: baseConfig.websocketUrl,
149
- appKey: baseConfig.appKey,
150
- appSecret: baseConfig.appSecret,
151
- localIp: baseConfig.localIp,
152
- allowInsecureTls: baseConfig.allowInsecureTls,
153
- requestTimeout: baseConfig.requestTimeout,
154
- }
155
- const tokenCacheKey = baseConfig.appKey || accountId
156
- const token = await getWorkclawAccessToken(tokenCacheKey, connConfig)
157
- await handleSkillsEvent(result.eventData, token, connConfig.baseUrl, connConfig.appKey || accountId, log)
158
- }
159
- catch (err) {
160
- logError(log, 'handleSkillsEvent failed', err)
161
- }
162
- }
163
-
164
- async function handleToolsListEvent(ctx: MessageDispatcherContext, result: ParseWorkClawResult): Promise<void> {
165
- const { accountId, baseConfig, log } = ctx
166
- if (!result.eventData)
167
- return
168
- try {
169
- const connConfig = {
170
- baseUrl: baseConfig.baseUrl || '',
171
- websocketUrl: baseConfig.websocketUrl,
172
- appKey: baseConfig.appKey,
173
- appSecret: baseConfig.appSecret,
174
- localIp: baseConfig.localIp,
175
- allowInsecureTls: baseConfig.allowInsecureTls,
176
- requestTimeout: baseConfig.requestTimeout,
177
- }
178
- const tokenCacheKey = baseConfig.appKey || accountId
179
- const token = await getWorkclawAccessToken(tokenCacheKey, connConfig)
180
- await doHandleToolsListEvent(result.eventData, connConfig.baseUrl, token, log)
181
- }
182
- catch (err) {
183
- logError(log, 'handleToolsListEvent failed', err)
184
- }
185
- }
186
-
187
- async function handleSkillsListEvent(ctx: MessageDispatcherContext, result: ParseWorkClawResult): Promise<void> {
188
- const { accountId, baseConfig, log } = ctx
189
- if (!result.eventData)
190
- return
191
- try {
192
- const connConfig = {
193
- baseUrl: baseConfig.baseUrl || '',
194
- websocketUrl: baseConfig.websocketUrl,
195
- appKey: baseConfig.appKey,
196
- appSecret: baseConfig.appSecret,
197
- localIp: baseConfig.localIp,
198
- allowInsecureTls: baseConfig.allowInsecureTls,
199
- requestTimeout: baseConfig.requestTimeout,
200
- }
201
- const tokenCacheKey = baseConfig.appKey || accountId
202
- const token = await getWorkclawAccessToken(tokenCacheKey, connConfig)
203
- await doHandleSkillsListEvent(result.eventData, connConfig.baseUrl, token, log)
204
- }
205
- catch (err) {
206
- logError(log, 'handleSkillsListEvent failed', err)
207
- }
208
- }
209
-
210
- async function handleInitAgentEvent(
211
- ctx: MessageDispatcherContext,
212
- result: ParseWorkClawResult,
213
- ): Promise<void> {
214
- const { accountId, cfg, baseConfig, log } = ctx
215
- const initStartedAt = Date.now()
216
- const eventData = result.eventData
217
-
218
- if (!eventData) {
219
- log?.warn?.(`init_agent received but no data`)
220
- return
221
- }
222
-
223
- log?.info?.(
224
- `init_agent accountId=${accountId} eventDataType=${typeof eventData} preview=${JSON.stringify(eventData).slice(0, 200)}`,
225
- )
226
-
227
- const connConfig = {
228
- baseUrl: baseConfig.baseUrl || '',
229
- websocketUrl: baseConfig.websocketUrl,
230
- appKey: baseConfig.appKey,
231
- appSecret: baseConfig.appSecret,
232
- localIp: baseConfig.localIp,
233
- allowInsecureTls: baseConfig.allowInsecureTls,
234
- requestTimeout: baseConfig.requestTimeout,
235
- }
236
-
237
- // Fetch apiKey and init agent config in one write
238
- try {
239
- const tokenCacheKey = baseConfig.appKey || accountId
240
- const { baseUrl: modelBaseUrl, apiKey } = await getWorkclawModelConfigByAppKey(tokenCacheKey, connConfig)
241
- const maskedKey = apiKey ? `${String(apiKey).slice(0, 6)}...(${String(apiKey).length})` : 'missing'
242
-
243
- log?.info?.(`init_agent model fetched baseUrl=${modelBaseUrl} apiKey=${maskedKey}`)
244
-
245
- const agentId = String(eventData?.id || eventData?.agentId || '')
246
- const userId = String(eventData?.futureId || eventData?.userId || '')
247
-
248
- await initWorkclawAgent({ apiKey, agentId, userId, accountId: 'default' }, cfg, log)
249
- log?.info?.(`init_agent config saved elapsedMs=${Date.now() - initStartedAt}`)
250
- }
251
- catch (err) {
252
- logError(log, 'init_agent failed', err)
253
- }
254
-
255
- log?.info?.(`init_agent handled elapsedMs=${Date.now() - initStartedAt}`)
256
- }
257
-
258
- async function handleCronTaskEvent(ctx: MessageDispatcherContext, result: ParseWorkClawResult): Promise<void> {
259
- const { accountId, baseConfig, log } = ctx
260
- if (!result.cronTaskEvent)
261
- return
262
- try {
263
- const { handleCronTaskEvent } = await import('./cron-tasks-handler.js')
264
- const connConfig = {
265
- baseUrl: baseConfig.baseUrl || '',
266
- websocketUrl: baseConfig.websocketUrl,
267
- appKey: baseConfig.appKey,
268
- appSecret: baseConfig.appSecret,
269
- localIp: baseConfig.localIp,
270
- allowInsecureTls: baseConfig.allowInsecureTls,
271
- requestTimeout: baseConfig.requestTimeout,
272
- }
273
- const tokenCacheKey = baseConfig.appKey || accountId
274
- const token = await getWorkclawAccessToken(tokenCacheKey, connConfig)
275
- await handleCronTaskEvent(result.cronTaskEvent, token, connConfig.baseUrl, connConfig.appKey || accountId, log)
276
- }
277
- catch (err) {
278
- logError(log, 'handleCronTaskEvent failed', err)
279
- }
280
- }
281
-
282
- async function handleAgentMessage(
283
- ctx: MessageDispatcherContext,
284
- data: string,
285
- result: ParseWorkClawResult,
286
- ): Promise<void> {
287
- const { accountId, account, cfg, accountConfig, log } = ctx
288
- const { message } = result
289
- if (!message)
290
- return
291
-
292
- // 这里的msgAgentId是云平台的agentId
293
- const { text, userId, openConversationId, messageId: parsedMessageId, agentId: msgAgentId } = message
294
-
295
- // Resolve account from message
296
- let messageAccountId = accountId
297
- let messageAccount = account
298
-
299
- if (userId && msgAgentId) {
300
- const resolvedAccountId = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(msgAgentId))
301
- if (resolvedAccountId) {
302
- const resolved = resolveWorkclawAccountWithCache({ cfg, accountId: resolvedAccountId })
303
- messageAccountId = resolvedAccountId
304
- messageAccount = resolved
305
- log?.info?.(`Resolved account ${resolvedAccountId} from message (userId=${userId}, agentId=${msgAgentId})`)
306
- }
307
- else if (String(msgAgentId) !== String(accountConfig.agentId)) {
308
- log?.warn?.(`Account not found for userId=${userId}, agentId=${msgAgentId}, ignoring`)
309
- return
310
- }
311
- }
312
-
313
- const resolvedAccountConfig = messageAccount.config as unknown as WorkClawAccountConfig
314
- const msgAgentIdResolved = msgAgentId ?? accountConfig.agentId
315
-
316
- // Save openConversationId if new
317
- if (openConversationId && openConversationId !== resolvedAccountConfig.openConversationId) {
318
- try {
319
- await saveOpenConversationId(messageAccountId, userId, openConversationId, cfg, log)
320
- log?.info?.(`Saved openConversationId ${openConversationId} to account ${messageAccountId}`)
321
- }
322
- catch (err) {
323
- logError(log, 'saveOpenConversationId failed', err)
324
- }
325
- }
326
-
327
- const resolvedOpenConversationId = openConversationId || resolvedAccountConfig.openConversationId || ''
328
- const to = userId
329
- const bodyText = text || data
330
-
331
- if (!bodyText) {
332
- log?.warn?.(`Empty body, ignoring`)
333
- return
334
- }
335
-
336
- // Route via bindings
337
- const bindings = cfg?.bindings ?? []
338
- let targetAgentId = 'default'
339
- const matchedBinding = bindings.find((binding: any) => {
340
- const match = binding?.match
341
- if (!match || match.channel !== 'openclaw-workclaw')
342
- return false
343
- return match.accountId === messageAccountId || match.accountId === '*'
344
- })
345
- if (matchedBinding) {
346
- targetAgentId = matchedBinding.agentId
347
- log?.info?.(`Routing to agent ${targetAgentId} via binding (account: ${messageAccountId})`)
348
- }
349
-
350
- if (!targetAgentId) {
351
- log?.warn?.(`Missing target, ignoring`)
352
- return
353
- }
354
-
355
- // 构建用于解析 sessionKey 的消息对象
356
- // to = agentId (智能体/机器人), from = userId (用户)
357
- const attachments = message.attachments
358
- const inboundMessage: InboundMessage = {
359
- text: bodyText,
360
- to,
361
- from: userId,
362
- chatType: 'dm',
363
- messageId: parsedMessageId,
364
- rawPayload: data,
365
- timestamp: Date.now(),
366
- attachments,
367
- }
368
- log?.info?.(`handleAgentMessage: text=${bodyText?.substring(0, 50)}, attachments=${attachments?.length || 0}, rawPayload=${data.substring(0, 300)}`)
369
- const pluginRuntime = getWorkclawRuntime()
370
-
371
- // 检测是否是框架命令(如 /new)
372
- const textForCommandCheck = bodyText?.replace(/^\[引用[^\]]*\]\s*/, '').trim() || ''
373
- const resolvedCommand = maybeResolveTextAlias(textForCommandCheck, messageAccount.config as any)
374
- const isFrameworkCommand = resolvedCommand !== null
375
- if (isFrameworkCommand) {
376
- log?.info?.(`[DELIVER] Framework command detected: ${bodyText}, will set CommandSource=native`)
377
- }
378
-
379
- // 使用扩展后的 buildInboundContext,它会处理完整的 session 流程
380
- const finalized = await buildInboundContext(
381
- inboundMessage,
382
- messageAccountId,
383
- messageAccount.config,
384
- targetAgentId,
385
- pluginRuntime,
386
- true,
387
- log,
388
- )
389
-
390
- // 如果是框架命令,设置 CommandSource 等字段,让 OpenClaw 框架识别为原生命令
391
- if (isFrameworkCommand) {
392
- const targetSessionKey = finalized.SessionKey as string
393
- const { sessionKey: commandSessionKey, commandTargetSessionKey } = resolveNativeCommandSessionTargets({
394
- agentId: targetAgentId,
395
- sessionPrefix: 'openclaw-workclaw:new',
396
- userId: String(userId ?? ''),
397
- targetSessionKey,
398
- });
399
- (finalized as any).SessionKey = commandSessionKey;
400
- (finalized as any).CommandTargetSessionKey = commandTargetSessionKey;
401
- (finalized as any).CommandSource = 'native';
402
- (finalized as any).CommandAuthorized = true
403
- log?.info?.(`[DELIVER] Set CommandSource=native, commandSessionKey=${commandSessionKey}, targetSessionKey=${targetSessionKey}`)
404
- }
405
-
406
- // 维护当前工具名称状态
407
- let currentToolName: string | null = null
408
- // 追踪是否有文本被 deliver 过
409
- let textDelivered = false
410
-
411
- // 发送工具消息的辅助函数
412
- const sendToolMessage = async (text: string, isStart: boolean = false) => {
413
- try {
414
- await sendMessageWorkclaw({
415
- cfg,
416
- to,
417
- text,
418
- accountId: messageAccountId,
419
- openConversationId: resolvedOpenConversationId,
420
- agentId: msgAgentIdResolved,
421
- replyToMessageId: parsedMessageId,
422
- last: false,
423
- source: 'deliver',
424
- })
425
- }
426
- catch (err) {
427
- logError(log, isStart ? 'Tool start message send failed' : 'Tool result message send failed', err)
428
- }
429
- }
430
-
431
- await pluginRuntime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
432
- ctx: finalized,
433
- cfg,
434
- replyOptions: {
435
- onAgentRunStart(runId: string) {
436
- // 保存 runId 和 ctx 的关联,供 hook 使用
437
- setToolContext(runId, {
438
- target: targetAgentId,
439
- replyToMessageId: parsedMessageId,
440
- openConversationId: resolvedOpenConversationId,
441
- accountId: messageAccountId,
442
- agentId: String(msgAgentIdResolved),
443
- sessionKey: (finalized.SessionKey as string) || '',
444
- })
445
- log?.info?.(`[AGENT] onAgentRunStart: runId=${runId} target=${targetAgentId}`)
446
- },
447
- /* onToolStart(payload) {
448
- log?.info?.(`[AGENT] onToolStart: ${JSON.stringify(payload)}`);
449
- if (payload.phase === "start") {
450
- currentToolName = payload.name ?? null;
451
- const hint = getToolStartHint(currentToolName);
452
- log?.info?.(`[TOOL] Starting tool: ${currentToolName}, sending hint: ${hint}`);
453
- sendToolMessage(hint, true).catch((err) => {
454
- logError(log, "Tool start hint send failed", err);
455
- });
456
- }
457
- } */
458
- },
459
- dispatcherOptions: {
460
- deliver: async (payload: any, info?: any) => {
461
- const resolvedReplyToId = payload?.replyToId || parsedMessageId
462
- const textOut = String(payload.text ?? payload.body ?? '')
463
- const target = to || String(payload.to ?? '')
464
- const kind = info?.kind
465
-
466
- if (!target) {
467
- log?.warn?.(`[DELIVER] skip: no target`)
468
- return
469
- }
470
-
471
- if (kind === 'tool') {
472
- // 工具结果已在工具 hook 中处理,此处跳过避免重复
473
- return
474
- }
475
-
476
- if (!textOut.trim()) {
477
- log?.warn?.(`[DELIVER] skip: empty text (kind=${kind})`)
478
- return
479
- }
480
-
481
- // 处理工具类型的 deliver
482
- /* if (info?.kind === "tool") {
483
- // log?.info?.(`[DELIVER] tool reply: ${JSON.stringify(payload)}`);
484
- const toolText = payload.text ?? "";
485
- if (toolText.trim()) {
486
- // 使用 formattedToolOutput 格式发送
487
- try {
488
- await sendMessageWorkclaw({
489
- cfg,
490
- to: targetAgentId,
491
- text: JSON.stringify({
492
- name: currentToolName || "Unknown",
493
- content: toolText,
494
- state: "result",
495
- }),
496
- msgType: "26",
497
- accountId: messageAccountId,
498
- openConversationId: resolvedOpenConversationId,
499
- agentId: msgAgentIdResolved,
500
- replyToMessageId: parsedMessageId,
501
- });
502
- } catch (err) {
503
- logError(log, "Tool result send failed", err);
504
- }
505
- }
506
- return;
507
- } */
508
-
509
- // last: true 统一由最后的 fallback 兜底发送,deliver 阶段全部传 false 避免重复触发
510
- try {
511
- const result = await sendMessageWorkclaw({
512
- cfg,
513
- to: target,
514
- text: textOut,
515
- accountId: messageAccountId,
516
- openConversationId: resolvedOpenConversationId,
517
- agentId: msgAgentIdResolved,
518
- replyToMessageId: resolvedReplyToId,
519
- last: false,
520
- source: 'deliver',
521
- })
522
- textDelivered = true
523
- log?.info?.(`[DELIVER] sent "${textOut.substring(0, 80)}" (last=false) kind=${kind} msgId=${result.messageId}`)
524
- }
525
- catch (err) {
526
- logError(log, 'Reply send failed', err)
527
- throw err
528
- }
529
- },
530
- },
531
- })
532
-
533
- currentToolName = null
534
-
535
- // 原始消息是命令但 AI 没有回复文本时,发送"命令已执行"确认
536
- if (!textDelivered && bodyText?.startsWith('/')) {
537
- log?.info?.(`[DELIVER] command ${bodyText} executed with no text reply, sending confirmation`)
538
- try {
539
- await sendMessageWorkclaw({
540
- cfg,
541
- to: to || String(userId || ''),
542
- text: `${bodyText} 已执行`,
543
- accountId: messageAccountId,
544
- openConversationId: resolvedOpenConversationId,
545
- agentId: msgAgentIdResolved,
546
- replyToMessageId: parsedMessageId,
547
- last: false,
548
- source: 'deliver',
549
- })
550
- }
551
- catch (err) {
552
- logError(log, 'Command confirmation send failed', err)
553
- }
554
- }
555
-
556
- // last: true 结束信号:每次回复最后都发,确保对话结束
557
- log?.info?.(`[DELIVER] sending last=true end signal`)
558
- try {
559
- await sendMessageWorkclaw({
560
- cfg,
561
- to: to || String(userId || ''),
562
- text: '',
563
- accountId: messageAccountId,
564
- openConversationId: resolvedOpenConversationId,
565
- agentId: msgAgentIdResolved,
566
- replyToMessageId: parsedMessageId,
567
- last: true,
568
- source: 'deliver',
569
- })
570
- }
571
- catch (err) {
572
- const errStr = String(err)
573
- if (errStr.includes("content can't be empty")) {
574
- log?.info?.(`[DELIVER] last=true skipped (empty content not supported)`)
575
- }
576
- else {
577
- logError(log, 'last=true signal send failed', err)
578
- }
579
- }
580
- }
581
-
582
- // ---------------------------------------------------------------------------
583
- // Main dispatcher
584
- // ---------------------------------------------------------------------------
585
-
586
- export async function dispatchWorkclawMessage(
587
- data: string,
588
- ctx: MessageDispatcherContext,
589
- ): Promise<void> {
590
- const { log } = ctx
591
-
592
- const rawHasAuthToken
593
- = data.includes('authToken')
594
- || data.toLowerCase().includes('authorization')
595
- || data.toLowerCase().includes('bearer ')
596
- const rawPreview = data.length > 300 ? `${data.slice(0, 300)}...` : data
597
- /* log?.info?.(
598
- `Raw message bytes=${data.length} hasAuthToken=${rawHasAuthToken} preview=${rawPreview}`,
599
- ); */
600
-
601
- const result = parseWorkClawMessage(data, log)
602
- if (!result) {
603
- log?.warn?.(`Failed to parse message`)
604
- return
605
- }
606
-
607
- log?.info?.(`Message type=${result.type}`)
608
-
609
- switch (result.type) {
610
- case 'ping':
611
- await handlePing(ctx, result)
612
- return
613
-
614
- case 'disconnect':
615
- await handleDisconnect(ctx)
616
- return
617
-
618
- case 'agent_created':
619
- case 'agent_updated':
620
- case 'agent_deleted':
621
- await handleAgentEvent(ctx, result)
622
- return
623
-
624
- case 'skills_event':
625
- await handleSkillsProcessingEvent(ctx, result)
626
- return
627
-
628
- case 'tools_list':
629
- await handleToolsListEvent(ctx, result)
630
- return
631
-
632
- case 'skills_list':
633
- await handleSkillsListEvent(ctx, result)
634
- return
635
-
636
- case 'init_agent':
637
- await handleInitAgentEvent(ctx, result)
638
- return
639
-
640
- case 'cron_task_event':
641
- await handleCronTaskEvent(ctx, result)
642
- return
643
-
644
- case 'ignored':
645
- log?.info?.(`Ignoring message type=${result.type}`)
646
- return
647
-
648
- case 'agent_message':
649
- await handleAgentMessage(ctx, data, result)
650
- return
651
-
652
- default:
653
- log?.info?.(`Unknown message type: ${result.type}, ignoring`)
654
- }
655
- }
656
-
657
- // 创建成功回调
658
- async function handleAgentCreatedMessage(baseConfig: WorkClawBaseConfig, agentId: string, accountId: string, log?: any) {
659
- // 通知
660
- try {
661
- const base = baseConfig.baseUrl.replace(/\/$/, '') // 去掉末尾多余的 /
662
- const callbackUrl = `${base}/open-apis/instance/agent-created/status`
663
- const tokenCacheKey = baseConfig.appKey || accountId
664
- const callbackData = {
665
- agentId,
666
- success: true,
667
- }
668
-
669
- log?.info?.(`[AgentCreated] Calling callback API: ${callbackUrl}`)
670
- const token = await getWorkclawAccessToken(tokenCacheKey, baseConfig)
671
-
672
- const response = await request(callbackUrl, {
673
- method: 'POST',
674
- headers: {
675
- 'Content-Type': 'application/json',
676
- 'Authorization': `Bearer ${token}`,
677
- },
678
- body: JSON.stringify(callbackData),
679
- })
680
-
681
- const responseBody = await response.body.text()
682
- log?.info?.(`[AgentCreated] Callback API response: status=${response.statusCode}, body=${responseBody}`)
683
- }
684
- catch (callbackErr) {
685
- // 接口调用失败不影响主流程,只记录错误
686
- log?.error?.(`[AgentCreated] Callback API failed: ${String(callbackErr)}`)
687
- }
688
- }