@dsh-plugin/dsh-synapse 0.0.0-stage → 0.1.0-dev.41044228975

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 (150) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE.md +18 -0
  3. package/README.md +136 -2
  4. package/cordis.patch.yml +3 -0
  5. package/lib/bot.cjs +8314 -0
  6. package/lib/bot.cjs.map +7 -0
  7. package/lib/build.json +1 -0
  8. package/lib/client.js +46 -0
  9. package/lib/host/agent.d.ts +21 -0
  10. package/lib/host/agent.js +151 -0
  11. package/lib/host/agent.js.map +1 -0
  12. package/lib/host/bot.d.ts +17 -0
  13. package/lib/host/bot.js +158 -0
  14. package/lib/host/bot.js.map +1 -0
  15. package/lib/host/client.d.ts +2 -0
  16. package/lib/host/client.js +19 -0
  17. package/lib/host/client.js.map +1 -0
  18. package/lib/host/config.d.ts +24 -0
  19. package/lib/host/config.js +192 -0
  20. package/lib/host/config.js.map +1 -0
  21. package/lib/host/files.d.ts +29 -0
  22. package/lib/host/files.js +188 -0
  23. package/lib/host/files.js.map +1 -0
  24. package/lib/host/gateway.d.ts +33 -0
  25. package/lib/host/gateway.js +237 -0
  26. package/lib/host/gateway.js.map +1 -0
  27. package/lib/host/github.d.ts +47 -0
  28. package/lib/host/github.js +219 -0
  29. package/lib/host/github.js.map +1 -0
  30. package/lib/host/http.d.ts +4 -0
  31. package/lib/host/http.js +41 -0
  32. package/lib/host/http.js.map +1 -0
  33. package/lib/host/index.d.ts +32 -0
  34. package/lib/host/index.js +192 -0
  35. package/lib/host/index.js.map +1 -0
  36. package/lib/host/inference-worker.d.ts +1 -0
  37. package/lib/host/inference-worker.js +72 -0
  38. package/lib/host/inference-worker.js.map +1 -0
  39. package/lib/host/mcp.d.ts +138 -0
  40. package/lib/host/mcp.js +104 -0
  41. package/lib/host/mcp.js.map +1 -0
  42. package/lib/host/memory.d.ts +26 -0
  43. package/lib/host/memory.js +155 -0
  44. package/lib/host/memory.js.map +1 -0
  45. package/lib/host/panel.d.ts +1 -0
  46. package/lib/host/panel.html +174 -0
  47. package/lib/host/panel.js +388 -0
  48. package/lib/host/panel.js.map +1 -0
  49. package/lib/host/runtime.d.ts +21 -0
  50. package/lib/host/runtime.js +145 -0
  51. package/lib/host/runtime.js.map +1 -0
  52. package/lib/host/types.d.ts +112 -0
  53. package/lib/host/types.js +2 -0
  54. package/lib/host/types.js.map +1 -0
  55. package/package.json +94 -4
  56. package/scripts/build.mjs +90 -0
  57. package/scripts/check.mjs +13 -0
  58. package/scripts/dump-npm-latest.mjs +30 -0
  59. package/scripts/model-smoke.mjs +25 -0
  60. package/src/agent.ts +203 -0
  61. package/src/bot.ts +173 -0
  62. package/src/client.ts +20 -0
  63. package/src/config.ts +217 -0
  64. package/src/files.ts +206 -0
  65. package/src/gateway.ts +276 -0
  66. package/src/github.ts +263 -0
  67. package/src/http.ts +41 -0
  68. package/src/index.ts +227 -0
  69. package/src/inference-worker.ts +89 -0
  70. package/src/mcp.ts +112 -0
  71. package/src/memory.ts +178 -0
  72. package/src/panel.html +174 -0
  73. package/src/panel.ts +440 -0
  74. package/src/runtime.ts +164 -0
  75. package/src/types.ts +106 -0
  76. package/test/core.test.mjs +279 -0
  77. package/test/fixtures/mcp-server.mjs +14 -0
  78. package/test/integration.test.mjs +253 -0
  79. package/test/panel.test.mjs +73 -0
  80. package/tsconfig.json +18 -0
  81. package/vendor/gugle-chat-bot/LICENSE +661 -0
  82. package/vendor/gugle-chat-bot/package.json +57 -0
  83. package/vendor/gugle-chat-bot/src/allow-rule.yml +9345 -0
  84. package/vendor/gugle-chat-bot/src/api.json +4285 -0
  85. package/vendor/gugle-chat-bot/src/command/index.ts +141 -0
  86. package/vendor/gugle-chat-bot/src/config/features.ts +548 -0
  87. package/vendor/gugle-chat-bot/src/config/index.ts +184 -0
  88. package/vendor/gugle-chat-bot/src/config/manager.ts +357 -0
  89. package/vendor/gugle-chat-bot/src/constants/index.ts +13 -0
  90. package/vendor/gugle-chat-bot/src/custom.ts +548 -0
  91. package/vendor/gugle-chat-bot/src/event/index.ts +118 -0
  92. package/vendor/gugle-chat-bot/src/features/admin/index.ts +414 -0
  93. package/vendor/gugle-chat-bot/src/features/bili/image/impl/video.ts +39 -0
  94. package/vendor/gugle-chat-bot/src/features/bili/image/index.ts +37 -0
  95. package/vendor/gugle-chat-bot/src/features/bili/index.ts +65 -0
  96. package/vendor/gugle-chat-bot/src/features/bili/template/style/video.css +97 -0
  97. package/vendor/gugle-chat-bot/src/features/bili/template/video.html +57 -0
  98. package/vendor/gugle-chat-bot/src/features/discord-bridge/index.ts +1128 -0
  99. package/vendor/gugle-chat-bot/src/features/discord-bridge/markdown.ts +105 -0
  100. package/vendor/gugle-chat-bot/src/features/discord-bridge/slash.ts +355 -0
  101. package/vendor/gugle-chat-bot/src/features/discord-bridge/source.ts +96 -0
  102. package/vendor/gugle-chat-bot/src/features/discord-bridge/template/message.html +14 -0
  103. package/vendor/gugle-chat-bot/src/features/discord-bridge/template/style/message.css +144 -0
  104. package/vendor/gugle-chat-bot/src/features/github/api.ts +93 -0
  105. package/vendor/gugle-chat-bot/src/features/github/binding.ts +161 -0
  106. package/vendor/gugle-chat-bot/src/features/github/image/impl/issue.ts +132 -0
  107. package/vendor/gugle-chat-bot/src/features/github/image/impl/pr.ts +144 -0
  108. package/vendor/gugle-chat-bot/src/features/github/image/impl/release.ts +160 -0
  109. package/vendor/gugle-chat-bot/src/features/github/image/index.ts +310 -0
  110. package/vendor/gugle-chat-bot/src/features/github/index.ts +302 -0
  111. package/vendor/gugle-chat-bot/src/features/github/template/issue.html +118 -0
  112. package/vendor/gugle-chat-bot/src/features/github/template/pull_request.html +125 -0
  113. package/vendor/gugle-chat-bot/src/features/github/template/release.html +53 -0
  114. package/vendor/gugle-chat-bot/src/features/github/template/style/issue.css +468 -0
  115. package/vendor/gugle-chat-bot/src/features/github/template/style/pull_request.css +469 -0
  116. package/vendor/gugle-chat-bot/src/features/github/template/style/release.css +412 -0
  117. package/vendor/gugle-chat-bot/src/features/hermes/client.ts +184 -0
  118. package/vendor/gugle-chat-bot/src/features/hermes/config.ts +210 -0
  119. package/vendor/gugle-chat-bot/src/features/hermes/context.ts +67 -0
  120. package/vendor/gugle-chat-bot/src/features/hermes/history.ts +86 -0
  121. package/vendor/gugle-chat-bot/src/features/hermes/index.ts +1533 -0
  122. package/vendor/gugle-chat-bot/src/features/hermes/message.ts +319 -0
  123. package/vendor/gugle-chat-bot/src/features/hermes/reaction.ts +181 -0
  124. package/vendor/gugle-chat-bot/src/features/hermes/render.ts +118 -0
  125. package/vendor/gugle-chat-bot/src/features/hermes/reply-units.ts +331 -0
  126. package/vendor/gugle-chat-bot/src/features/hermes/skills.ts +406 -0
  127. package/vendor/gugle-chat-bot/src/features/hermes/template/approval.html +30 -0
  128. package/vendor/gugle-chat-bot/src/features/hermes/template/progress.html +21 -0
  129. package/vendor/gugle-chat-bot/src/features/hermes/template/style/approval.css +81 -0
  130. package/vendor/gugle-chat-bot/src/features/hermes/template/style/progress.css +120 -0
  131. package/vendor/gugle-chat-bot/src/features/hermes/trigger.ts +86 -0
  132. package/vendor/gugle-chat-bot/src/features/hermes/types.ts +327 -0
  133. package/vendor/gugle-chat-bot/src/features/management/index.ts +156 -0
  134. package/vendor/gugle-chat-bot/src/features/minecraft/index.ts +127 -0
  135. package/vendor/gugle-chat-bot/src/features/modrinth/index.ts +71 -0
  136. package/vendor/gugle-chat-bot/src/features/parentheses/index.ts +117 -0
  137. package/vendor/gugle-chat-bot/src/features/peak-valley-timer/index.ts +452 -0
  138. package/vendor/gugle-chat-bot/src/features/poke/index.ts +24 -0
  139. package/vendor/gugle-chat-bot/src/features/version-tracker/index.ts +78 -0
  140. package/vendor/gugle-chat-bot/src/features/welcome/index.ts +58 -0
  141. package/vendor/gugle-chat-bot/src/github-ip.json +5814 -0
  142. package/vendor/gugle-chat-bot/src/image/index.ts +278 -0
  143. package/vendor/gugle-chat-bot/src/index.ts +695 -0
  144. package/vendor/gugle-chat-bot/src/logger/index.ts +54 -0
  145. package/vendor/gugle-chat-bot/src/result.json +359 -0
  146. package/vendor/gugle-chat-bot/src/type/bili.d.ts +825 -0
  147. package/vendor/gugle-chat-bot/src/type/event.d.ts +146 -0
  148. package/vendor/gugle-chat-bot/src/type/github.d.ts +446 -0
  149. package/vendor/gugle-chat-bot/src/type/index.d.ts +254 -0
  150. package/vendor/gugle-chat-bot/tsconfig.json +46 -0
@@ -0,0 +1,412 @@
1
+ * {
2
+ font-family: "Sarasa Fixed SC", sans-serif;
3
+ margin: 0;
4
+ box-sizing: border-box;
5
+ --fontStack-monospace: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
6
+ --fontStack-sansSerif: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"
7
+ }
8
+
9
+ body {
10
+ margin: 10px;
11
+ font-family: var(--fontStack-sansSerif), serif;
12
+ font-size: .875rem;
13
+ font-weight: 400;
14
+ color: #1f2328;
15
+ background-color: #fff;
16
+ line-height: 1.5;
17
+ padding-top: 0;
18
+ padding-bottom: 0;
19
+ }
20
+
21
+ .message {
22
+ display: inline-block;
23
+ }
24
+
25
+ .user {
26
+ display: inline-block;
27
+ font-weight: bold;
28
+ }
29
+
30
+ .repository_header {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: .5rem;
34
+ min-height: 52px;
35
+ padding: .65rem 1.2rem;
36
+ color: #f0f6fc;
37
+ background: #161b22;
38
+ font-size: 1rem;
39
+ line-height: 1.25;
40
+ overflow: hidden;
41
+ }
42
+
43
+ .repository_mark {
44
+ width: 30px;
45
+ height: 30px;
46
+ margin-right: .2rem;
47
+ flex: 0 0 auto;
48
+ fill: #f0f6fc;
49
+ }
50
+
51
+ .repository_owner,
52
+ .repository_name {
53
+ overflow: hidden;
54
+ text-overflow: ellipsis;
55
+ white-space: nowrap;
56
+ }
57
+
58
+ .repository_owner {
59
+ color: #c9d1d9;
60
+ }
61
+
62
+ .repository_separator {
63
+ color: #8b949e;
64
+ }
65
+
66
+ .repository_name {
67
+ color: #f0f6fc;
68
+ }
69
+
70
+ .repository_chevron {
71
+ display: inline-flex;
72
+ align-items: center;
73
+ margin-left: .1rem;
74
+ }
75
+
76
+ .repository_chevron svg {
77
+ width: 16px;
78
+ height: 16px;
79
+ fill: #8b949e;
80
+ }
81
+
82
+ .container {
83
+ padding: 0 1.5rem;
84
+ width: 100%;
85
+ max-width: 1280px;
86
+ display: block;
87
+ word-wrap: break-word;
88
+ }
89
+
90
+ .release_header {
91
+ padding: 1.5rem 0 1rem;
92
+ }
93
+
94
+ .release_title_row {
95
+ display: flex;
96
+ align-items: center;
97
+ flex-flow: wrap;
98
+ gap: .5rem;
99
+ }
100
+
101
+ .release_title {
102
+ font-size: 1.75rem;
103
+ font-weight: 600;
104
+ line-height: 1.25;
105
+ margin: 0;
106
+ }
107
+
108
+ /* 「Latest」徽章 */
109
+ .release_badge {
110
+ display: inline-flex;
111
+ align-items: center;
112
+ gap: .25rem;
113
+ padding: 0 .5rem;
114
+ height: 24px;
115
+ font-size: 12px;
116
+ font-weight: 600;
117
+ color: #1a7f37;
118
+ border: 1px solid #1a7f3755;
119
+ background-color: #1a7f371a;
120
+ border-radius: 999px;
121
+ white-space: nowrap;
122
+ }
123
+
124
+ .release_badge_prerelease {
125
+ color: #9a6700;
126
+ border-color: #9a670055;
127
+ background-color: #9a67001a;
128
+ }
129
+
130
+ .release_tag_row {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: .375rem;
134
+ margin-top: .5rem;
135
+ font-size: .875rem;
136
+ }
137
+
138
+ .release_tag_icon {
139
+ width: 16px;
140
+ height: 16px;
141
+ fill: #59636e;
142
+ flex: 0 0 auto;
143
+ }
144
+
145
+ .release_tag_name {
146
+ font-family: var(--fontStack-monospace), serif;
147
+ color: #0969da;
148
+ background-color: #0969da1a;
149
+ border-radius: 6px;
150
+ padding: .125rem .375rem;
151
+ font-size: .75rem;
152
+ }
153
+
154
+ .release_tag_meta {
155
+ color: #59636e;
156
+ font-size: .75rem;
157
+ }
158
+
159
+ .release_meta_row {
160
+ display: flex;
161
+ align-items: center;
162
+ gap: .25rem;
163
+ margin-top: .75rem;
164
+ color: #59636e;
165
+ font-size: .875rem;
166
+ }
167
+
168
+ .release_author {
169
+ color: #1f2328;
170
+ font-weight: 600;
171
+ }
172
+
173
+ .header_body_splitter {
174
+ margin-left: auto;
175
+ margin-right: auto;
176
+ margin-bottom: 1.5rem;
177
+ }
178
+
179
+ .release_body {
180
+ display: flex;
181
+ flex-direction: column;
182
+ gap: 1.5rem;
183
+ padding-bottom: 1.25rem;
184
+ }
185
+
186
+ .content_container {
187
+ border-width: 1px;
188
+ border-style: solid;
189
+ border-color: #d1d9e0;
190
+ border-radius: 6px;
191
+ min-width: 0;
192
+ }
193
+
194
+ .content_body {
195
+ padding: 1rem;
196
+
197
+ img {
198
+ max-width: 100%;
199
+ height: auto;
200
+ }
201
+ }
202
+
203
+ /* 正文中的 issue/PR 引用(与 issue 卡片一致) */
204
+ .issue-ref {
205
+ display: inline;
206
+ }
207
+
208
+ .issue-ref-icon {
209
+ display: inline-flex;
210
+ align-items: center;
211
+ vertical-align: -2px;
212
+ margin-right: 3px;
213
+ }
214
+
215
+ .issue-ref-title {
216
+ color: #0969da;
217
+ font-weight: 500;
218
+ text-decoration: none;
219
+ }
220
+
221
+ .issue-ref-number {
222
+ color: #0969da;
223
+ text-decoration: none;
224
+ margin-left: 3px;
225
+ white-space: nowrap;
226
+ }
227
+
228
+ p {
229
+ margin: 0 0 1rem;
230
+ }
231
+
232
+ code {
233
+ tab-size: 4;
234
+ font-family: var(--fontStack-monospace), serif;
235
+ white-space: break-spaces;
236
+ background-color: #818b981f;
237
+ border-radius: 6px;
238
+ margin: 0;
239
+ padding: .2em .4em;
240
+ font-size: 85%;
241
+ }
242
+
243
+ pre {
244
+ background-color: #f6f8fa;
245
+ border-radius: 6px;
246
+ padding: 1rem;
247
+ overflow: auto;
248
+ margin: 0 0 1rem;
249
+ }
250
+
251
+ pre code {
252
+ background: transparent;
253
+ padding: 0;
254
+ font-size: 85%;
255
+ }
256
+
257
+ blockquote {
258
+ color: #59636e;
259
+ border-left: .25em solid #d1d9e0;
260
+ padding: 0 1em;
261
+ margin: 0;
262
+ }
263
+
264
+ div {
265
+ min-height: 0;
266
+ }
267
+
268
+ /* Assets 区块 */
269
+ .assets_section {
270
+ border: 1px solid #d1d9e0;
271
+ border-radius: 6px;
272
+ overflow: hidden;
273
+ }
274
+
275
+ .assets_header {
276
+ display: flex;
277
+ align-items: center;
278
+ gap: .5rem;
279
+ padding: .75rem 1rem;
280
+ background-color: #f6f8fa;
281
+ border-bottom: 1px solid #d1d9e0b3;
282
+ }
283
+
284
+ .assets_title {
285
+ font-size: .875rem;
286
+ font-weight: 600;
287
+ margin: 0;
288
+ }
289
+
290
+ .assets_count {
291
+ color: #59636e;
292
+ font-size: .75rem;
293
+ }
294
+
295
+ .assets_list {
296
+ display: flex;
297
+ flex-direction: column;
298
+ }
299
+
300
+ .asset_item {
301
+ display: flex;
302
+ align-items: center;
303
+ gap: .5rem;
304
+ padding: .75rem 1rem;
305
+ border-bottom: 1px solid #d1d9e0b3;
306
+ font-size: .875rem;
307
+ }
308
+
309
+ .asset_item:last-child {
310
+ border-bottom: none;
311
+ }
312
+
313
+ .asset_icon {
314
+ width: 16px;
315
+ height: 16px;
316
+ fill: #59636e;
317
+ flex: 0 0 auto;
318
+ }
319
+
320
+ .asset_name {
321
+ color: #0969da;
322
+ font-weight: 600;
323
+ text-decoration: none;
324
+ overflow: hidden;
325
+ text-overflow: ellipsis;
326
+ white-space: nowrap;
327
+ }
328
+
329
+ .asset_size {
330
+ color: #59636e;
331
+ font-size: .75rem;
332
+ flex: 0 0 auto;
333
+ }
334
+
335
+ .asset_downloads {
336
+ color: #59636e;
337
+ font-size: .75rem;
338
+ flex: 0 0 auto;
339
+ }
340
+
341
+ .asset_spacer {
342
+ flex-grow: 1;
343
+ }
344
+
345
+ .empty_assets {
346
+ padding: .75rem 1rem;
347
+ color: #59636e;
348
+ font-size: .875rem;
349
+ }
350
+
351
+ h1, h2, h3 {
352
+ font-weight: 600;
353
+ }
354
+
355
+ h1 {
356
+ font-size: 1.5rem;
357
+ margin: 0 0 1rem;
358
+ padding-bottom: .3em;
359
+ border-bottom: 1px solid #d1d9e0b3;
360
+ }
361
+
362
+ h2 {
363
+ font-size: 1.25rem;
364
+ margin: 0 0 1rem;
365
+ padding-bottom: .3em;
366
+ border-bottom: 1px solid #d1d9e0b3;
367
+ }
368
+
369
+ h3 {
370
+ font-size: 1rem;
371
+ margin: 0 0 1rem;
372
+ }
373
+
374
+ ul, ol {
375
+ padding-left: 1.5rem;
376
+ margin: 0 0 1rem;
377
+ }
378
+
379
+ li + li {
380
+ margin-top: .25rem;
381
+ }
382
+
383
+ a {
384
+ color: #0969da;
385
+ text-decoration: none;
386
+ }
387
+
388
+ table {
389
+ border-collapse: collapse;
390
+ margin: 0 0 1rem;
391
+ width: auto;
392
+ }
393
+
394
+ th, td {
395
+ border: 1px solid #d1d9e0;
396
+ padding: .375rem .75rem;
397
+ }
398
+
399
+ th {
400
+ background-color: #f6f8fa;
401
+ font-weight: 600;
402
+ }
403
+
404
+ img {
405
+ max-width: 100%;
406
+ }
407
+
408
+ hr {
409
+ border: none;
410
+ border-top: 1px solid #d1d9e0b3;
411
+ margin: 1.5rem 0;
412
+ }
@@ -0,0 +1,184 @@
1
+ import { getHermesConfig } from '@/features/hermes/config';
2
+ import { ApprovalChoice, HermesEventCallbacks, MessageContentPart } from '@/features/hermes/types';
3
+
4
+ /** 提交运行参数 */
5
+ interface SubmitRunParams {
6
+ userMessage: string | MessageContentPart[];
7
+ sessionId?: string;
8
+ systemPrompt?: string;
9
+ conversationHistory?: Array<{ role: string; content: string }>;
10
+ }
11
+
12
+ /**
13
+ * Hermes API 客户端(移植自 qq-hermes-bridge src/hermes.ts,MIT 协议,原作者 Amorter)。
14
+ * 负责异步提交任务、监听 SSE 事件流、审批管理和任务停止。
15
+ * 配置每次调用时读取(ConfigStore 热重载生效)。
16
+ */
17
+ export class HermesClient {
18
+ private get baseUrl(): string {
19
+ return getHermesConfig().apiUrl;
20
+ }
21
+
22
+ /**
23
+ * 提交异步运行任务。
24
+ * @returns 包含 runId 和 status 的结果
25
+ */
26
+ public async submitRun({ userMessage, sessionId, systemPrompt, conversationHistory }: SubmitRunParams): Promise<{
27
+ runId: string;
28
+ status: string;
29
+ }> {
30
+ // 多模态消息:input 需传消息数组,最后一条携带图片内容段;纯文本保持原样
31
+ const body: Record<string, unknown> = {
32
+ input: Array.isArray(userMessage) ? [{ role: 'user', content: userMessage }] : userMessage
33
+ };
34
+ if (systemPrompt) body.instructions = systemPrompt;
35
+ if (sessionId) body.session_id = sessionId;
36
+ if (conversationHistory?.length) body.conversation_history = conversationHistory;
37
+
38
+ const resp = await fetch(`${this.baseUrl}/v1/runs`, {
39
+ method: 'POST',
40
+ headers: this.headers(),
41
+ body: JSON.stringify(body)
42
+ });
43
+
44
+ if (!resp.ok) {
45
+ const text = await resp.text().catch(() => '');
46
+ throw new Error(`submitRun 失败 ${resp.status}: ${text.slice(0, 200)}`);
47
+ }
48
+
49
+ const data = (await resp.json()) as Record<string, string>;
50
+ return {
51
+ runId: data.run_id || data.id || '',
52
+ status: data.status || ''
53
+ };
54
+ }
55
+
56
+ /**
57
+ * 连接 SSE 事件流,监听运行过程中的各类事件。
58
+ * @param runId 运行 ID
59
+ * @param callbacks 事件回调映射
60
+ * @returns 包含 abort 方法的控制器
61
+ */
62
+ public streamEvents(runId: string, callbacks: HermesEventCallbacks): { abort(): void } {
63
+ const url = `${this.baseUrl}/v1/runs/${runId}/events`;
64
+ const headers = this.headers({ Accept: 'text/event-stream' });
65
+
66
+ let aborted = false;
67
+ const controller = new AbortController();
68
+
69
+ const doConnect = async () => {
70
+ try {
71
+ const resp = await fetch(url, { headers, signal: controller.signal });
72
+ if (!resp.ok) {
73
+ const text = await resp.text().catch(() => '');
74
+ callbacks._error?.(new Error(`SSE ${resp.status}: ${text.slice(0, 200)}`));
75
+ return;
76
+ }
77
+
78
+ const reader = resp.body!.getReader();
79
+ const decoder = new TextDecoder();
80
+ let buffer = '';
81
+ let currentEvent = '';
82
+
83
+ while (!aborted) {
84
+ const { done, value } = await reader.read();
85
+ if (done) break;
86
+
87
+ buffer += decoder.decode(value, { stream: true });
88
+ const lines = buffer.split('\n');
89
+ buffer = lines.pop() || '';
90
+
91
+ for (const line of lines) {
92
+ // SSE event 行:记录当前事件类型
93
+ if (line.startsWith('event: ')) {
94
+ currentEvent = line.slice(7).trim();
95
+ continue;
96
+ }
97
+ if (line.startsWith('data: ')) {
98
+ const jsonStr = line.slice(6).trim();
99
+ if (!jsonStr) continue;
100
+ try {
101
+ const data = JSON.parse(jsonStr) as { event?: string } & Record<string, unknown>;
102
+ // 优先用 SSE event 行的类型,其次用 data.event
103
+ const eventType =
104
+ currentEvent || (data.event as string) || (data as Record<string, unknown>).type as string || 'unknown';
105
+
106
+ if (eventType) {
107
+ const handler = (callbacks as Record<string, ((ev: unknown) => void) | undefined>)[eventType];
108
+ if (handler) {
109
+ handler(data);
110
+ } else {
111
+ callbacks._any?.(data as never);
112
+ }
113
+ // run.completed / run.failed 也触发 _end
114
+ if (eventType === 'run.completed' || eventType === 'run.failed') {
115
+ callbacks._end?.();
116
+ }
117
+ }
118
+
119
+ currentEvent = ''; // 重置,等待下一个事件
120
+ } catch {
121
+ // 非 JSON 的 SSE 数据(心跳注释)
122
+ }
123
+ } else if (line.startsWith(': stream closed')) {
124
+ callbacks._end?.();
125
+ return;
126
+ }
127
+ }
128
+ }
129
+ callbacks._end?.();
130
+ } catch (err) {
131
+ if (!aborted) callbacks._error?.(err as Error);
132
+ }
133
+ };
134
+
135
+ void doConnect();
136
+
137
+ return {
138
+ /** 中止 SSE 连接 */
139
+ abort() {
140
+ aborted = true;
141
+ controller.abort();
142
+ }
143
+ };
144
+ }
145
+
146
+ /**
147
+ * 批准或拒绝待审批命令。
148
+ * @param runId 运行 ID
149
+ * @param choice 审批选择:once | deny | always | session
150
+ */
151
+ public async resolveApproval(runId: string, choice: ApprovalChoice): Promise<unknown> {
152
+ const resp = await fetch(`${this.baseUrl}/v1/runs/${runId}/approval`, {
153
+ method: 'POST',
154
+ headers: this.headers(),
155
+ body: JSON.stringify({ choice })
156
+ });
157
+ if (!resp.ok) {
158
+ const text = await resp.text().catch(() => '');
159
+ throw new Error(`审批处理失败 ${resp.status}: ${text.slice(0, 200)}`);
160
+ }
161
+ return resp.json();
162
+ }
163
+
164
+ /**
165
+ * 停止正在运行的 Agent 任务。
166
+ * @param runId 运行 ID
167
+ * @returns 是否成功停止
168
+ */
169
+ public async stopRun(runId: string): Promise<boolean> {
170
+ const resp = await fetch(`${this.baseUrl}/v1/runs/${runId}/stop`, {
171
+ method: 'POST',
172
+ headers: this.headers()
173
+ });
174
+ return resp.ok;
175
+ }
176
+
177
+ /** 构建请求头 */
178
+ private headers(extra: Record<string, string> = {}): Record<string, string> {
179
+ const headers: Record<string, string> = { 'Content-Type': 'application/json', ...extra };
180
+ const apiKey = getHermesConfig().apiKey;
181
+ if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
182
+ return headers;
183
+ }
184
+ }