@dcrays/dcgchat-test 0.6.2 → 0.6.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 (59) hide show
  1. package/index.js +304 -0
  2. package/openclaw.plugin.json +41 -12
  3. package/package.json +8 -35
  4. package/README.md +0 -167
  5. package/index.ts +0 -31
  6. package/src/agent.ts +0 -592
  7. package/src/bot.ts +0 -676
  8. package/src/channel/index.ts +0 -329
  9. package/src/channel/outboundMedia.ts +0 -144
  10. package/src/channel/outboundTarget.ts +0 -24
  11. package/src/channel/uploadMediaUrl.ts +0 -76
  12. package/src/cron/message.ts +0 -109
  13. package/src/cron/params.ts +0 -166
  14. package/src/cron/toolGuard.ts +0 -285
  15. package/src/cron/types.ts +0 -15
  16. package/src/gateway/index.ts +0 -430
  17. package/src/gateway/security.ts +0 -95
  18. package/src/gateway/socket.ts +0 -256
  19. package/src/libs/ali-oss-6.23.0.tgz +0 -0
  20. package/src/libs/axios-1.13.6.tgz +0 -0
  21. package/src/libs/md5-2.3.0.tgz +0 -0
  22. package/src/libs/mime-types-3.0.2.tgz +0 -0
  23. package/src/libs/unzipper-0.12.3.tgz +0 -0
  24. package/src/libs/ws-8.19.0.tgz +0 -0
  25. package/src/monitor.ts +0 -269
  26. package/src/request/api.ts +0 -80
  27. package/src/request/oss.ts +0 -200
  28. package/src/request/request.ts +0 -191
  29. package/src/request/userInfo.ts +0 -44
  30. package/src/session.ts +0 -28
  31. package/src/skill.ts +0 -114
  32. package/src/tool.ts +0 -625
  33. package/src/tools/messageTool.ts +0 -334
  34. package/src/tools/toolCallGuard.ts +0 -323
  35. package/src/transport.ts +0 -217
  36. package/src/types.ts +0 -139
  37. package/src/utils/agentErrors.ts +0 -23
  38. package/src/utils/constant.ts +0 -60
  39. package/src/utils/env-config.ts +0 -19
  40. package/src/utils/formatLlmInputEvent.ts +0 -48
  41. package/src/utils/gatewayMsgHandler.ts +0 -89
  42. package/src/utils/global.ts +0 -306
  43. package/src/utils/inboundTurnState.ts +0 -66
  44. package/src/utils/log.ts +0 -77
  45. package/src/utils/mediaAttached.ts +0 -244
  46. package/src/utils/mediaEmitter.ts +0 -54
  47. package/src/utils/outboundAssistantText.ts +0 -110
  48. package/src/utils/params.ts +0 -104
  49. package/src/utils/passTxt.ts +0 -4
  50. package/src/utils/resolveRegisterConfig.ts +0 -33
  51. package/src/utils/searchFile.ts +0 -228
  52. package/src/utils/sessionState.ts +0 -137
  53. package/src/utils/sessionTermination.ts +0 -228
  54. package/src/utils/streamMerge.ts +0 -150
  55. package/src/utils/subagentRunMap.ts +0 -71
  56. package/src/utils/workspaceFilePaths.ts +0 -18
  57. package/src/utils/wsMessageHandler.ts +0 -124
  58. package/src/utils/zipExtract.ts +0 -97
  59. package/src/utils/zipPath.ts +0 -24
package/src/transport.ts DELETED
@@ -1,217 +0,0 @@
1
- import { WebSocket } from 'ws'
2
- import { clearSentMediaKeys, getWsConnection, resolveOutboundMediaDedupeKey } from './utils/global.js'
3
- import { dcgLogger } from './utils/log.js'
4
- import type { IMsgParams } from './types.js'
5
- import { getEffectiveMsgParams, getParamsDefaults } from './utils/params.js'
6
-
7
- /** 用 sessionKey 从 map 取参,再合并 overrides(channel 出站、媒体等) */
8
- export function mergeSessionParams(sessionKey: string, overrides?: Partial<IMsgParams>): IMsgParams {
9
- const base = getEffectiveMsgParams(sessionKey)
10
- if (!overrides) return base
11
- return { ...base, ...overrides }
12
- }
13
- export function mergeDefaultParams(overrides?: Partial<IMsgParams>): IMsgParams {
14
- const base = getParamsDefaults()
15
- if (!overrides) return base
16
- return { ...base, ...overrides }
17
- }
18
-
19
- export type InboundMsgForContext = {
20
- _userId: number | string
21
- content: {
22
- bot_token: string
23
- domain_id?: string
24
- app_id?: string
25
- bot_id?: string
26
- agent_id?: string
27
- session_id: string
28
- message_id: string
29
- }
30
- }
31
-
32
- export type OpenclawBotChatEnvelope = {
33
- messageType: 'openclaw_bot_chat'
34
- _userId: number | undefined
35
- source: 'client'
36
- content: Record<string, unknown>
37
- }
38
-
39
- function formatWsEnvelopeForLog(envelope: OpenclawBotChatEnvelope): string {
40
- const c = envelope.content
41
- const sid = c.session_id
42
- const mid = c.message_id
43
- const state = c.state
44
- const r = c.response
45
- const files = c.files
46
- const respLen = typeof r === 'string' ? r.length : 0
47
- const fileHint = Array.isArray(files) ? ` files=${files.length}` : ''
48
- return `[ws] ${envelope.messageType} session_id=${sid} message_id=${mid} state=${String(state)} responseLen=${respLen}${fileHint}`
49
- }
50
-
51
- function isInboundWire(arg: unknown): arg is InboundMsgForContext {
52
- return Boolean(arg && typeof arg === 'object' && '_userId' in arg && 'content' in arg)
53
- }
54
-
55
- /** 下行 WebSocket 帧 → 内部上下文(字段缺省用 channel 配置补) */
56
- function inboundToCtx(msg: InboundMsgForContext, d: IMsgParams): IMsgParams {
57
- const c = msg.content
58
- return {
59
- userId: Number(msg._userId ?? d.userId),
60
- botToken: c.bot_token ?? d.botToken,
61
- domainId: String(c.domain_id ?? d.domainId),
62
- appId: String(c.app_id ?? d.appId),
63
- botId: c.bot_id,
64
- agentId: c.agent_id,
65
- sessionId: c.session_id,
66
- messageId: c.message_id
67
- }
68
- }
69
-
70
- /** 上行:与配置合并缺省后再 `...ctx` 覆盖(原 wsSendRaw) */
71
- function mergeOutboundWithDefaults(ctx: IMsgParams, d: IMsgParams): IMsgParams {
72
- return {
73
- userId: Number(ctx.userId ?? d.userId),
74
- botToken: ctx.botToken ?? d.botToken,
75
- domainId: String(ctx.domainId ?? d.domainId),
76
- appId: String(ctx.appId ?? d.appId),
77
- ...ctx
78
- }
79
- }
80
-
81
- /**
82
- * 组装完整 wire `content` 对象:先写会话/机器人基础字段(回落到 d),再合并调用方传入的 payload。
83
- * `content` 在使用处构造(如 response、state、files),同名键可覆盖基础字段。
84
- */
85
- export function buildWireContent(base: IMsgParams, d: IMsgParams, content: Record<string, unknown>): Record<string, unknown> {
86
- return {
87
- bot_token: base.botToken ?? d.botToken,
88
- domain_id: base.domainId ?? d.domainId,
89
- app_id: base.appId ?? d.appId,
90
- bot_id: base.botId,
91
- agent_id: base.agentId,
92
- session_id: base.sessionId,
93
- message_id: base.messageId || Date.now().toString(),
94
- ...content
95
- }
96
- }
97
-
98
- /** 上行:在已合并的 ctx 上套 openclaw_bot_chat 信封(messageType / _userId / source + content) */
99
- function buildOutboundOpenclawBotChatEnvelope(
100
- ctx: IMsgParams,
101
- content: Record<string, unknown>,
102
- opts?: { mergeChannelDefaults?: boolean }
103
- ): OpenclawBotChatEnvelope {
104
- const d = getParamsDefaults()
105
- const base = opts?.mergeChannelDefaults ? mergeOutboundWithDefaults(ctx, d) : ctx
106
- return {
107
- messageType: 'openclaw_bot_chat',
108
- _userId: base.userId,
109
- source: 'client',
110
- content: buildWireContent(base, d, content)
111
- }
112
- }
113
-
114
- /**
115
- * 下行解析为 DcgchatMsgContext,或上行组装 openclaw_bot_chat 信封。
116
- * 上行时 `content` 由调用方传入;基础参数来自 `ctx` 与 `getParamsDefaults()`(可选 mergeChannelDefaults,同原 wsSendRaw)。
117
- */
118
- export function buildOpenclawBotChat(msg: InboundMsgForContext): IMsgParams
119
- export function buildOpenclawBotChat(
120
- ctx: IMsgParams,
121
- content: Record<string, unknown>,
122
- opts?: { mergeChannelDefaults?: boolean }
123
- ): OpenclawBotChatEnvelope
124
- export function buildOpenclawBotChat(
125
- arg1: InboundMsgForContext | IMsgParams,
126
- arg2?: Record<string, unknown>,
127
- opts?: { mergeChannelDefaults?: boolean }
128
- ): IMsgParams | OpenclawBotChatEnvelope {
129
- const d = getParamsDefaults()
130
-
131
- if (arg2 === undefined && isInboundWire(arg1)) {
132
- return inboundToCtx(arg1, d)
133
- }
134
-
135
- const ctx = arg1 as IMsgParams
136
- return buildOutboundOpenclawBotChatEnvelope(ctx, arg2 ?? {}, opts)
137
- }
138
-
139
- export function isWsOpen(): boolean {
140
- const isOpen = getWsConnection()?.readyState === WebSocket.OPEN
141
- if (!isOpen) {
142
- dcgLogger(`server socket not ready ${getWsConnection()?.readyState}`, 'error')
143
- }
144
- return isOpen
145
- }
146
-
147
- /**
148
- * 聊天流路径:content 单独 JSON.stringify(双重编码),符合 dcgchat 协议。
149
- * `ctx` 须由调用方用 getEffectiveMsgParams(sessionKey) 等解析好;`content` 为完整业务 payload。
150
- */
151
- export function wsSend(ctx: IMsgParams, content: Record<string, unknown>, isLog = true): boolean {
152
- const ws = getWsConnection()
153
- if (ws?.readyState !== WebSocket.OPEN) return false
154
- const envelope = buildOpenclawBotChat(ctx, content)
155
- ws.send(JSON.stringify({ ...envelope, content: JSON.stringify(envelope.content) }))
156
- if (isLog) {
157
- dcgLogger('[插件=>后端]-[wsSend]:' + formatWsEnvelopeForLog(envelope))
158
- }
159
- return true
160
- }
161
-
162
- /**
163
- * 媒体 / channel 出站:content 保持嵌套对象(单次编码)。
164
- * `ctx` 须由调用方解析(如需合并覆盖可先 mergeSessionParams)。
165
- */
166
- export function wsSendRaw(ctx: IMsgParams, content: Record<string, unknown>, isLog = true): boolean {
167
- const ws = getWsConnection()
168
- if (ws?.readyState !== WebSocket.OPEN) {
169
- dcgLogger(`server socket not ready ${ws?.readyState}`, 'error')
170
- return false
171
- }
172
- const envelope = buildOpenclawBotChat(ctx, content, { mergeChannelDefaults: true })
173
- ws.send(JSON.stringify(envelope))
174
- if (isLog) {
175
- dcgLogger('[插件=>后端]-[wsSendRaw]:' + formatWsEnvelopeForLog(envelope))
176
- }
177
- return true
178
- }
179
-
180
- export function sendChunk(text: string, ctx: IMsgParams, chunkIdx: number): boolean {
181
- return wsSend(ctx, { response: text, state: 'chunk', chunk_idx: chunkIdx }, false)
182
- }
183
-
184
- export function sendFinal(ctx: IMsgParams, tag: string, text?: string, params?: Record<string, unknown>): boolean {
185
- dcgLogger(` message handling complete state: to=${ctx.sessionId} final tag:${tag}`)
186
- const mediaBucket = resolveOutboundMediaDedupeKey(ctx.messageId, ctx.sessionId, ctx.sessionKey)
187
- if (mediaBucket) clearSentMediaKeys(mediaBucket)
188
- return wsSend(ctx, { response: text || '', state: 'final', ...(params || {}) }, false)
189
- }
190
-
191
- export function sendText(text: string, ctx: IMsgParams, event?: Record<string, unknown>): boolean {
192
- return wsSend(ctx, { response: text, ...event })
193
- }
194
-
195
- export function sendError(errorMsg: string, ctx: IMsgParams): boolean {
196
- return wsSend(ctx, { response: `[错误] ${errorMsg}`, state: 'final' })
197
- }
198
-
199
- export function sendEventMessage(params: Record<string, any> = {}) {
200
- const ctx = getParamsDefaults()
201
- const ws = getWsConnection()
202
- if (isWsOpen()) {
203
- ws?.send(
204
- JSON.stringify({
205
- messageType: 'openclaw_bot_event',
206
- source: 'client',
207
- content: {
208
- bot_token: ctx.botToken,
209
- domain_id: ctx.domainId,
210
- app_id: ctx.appId,
211
- bot_id: ctx.botId,
212
- ...params
213
- }
214
- })
215
- )
216
- }
217
- }
package/src/types.ts DELETED
@@ -1,139 +0,0 @@
1
- /**
2
- * 插件配置(channels.dcgchat 下的字段)
3
- */
4
- export type DcgchatConfig = {
5
- enabled?: boolean
6
- /** 后端 WebSocket 地址,例如 ws://localhost:8080/openclaw/ws */
7
- wsUrl?: string
8
- /** 连接认证 token */
9
- botToken?: string
10
- /** 用户标识 */
11
- userId?: string
12
- domainId?: string
13
- appId?: string
14
- /**
15
- * 内置 `message` 工具走 OpenClaw 目标解析:`true`(默认)时仅将符合 sessionKey 形态的字符串视为合法 target,
16
- * 纯数字(WS userId 等)会解析失败;设为 `false` 恢复旧版宽松行为(不推荐)。
17
- */
18
- strictMessageToolTarget?: boolean
19
- }
20
-
21
- export type ResolvedDcgchatAccount = {
22
- accountId: string
23
- enabled: boolean
24
- configured: boolean
25
- wsUrl: string
26
- botToken: string
27
- userId: string
28
- domainId?: string
29
- appId?: string
30
- }
31
-
32
- /**
33
- * 下行消息:后端 → OpenClaw(用户发的消息)
34
- */
35
- // export type InboundMessage = {
36
- // type: "message";
37
- // userId: string;
38
- // text: string;
39
- // };
40
- export type InboundMessage = {
41
- messageType: string // "openclaw_bot_chat",
42
- _userId: number
43
- source: string // 'server',
44
- // content: string;
45
- content: {
46
- skills_scope?: Record<string, any>[]
47
- bot_token: string
48
- agent_clone_code?: string
49
- domain_id?: string
50
- app_id?: string
51
- bot_id?: string
52
- agent_id?: string
53
- session_id: string
54
- real_mobook: string | number
55
- message_id: string
56
- text: string
57
- files?: {
58
- url: string
59
- name: string
60
- }[]
61
- }
62
- }
63
-
64
- // {"_userId":40,"content":"{\"bot_token\":\"sk_b7f8a3e1c5d24e6f8a1b3c4d5e6f7a8b\",\"session_id\":\"1\",\"message_id\":\"1\",\"text\":\"你好\"}","messageType":"openclaw_bot_chat","msgId":398599,"source":"server","title":"OPENCLAW机器人对话"}
65
-
66
- /**
67
- * 上行消息:OpenClaw → 后端(Agent 回复)
68
- */
69
- // export type OutboundReply = {
70
- // type: "reply";
71
- // userId: string;
72
- // text: string;
73
- // };
74
-
75
- export type OutboundReply = {
76
- messageType: string // "openclaw_bot_chat",
77
- _userId: number // 100
78
- source: string // 'client',
79
- // content: string;
80
- content: {
81
- bot_token: string // ""
82
- session_id: string // ""
83
- message_id: string // ""
84
- response: string // ""
85
- state: string // final, chunk
86
- domain_id?: string
87
- app_id?: string
88
- bot_id?: string
89
- agent_id?: string
90
- files?: { url: string; name: string }[]
91
- }
92
- }
93
-
94
- export interface IResponse<T = unknown> {
95
- /** 响应状态码 */
96
- code?: number | string
97
- /** 响应数据 */
98
- data?: T
99
- /** 响应消息 */
100
- message?: string
101
- }
102
-
103
- export interface IStsToken {
104
- bucket: string
105
- endPoint: string
106
- expiration: string
107
- ossFileKey: string
108
- policy: string
109
- region: string
110
- signature: string
111
- sourceFileName: string
112
- stsEndPoint: string
113
- tempAccessKeyId: string
114
- tempAccessKeySecret: string
115
- tempSecurityToken: string
116
- uploadDir: string
117
- protocol: string
118
- }
119
-
120
- export interface IStsTokenReq {
121
- sourceFileName: string
122
- isPrivate: number
123
- }
124
-
125
- export interface IMsgParams {
126
- userId?: number
127
- botToken?: string
128
- sessionId?: string
129
- messageId?: string
130
- domainId?: string
131
- appId?: string
132
- botId?: string
133
- agentId?: string
134
- /** 与 OpenClaw 路由一致,用于 map 与异步链路(工具 / HTTP / cron)对齐当前会话 */
135
- sessionKey?: string
136
- real_mobook?: string | number
137
- is_finish?: number
138
- message_tags?: Record<string, string>
139
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * 识别 OpenClaw embedded agent 抛出的上下文/压缩相关错误(日志与异常文案可能略有差异)。
3
- */
4
- function errorText(err: unknown): string {
5
- if (err instanceof Error) return err.message
6
- if (typeof err === 'string') return err
7
- return String(err)
8
- }
9
-
10
- export function isContextOverflowError(err: unknown): boolean {
11
- const t = errorText(err)
12
- return (
13
- /context overflow/i.test(t) ||
14
- /prompt too large/i.test(t) ||
15
- /auto-compaction failed/i.test(t) ||
16
- /\(precheck\)/i.test(t)
17
- )
18
- }
19
-
20
- /** 用户可见说明(不含 transport 层前缀) */
21
- export function contextOverflowUserHint(): string {
22
- return '当前对话过长,已超过模型上下文限制;自动压缩未完全生效时会出现此情况。请尝试新开对话、缩短任务,或换用更大上下文的模型。'
23
- }
@@ -1,60 +0,0 @@
1
- import { ENV_CONFIG, DEFAULT_BUILD_ENV, BuildEnv } from './env-config.js'
2
-
3
- declare const __ENV__: BuildEnv
4
- declare const __CHANNEL_ID__: string
5
-
6
- // esbuild define 会将 __ENV__ / __CHANNEL_ID__ 内联为字面量
7
-
8
- export const ENV: BuildEnv =
9
- typeof __ENV__ !== 'undefined' ? __ENV__ : DEFAULT_BUILD_ENV
10
- export const CHANNEL_ID: string =
11
- typeof __CHANNEL_ID__ !== 'undefined'
12
- ? __CHANNEL_ID__
13
- : ENV_CONFIG[DEFAULT_BUILD_ENV].channelId
14
-
15
- export const systemCommand = ['/new', '/status']
16
- export const stopCommand = ['/stop']
17
-
18
- export const ignoreToolCommand = ['/search', '/abort', '/queue interrupt', ...systemCommand, ...stopCommand]
19
-
20
- /* ── 网络与定时器统一常量 ── */
21
-
22
- /** WebSocket 心跳/Ping 间隔(毫秒) */
23
- export const WS_HEARTBEAT_INTERVAL_MS = 30_000
24
-
25
- /** WebSocket 重连延迟(毫秒) */
26
- export const WS_RECONNECT_DELAY_MS = 5_000
27
-
28
- /** OpenClaw Gateway WebSocket 握手协议版本(须与宿主 gateway 一致,当前为 v4) */
29
- export const GATEWAY_PROTOCOL_VERSION = 4
30
-
31
- /** /stop 后立即发新消息时,等待宿主 reply run 释放的 settle 时间(毫秒) */
32
- export const AFTER_STOP_SETTLE_MS = 50
33
-
34
- /** 等待子 agent 空闲的最大超时时间(毫秒) */
35
- export const SUBAGENT_IDLE_TIMEOUT_MS = 60 * 60 * 1000
36
-
37
- /* ── 通用重试工具 ── */
38
-
39
- /**
40
- * 带指数退避的重试函数
41
- * @param fn 需要重试的异步操作
42
- * @param maxAttempts 最大尝试次数(默认3)
43
- * @param initialDelayMs 初始延迟毫秒(默认500)
44
- */
45
- export async function retryWithBackoff<T>(
46
- fn: () => Promise<T>,
47
- maxAttempts: number = 3,
48
- initialDelayMs: number = 500
49
- ): Promise<T> {
50
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
51
- try {
52
- return await fn()
53
- } catch (err) {
54
- if (attempt === maxAttempts - 1) throw err
55
- const delay = initialDelayMs * Math.pow(2, attempt)
56
- await new Promise((resolve) => setTimeout(resolve, delay))
57
- }
58
- }
59
- throw new Error('retryWithBackoff: unreachable')
60
- }
@@ -1,19 +0,0 @@
1
- export type BuildEnv = 'production' | 'test'
2
-
3
- export interface EnvConfig {
4
- packageName: string
5
- channelId: string
6
- }
7
-
8
- export const ENV_CONFIG: Record<BuildEnv, EnvConfig> = {
9
- production: {
10
- packageName: '@dcrays/dcgchat',
11
- channelId: 'dcgchat'
12
- },
13
- test: {
14
- packageName: '@dcrays/dcgchat-test',
15
- channelId: 'dcgchat-test'
16
- }
17
- }
18
-
19
- export const DEFAULT_BUILD_ENV: BuildEnv = 'test'
@@ -1,48 +0,0 @@
1
- export function formatLlmInputEvent(event: Record<string, unknown>, ctx: any): string {
2
- try {
3
- const runId = String(event.runId ?? '?')
4
- const sessionId = String(event.sessionId ?? '?')
5
- const provider = String(event.provider ?? '?')
6
- const model = String(event.model ?? '?')
7
- const imagesCount = Number(event.imagesCount ?? 0)
8
- const historyLen = Array.isArray(event.historyMessages) ? event.historyMessages.length : 0
9
- const prompt = String(event.prompt ?? '')
10
- const systemPrompt = event.systemPrompt != null ? String(event.systemPrompt) : ''
11
-
12
- const sessionKey = ctx?.sessionKey || ''
13
- const agentId = ctx?.agentId || ''
14
-
15
- const lines: string[] = []
16
- lines.push(`===== llm_input =====`)
17
- lines.push(
18
- `sessionKey=${sessionKey} agentId=${agentId} runId=${runId} sessionId=${sessionId} provider=${provider} model=${model} images=${imagesCount} historyMessages=${historyLen} systemPromptLenght=${systemPrompt.length} promptLength=${prompt.length}`
19
- )
20
-
21
- const sampleText = extractTextPreview(prompt, 200)
22
- lines.push(`[TEXT] ${sampleText}${sampleText.length === 200 ? '...' : ''}`)
23
- return lines.join('\n')
24
- } catch (error) {
25
- return `formatLlmInputEvent Error: ${error}`
26
- }
27
- }
28
-
29
- function extractTextPreview(prompt: string, maxLen: number): string {
30
- let result = ''
31
-
32
- for (const seg of prompt.split('\n\n')) {
33
- const raw = seg.trim()
34
- if (!raw) continue
35
- if (raw.startsWith('### Inbound message metadata')) continue
36
- if (raw.startsWith('### Chat history since last reply')) continue
37
- if (raw.startsWith('[media attached:')) continue
38
- if (raw.startsWith('Conversation info')) continue
39
- if (raw.startsWith('Sender (untrusted metadata)')) continue
40
- if (raw.startsWith('```json') && raw.includes('message_id')) continue
41
- if (raw.includes('[media:') || raw.includes('<media:') || raw.includes('<file ')) continue
42
-
43
- if (result) result += '\n\n'
44
- result += raw
45
- if (result.length >= maxLen) break
46
- }
47
- return result.slice(0, maxLen).replace(/\n/g, ' ')
48
- }
@@ -1,89 +0,0 @@
1
- import type { GatewayEvent } from '../gateway/index.js'
2
- import { finishedDcgchatCron } from '../cron/message.js'
3
- import { cronAddHandler } from '../cron/params.js'
4
- import { sendGatewayRpc } from '../gateway/socket.js'
5
- import { dcgLogger } from './log.js'
6
- import { getEffectiveMsgParams } from './params.js'
7
- import { sendChunk, sendEventMessage } from '../transport.js'
8
- import { setCronSessionKeyMap } from '../channel/outboundTarget.js'
9
- import { resolveRequesterSessionKeyBySubagentRunId } from './subagentRunMap.js'
10
- import { isSessionActiveForTool } from '../tool.js'
11
- import { isSessionStreamSuppressed } from './sessionTermination.js'
12
- import { takeStreamChunkIdxAndAdvance } from './inboundTurnState.js'
13
-
14
- /**
15
- * 处理网关 event 帧的副作用(agent 流式输出、cron 同步),并构造供上层分发的 GatewayEvent。
16
- */
17
- export async function handleGatewayEventMessage(msg: {
18
- event?: string
19
- payload?: Record<string, unknown>
20
- seq?: number
21
- }): Promise<GatewayEvent> {
22
- try {
23
- // 子agent消息输出
24
- if (msg.event === 'agent') {
25
- const pl = msg.payload as { runId: string; data?: { delta?: unknown } }
26
- const sessionKey = resolveRequesterSessionKeyBySubagentRunId(pl.runId)
27
- if (!sessionKey) return { type: msg.event as string, payload: msg.payload, seq: msg.seq as number | undefined }
28
- if (!isSessionActiveForTool(sessionKey)) {
29
- dcgLogger(`[Gateway] drop subagent delta: session not active, runId=${pl.runId}, sessionKey=${sessionKey}`)
30
- return { type: msg.event as string, payload: msg.payload, seq: msg.seq as number | undefined }
31
- }
32
- if (isSessionStreamSuppressed(sessionKey)) {
33
- dcgLogger(`[Gateway] drop subagent delta: stream suppressed, runId=${pl.runId}, sessionKey=${sessionKey}`)
34
- return { type: msg.event as string, payload: msg.payload, seq: msg.seq as number | undefined }
35
- }
36
- if (typeof pl.data?.delta !== 'string' || !pl.data.delta) {
37
- return { type: msg.event as string, payload: msg.payload, seq: msg.seq as number | undefined }
38
- }
39
- const outboundCtx = getEffectiveMsgParams(sessionKey)
40
- if (outboundCtx.sessionId) {
41
- const chunkIdx = takeStreamChunkIdxAndAdvance(sessionKey)
42
- sendChunk(pl.data.delta, outboundCtx, chunkIdx)
43
- }
44
- }
45
- // 定时任务
46
- if (msg.event === 'cron') {
47
- const p = msg.payload as Record<string, any>
48
- if (p?.action === 'started') {
49
- setCronSessionKeyMap((p?.sessionKey || p.job?.sessionKey) as string)
50
- dcgLogger(`[Gateway] 定时任务执行开始: ${JSON.stringify(p)}`)
51
- } else {
52
- dcgLogger(`[Gateway] 收到定时任务事件: ${JSON.stringify(p)}`)
53
- }
54
- if (p?.action === 'added') {
55
- cronAddHandler(p?.jobId as string)
56
- }
57
- if (p?.action === 'updated') {
58
- const data = (await sendGatewayRpc({ method: 'cron.get', params: { id: p?.jobId as string } })) as Record<string, any>
59
- const params = {
60
- event_type: 'cron',
61
- operation_type: 'install',
62
- data
63
- }
64
- sendEventMessage(params)
65
- }
66
- if (p?.action === 'removed') {
67
- const params = {
68
- event_type: 'cron',
69
- operation_type: 'remove',
70
- data: { ...p }
71
- }
72
- sendEventMessage(params)
73
- }
74
- if (p?.action === 'finished') {
75
- const jobId = typeof p?.jobId === 'string' ? p.jobId.trim() : ''
76
- const summary = typeof p?.summary === 'string' ? p.summary : ''
77
- const text = p?.status !== 'ok' ? '定时任务执行失败' + (summary ? `\n ${summary}` : '') : ''
78
- finishedDcgchatCron(jobId, text)
79
- }
80
- }
81
- } catch (error) {
82
- dcgLogger(`[Gateway] 处理事件失败: ${error}`, 'error')
83
- }
84
- return {
85
- type: msg.event as string,
86
- payload: msg.payload,
87
- seq: msg.seq as number | undefined
88
- }
89
- }