@hopgoldy/agent-bridge 0.3.1 → 0.4.0
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/README.md +2 -0
- package/dist/agent-bridge.js +585 -70
- package/dist/cli.js +585 -70
- package/dist/index.d.ts +62 -2
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import process2 from "process";
|
|
5
|
+
import { createRequire } from "module";
|
|
5
6
|
import { Command } from "commander";
|
|
6
7
|
|
|
7
8
|
// src/config/prompt.ts
|
|
@@ -106,6 +107,115 @@ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from
|
|
|
106
107
|
import os2 from "os";
|
|
107
108
|
import path2 from "path";
|
|
108
109
|
|
|
110
|
+
// src/i18n/index.ts
|
|
111
|
+
import i18next from "i18next";
|
|
112
|
+
var DEFAULT_LOCALE = "en-US";
|
|
113
|
+
var resources = {
|
|
114
|
+
"en-US": {
|
|
115
|
+
translation: {
|
|
116
|
+
progress: {
|
|
117
|
+
noProgress: "No progress yet.",
|
|
118
|
+
collapsed: "Collapsed {{count}} earlier updates.",
|
|
119
|
+
running: "\u23F3 {{subject}}",
|
|
120
|
+
finished: "\u2705 {{subject}}",
|
|
121
|
+
failed: "\u274C {{subject}}",
|
|
122
|
+
failedWithDetail: "\u274C {{subject}}: {{detail}}",
|
|
123
|
+
compacting: "Compacting session",
|
|
124
|
+
compactingWithDetail: "Compacting session: {{detail}}"
|
|
125
|
+
},
|
|
126
|
+
client: {
|
|
127
|
+
processing: "Processing...",
|
|
128
|
+
helpMessage: "Available commands:\n\n- `/new` (`/n`) - Start a new agent session\n- `/compact` (`/c`) - Compact the current session context\n- `/stop` (`/s`) - Stop the active agent run\n- `/status` (`/st`) - Show the current agent session status\n- `/model` (`/m`) - List available models, or switch with `/model provider/modelId`\n- `/help` (`/h`) - Show this help message",
|
|
129
|
+
messageDeliveryFailedTitle: "[agent-bridge error] Message delivery failed",
|
|
130
|
+
weixinCooldown: "Weixin send is cooling down after rate limiting. Please try again shortly.",
|
|
131
|
+
statusTitle: "Current session status",
|
|
132
|
+
statusSessionId: "Session ID",
|
|
133
|
+
statusModel: "Model",
|
|
134
|
+
statusThinkingLevel: "Thinking level",
|
|
135
|
+
statusContext: "Context",
|
|
136
|
+
statusUnavailable: "Current session status is unavailable.",
|
|
137
|
+
statusUnavailableValue: "Unavailable",
|
|
138
|
+
modelListTitle: "Available models",
|
|
139
|
+
modelListCurrent: "current",
|
|
140
|
+
modelListUsage: "Use `/model provider/modelId` to switch.",
|
|
141
|
+
modelListUnavailable: "Available models are unavailable for the current session.",
|
|
142
|
+
modelSetUnavailable: "Current session model switching is unavailable.",
|
|
143
|
+
modelInvalid: "The requested model is invalid or unavailable.",
|
|
144
|
+
modelBusy: "Current session is busy, so the model cannot be switched. Please use `/stop` first.",
|
|
145
|
+
modelUpdated: "Switched current model to `{{model}}`."
|
|
146
|
+
},
|
|
147
|
+
gateway: {
|
|
148
|
+
noActiveSessionToCompact: "No active agent session to compact.",
|
|
149
|
+
noActiveSessionToStop: "No active agent session to stop.",
|
|
150
|
+
sessionCannotBeStopped: "This agent session cannot be stopped right now.",
|
|
151
|
+
noActiveRunToStop: "No active agent run to stop.",
|
|
152
|
+
startedNewSession: "Started a new session."
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"zh-CN": {
|
|
157
|
+
translation: {
|
|
158
|
+
progress: {
|
|
159
|
+
noProgress: "\u6682\u65E0\u8FDB\u5EA6\u3002",
|
|
160
|
+
collapsed: "\u5DF2\u6298\u53E0 {{count}} \u6761\u8F83\u65E9\u66F4\u65B0\u3002",
|
|
161
|
+
running: "\u23F3 {{subject}}",
|
|
162
|
+
finished: "\u2705 {{subject}}",
|
|
163
|
+
failed: "\u274C {{subject}}",
|
|
164
|
+
failedWithDetail: "\u274C {{subject}}: {{detail}}",
|
|
165
|
+
compacting: "\u6B63\u5728\u538B\u7F29\u4F1A\u8BDD",
|
|
166
|
+
compactingWithDetail: "\u6B63\u5728\u538B\u7F29\u4F1A\u8BDD: {{detail}}"
|
|
167
|
+
},
|
|
168
|
+
client: {
|
|
169
|
+
processing: "\u6B63\u5728\u5904\u7406\u4E2D...",
|
|
170
|
+
helpMessage: "\u53EF\u7528\u547D\u4EE4\uFF1A\n\n- `/new` (`/n`) - \u5F00\u59CB\u4E00\u4E2A\u65B0\u4F1A\u8BDD\n- `/compact` (`/c`) - \u538B\u7F29\u5F53\u524D\u4F1A\u8BDD\u4E0A\u4E0B\u6587\n- `/stop` (`/s`) - \u505C\u6B62\u5F53\u524D\u6B63\u5728\u8FD0\u884C\u7684\u4EFB\u52A1\n- `/status` (`/st`) - \u67E5\u770B\u5F53\u524D\u667A\u80FD\u4F53\u4F1A\u8BDD\u72B6\u6001\n- `/model` (`/m`) - \u67E5\u770B\u53EF\u7528\u6A21\u578B\uFF0C\u6216\u4F7F\u7528 `/model provider/modelId` \u5207\u6362\u6A21\u578B\n- `/help` (`/h`) - \u67E5\u770B\u8FD9\u6761\u5E2E\u52A9\u4FE1\u606F",
|
|
171
|
+
messageDeliveryFailedTitle: "[agent-bridge \u9519\u8BEF] \u6D88\u606F\u53D1\u9001\u5931\u8D25",
|
|
172
|
+
weixinCooldown: "\u5FAE\u4FE1\u53D1\u9001\u56E0\u9650\u6D41\u5DF2\u8FDB\u5165\u51B7\u5374\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",
|
|
173
|
+
statusTitle: "\u5F53\u524D\u4F1A\u8BDD\u72B6\u6001",
|
|
174
|
+
statusSessionId: "Session ID",
|
|
175
|
+
statusModel: "\u6A21\u578B",
|
|
176
|
+
statusThinkingLevel: "\u601D\u8003\u7B49\u7EA7",
|
|
177
|
+
statusContext: "\u4E0A\u4E0B\u6587",
|
|
178
|
+
statusUnavailable: "\u5F53\u524D\u65E0\u6CD5\u83B7\u53D6\u4F1A\u8BDD\u72B6\u6001\u3002",
|
|
179
|
+
statusUnavailableValue: "\u6682\u4E0D\u53EF\u7528",
|
|
180
|
+
modelListTitle: "\u53EF\u7528\u6A21\u578B",
|
|
181
|
+
modelListCurrent: "\u5F53\u524D",
|
|
182
|
+
modelListUsage: "\u4F7F\u7528 `/model provider/modelId` \u5207\u6362\u6A21\u578B\u3002",
|
|
183
|
+
modelListUnavailable: "\u5F53\u524D\u4F1A\u8BDD\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\u53EF\u7528\u6A21\u578B\u5217\u8868\u3002",
|
|
184
|
+
modelSetUnavailable: "\u5F53\u524D\u4F1A\u8BDD\u6682\u65F6\u65E0\u6CD5\u5207\u6362\u6A21\u578B\u3002",
|
|
185
|
+
modelInvalid: "\u8BF7\u6C42\u7684\u6A21\u578B\u65E0\u6548\u6216\u5F53\u524D\u4E0D\u53EF\u7528\u3002",
|
|
186
|
+
modelBusy: "\u5F53\u524D\u6B63\u5728\u8FD0\u884C\uFF0C\u65E0\u6CD5\u5207\u6362\u6A21\u578B\u3002\u8BF7\u5148\u4F7F\u7528 `/stop`\u3002",
|
|
187
|
+
modelUpdated: "\u5F53\u524D\u6A21\u578B\u5DF2\u5207\u6362\u81F3 `{{model}}`\u3002"
|
|
188
|
+
},
|
|
189
|
+
gateway: {
|
|
190
|
+
noActiveSessionToCompact: "\u5F53\u524D\u6CA1\u6709\u53EF\u538B\u7F29\u7684\u667A\u80FD\u4F53\u4F1A\u8BDD\u3002",
|
|
191
|
+
noActiveSessionToStop: "\u5F53\u524D\u6CA1\u6709\u53EF\u505C\u6B62\u7684\u667A\u80FD\u4F53\u4F1A\u8BDD\u3002",
|
|
192
|
+
sessionCannotBeStopped: "\u5F53\u524D\u65E0\u6CD5\u505C\u6B62\u8FD9\u4E2A\u667A\u80FD\u4F53\u4F1A\u8BDD\u3002",
|
|
193
|
+
noActiveRunToStop: "\u5F53\u524D\u6CA1\u6709\u6B63\u5728\u8FD0\u884C\u7684\u667A\u80FD\u4F53\u4EFB\u52A1\u53EF\u505C\u6B62\u3002",
|
|
194
|
+
startedNewSession: "\u5DF2\u5F00\u59CB\u65B0\u4F1A\u8BDD\u3002"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
var instance = i18next.createInstance();
|
|
200
|
+
void instance.init({
|
|
201
|
+
lng: DEFAULT_LOCALE,
|
|
202
|
+
fallbackLng: DEFAULT_LOCALE,
|
|
203
|
+
resources,
|
|
204
|
+
interpolation: { escapeValue: false },
|
|
205
|
+
initImmediate: false
|
|
206
|
+
});
|
|
207
|
+
function getTranslator(locale) {
|
|
208
|
+
return instance.getFixedT(locale);
|
|
209
|
+
}
|
|
210
|
+
function getTranslatorForCommon(common) {
|
|
211
|
+
return getTranslator(common?.language ?? DEFAULT_LOCALE);
|
|
212
|
+
}
|
|
213
|
+
function formatSendFailureNotice(t, detail) {
|
|
214
|
+
return `${t("client.messageDeliveryFailedTitle")}
|
|
215
|
+
|
|
216
|
+
${detail}`;
|
|
217
|
+
}
|
|
218
|
+
|
|
109
219
|
// src/config/defaults.ts
|
|
110
220
|
var DEFAULTS = {
|
|
111
221
|
agentIdleTimeoutMs: 10 * 60 * 1e3
|
|
@@ -114,11 +224,20 @@ var DEFAULTS = {
|
|
|
114
224
|
// src/config/store.ts
|
|
115
225
|
var CONFIG_DIR = path2.join(os2.homedir(), ".config", "agent-bridge");
|
|
116
226
|
var CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
|
|
227
|
+
function normalizeLanguage(language) {
|
|
228
|
+
return language === "en-US" || language === "zh-CN" ? language : DEFAULT_LOCALE;
|
|
229
|
+
}
|
|
117
230
|
function normalizeChannelConfig(channel) {
|
|
118
231
|
if (!channel.client || !channel.agent) {
|
|
119
232
|
throw new Error("Invalid channel config shape");
|
|
120
233
|
}
|
|
121
|
-
return
|
|
234
|
+
return {
|
|
235
|
+
common: {
|
|
236
|
+
language: normalizeLanguage(channel.common?.language)
|
|
237
|
+
},
|
|
238
|
+
client: channel.client,
|
|
239
|
+
agent: channel.agent
|
|
240
|
+
};
|
|
122
241
|
}
|
|
123
242
|
function mergeDefaults(config = {}) {
|
|
124
243
|
const channels = Object.fromEntries(
|
|
@@ -193,16 +312,20 @@ var GatewayCore = class {
|
|
|
193
312
|
#agentConfig;
|
|
194
313
|
#agentIdleTimeoutMs;
|
|
195
314
|
#bindingStore;
|
|
315
|
+
#common;
|
|
316
|
+
#t;
|
|
196
317
|
#logger = createLogger("core");
|
|
197
318
|
#clientToAgentSession = /* @__PURE__ */ new Map();
|
|
198
319
|
#agentRuntimes = /* @__PURE__ */ new Map();
|
|
199
320
|
#started = false;
|
|
200
|
-
constructor({ imAdapter, agentModule, agentConfig, agentIdleTimeoutMs, bindingStore }) {
|
|
321
|
+
constructor({ imAdapter, agentModule, agentConfig, agentIdleTimeoutMs, bindingStore, common }) {
|
|
201
322
|
this.#imAdapter = imAdapter;
|
|
202
323
|
this.#agentModule = agentModule;
|
|
203
324
|
this.#agentConfig = agentConfig;
|
|
204
325
|
this.#agentIdleTimeoutMs = agentIdleTimeoutMs;
|
|
205
326
|
this.#bindingStore = bindingStore;
|
|
327
|
+
this.#common = common;
|
|
328
|
+
this.#t = getTranslatorForCommon(common);
|
|
206
329
|
}
|
|
207
330
|
async start() {
|
|
208
331
|
if (this.#started) return;
|
|
@@ -242,6 +365,18 @@ var GatewayCore = class {
|
|
|
242
365
|
await this.#handleSessionStop(event.clientSessionId);
|
|
243
366
|
return;
|
|
244
367
|
}
|
|
368
|
+
if (event.type === "command.session.status") {
|
|
369
|
+
await this.#handleSessionStatus(event.clientSessionId);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (event.type === "command.session.model.list") {
|
|
373
|
+
await this.#handleSessionModelList(event.clientSessionId);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (event.type === "command.session.model.set") {
|
|
377
|
+
await this.#handleSessionModelSet(event.clientSessionId, event.target);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
245
380
|
await this.#handleUserMessage(event.clientSessionId, event.text);
|
|
246
381
|
}
|
|
247
382
|
async #handleUserMessage(clientSessionId, text2) {
|
|
@@ -258,7 +393,7 @@ var GatewayCore = class {
|
|
|
258
393
|
await this.#deliverClientInput({
|
|
259
394
|
type: "assistant.message",
|
|
260
395
|
clientSessionId,
|
|
261
|
-
text: "
|
|
396
|
+
text: this.#t("gateway.noActiveSessionToCompact")
|
|
262
397
|
});
|
|
263
398
|
return;
|
|
264
399
|
}
|
|
@@ -273,7 +408,7 @@ var GatewayCore = class {
|
|
|
273
408
|
await this.#deliverClientInput({
|
|
274
409
|
type: "assistant.message",
|
|
275
410
|
clientSessionId,
|
|
276
|
-
text: "
|
|
411
|
+
text: this.#t("gateway.noActiveSessionToStop")
|
|
277
412
|
});
|
|
278
413
|
return;
|
|
279
414
|
}
|
|
@@ -282,7 +417,7 @@ var GatewayCore = class {
|
|
|
282
417
|
await this.#deliverClientInput({
|
|
283
418
|
type: "assistant.message",
|
|
284
419
|
clientSessionId,
|
|
285
|
-
text: "
|
|
420
|
+
text: this.#t("gateway.sessionCannotBeStopped")
|
|
286
421
|
});
|
|
287
422
|
return;
|
|
288
423
|
}
|
|
@@ -290,12 +425,129 @@ var GatewayCore = class {
|
|
|
290
425
|
await this.#deliverClientInput({
|
|
291
426
|
type: "assistant.message",
|
|
292
427
|
clientSessionId,
|
|
293
|
-
text: "
|
|
428
|
+
text: this.#t("gateway.noActiveRunToStop")
|
|
294
429
|
});
|
|
295
430
|
return;
|
|
296
431
|
}
|
|
297
432
|
await runtime.agentAdapter.abort();
|
|
298
433
|
}
|
|
434
|
+
async #handleSessionStatus(clientSessionId) {
|
|
435
|
+
const runtime = await this.#getActiveRuntime(clientSessionId);
|
|
436
|
+
if (!runtime) {
|
|
437
|
+
await this.#deliverClientInput({
|
|
438
|
+
type: "error",
|
|
439
|
+
clientSessionId,
|
|
440
|
+
kind: "agent.status.unavailable"
|
|
441
|
+
});
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
this.#touchRuntime(runtime);
|
|
445
|
+
if (!runtime.agentAdapter.getStatus) {
|
|
446
|
+
await this.#deliverClientInput({
|
|
447
|
+
type: "error",
|
|
448
|
+
clientSessionId,
|
|
449
|
+
kind: "agent.status.unavailable"
|
|
450
|
+
});
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
const status = await runtime.agentAdapter.getStatus();
|
|
455
|
+
await this.#deliverClientInput({
|
|
456
|
+
type: "agent.status.info",
|
|
457
|
+
clientSessionId,
|
|
458
|
+
status
|
|
459
|
+
});
|
|
460
|
+
} catch (error) {
|
|
461
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
462
|
+
await this.#deliverClientInput({
|
|
463
|
+
type: "error",
|
|
464
|
+
clientSessionId,
|
|
465
|
+
kind: "agent.status.unavailable",
|
|
466
|
+
detail
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async #handleSessionModelList(clientSessionId) {
|
|
471
|
+
const runtime = await this.#getActiveRuntime(clientSessionId);
|
|
472
|
+
if (!runtime) {
|
|
473
|
+
await this.#deliverClientInput({
|
|
474
|
+
type: "error",
|
|
475
|
+
clientSessionId,
|
|
476
|
+
kind: "agent.model.list.unavailable"
|
|
477
|
+
});
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
this.#touchRuntime(runtime);
|
|
481
|
+
if (!runtime.agentAdapter.getAvailableModels) {
|
|
482
|
+
await this.#deliverClientInput({
|
|
483
|
+
type: "error",
|
|
484
|
+
clientSessionId,
|
|
485
|
+
kind: "agent.model.list.unavailable"
|
|
486
|
+
});
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
try {
|
|
490
|
+
const models = await runtime.agentAdapter.getAvailableModels();
|
|
491
|
+
await this.#deliverClientInput({
|
|
492
|
+
type: "agent.model.list",
|
|
493
|
+
clientSessionId,
|
|
494
|
+
models
|
|
495
|
+
});
|
|
496
|
+
} catch (error) {
|
|
497
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
498
|
+
await this.#deliverClientInput({
|
|
499
|
+
type: "error",
|
|
500
|
+
clientSessionId,
|
|
501
|
+
kind: "agent.model.list.unavailable",
|
|
502
|
+
detail
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async #handleSessionModelSet(clientSessionId, target) {
|
|
507
|
+
const runtime = await this.#getActiveRuntime(clientSessionId);
|
|
508
|
+
if (!runtime) {
|
|
509
|
+
await this.#deliverClientInput({
|
|
510
|
+
type: "error",
|
|
511
|
+
clientSessionId,
|
|
512
|
+
kind: "agent.model.set.unavailable"
|
|
513
|
+
});
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
this.#touchRuntime(runtime);
|
|
517
|
+
if (!runtime.agentAdapter.setModel) {
|
|
518
|
+
await this.#deliverClientInput({
|
|
519
|
+
type: "error",
|
|
520
|
+
clientSessionId,
|
|
521
|
+
kind: "agent.model.set.unavailable"
|
|
522
|
+
});
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (await runtime.agentAdapter.isBusy()) {
|
|
526
|
+
await this.#deliverClientInput({
|
|
527
|
+
type: "error",
|
|
528
|
+
clientSessionId,
|
|
529
|
+
kind: "agent.model.busy"
|
|
530
|
+
});
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
const result = await runtime.agentAdapter.setModel(target);
|
|
535
|
+
await this.#deliverClientInput({
|
|
536
|
+
type: "agent.model.updated",
|
|
537
|
+
clientSessionId,
|
|
538
|
+
provider: result.provider,
|
|
539
|
+
modelId: result.modelId
|
|
540
|
+
});
|
|
541
|
+
} catch (error) {
|
|
542
|
+
const { kind, detail } = this.#resolveModelCommandError(error);
|
|
543
|
+
await this.#deliverClientInput({
|
|
544
|
+
type: "error",
|
|
545
|
+
clientSessionId,
|
|
546
|
+
kind,
|
|
547
|
+
...detail ? { detail } : {}
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
299
551
|
async #handleSessionNew(clientSessionId) {
|
|
300
552
|
const previousAgentSessionId = this.#clientToAgentSession.get(clientSessionId);
|
|
301
553
|
if (previousAgentSessionId) {
|
|
@@ -309,7 +561,7 @@ var GatewayCore = class {
|
|
|
309
561
|
await this.#deliverClientInput({
|
|
310
562
|
type: "assistant.message",
|
|
311
563
|
clientSessionId,
|
|
312
|
-
text: "
|
|
564
|
+
text: this.#t("gateway.startedNewSession")
|
|
313
565
|
});
|
|
314
566
|
}
|
|
315
567
|
async #deliverClientInput(event) {
|
|
@@ -344,6 +596,7 @@ var GatewayCore = class {
|
|
|
344
596
|
if (this.#agentModule.resumeAgentSession) {
|
|
345
597
|
const agentAdapter = await this.#agentModule.resumeAgentSession({
|
|
346
598
|
config: this.#agentConfig,
|
|
599
|
+
common: this.#common ?? { channelName: "", language: "en-US" },
|
|
347
600
|
agentSessionId
|
|
348
601
|
});
|
|
349
602
|
return this.#startRuntime(clientSessionId, agentSessionId, agentAdapter);
|
|
@@ -354,7 +607,8 @@ var GatewayCore = class {
|
|
|
354
607
|
}
|
|
355
608
|
async #createRuntimeForClient(clientSessionId) {
|
|
356
609
|
const { agentSessionId, agentAdapter } = await this.#agentModule.createAgentSession({
|
|
357
|
-
config: this.#agentConfig
|
|
610
|
+
config: this.#agentConfig,
|
|
611
|
+
common: this.#common ?? { channelName: "", language: "en-US" }
|
|
358
612
|
});
|
|
359
613
|
return this.#startRuntime(clientSessionId, agentSessionId, agentAdapter);
|
|
360
614
|
}
|
|
@@ -387,6 +641,18 @@ var GatewayCore = class {
|
|
|
387
641
|
this.#logger.error("failed to persist session bindings:", error);
|
|
388
642
|
}
|
|
389
643
|
}
|
|
644
|
+
#resolveModelCommandError(error) {
|
|
645
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
646
|
+
if (typeof error === "object" && error && "kind" in error && typeof error.kind === "string") {
|
|
647
|
+
switch (error.kind) {
|
|
648
|
+
case "agent.model.invalid":
|
|
649
|
+
case "agent.model.busy":
|
|
650
|
+
case "agent.model.set.unavailable":
|
|
651
|
+
return { kind: error.kind, ...detail ? { detail } : {} };
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return { kind: "agent.model.set.unavailable", ...detail ? { detail } : {} };
|
|
655
|
+
}
|
|
390
656
|
async #handleAgentOutput(event) {
|
|
391
657
|
const agentSessionId = event.agentSessionId;
|
|
392
658
|
const runtime = this.#agentRuntimes.get(agentSessionId);
|
|
@@ -789,6 +1055,19 @@ var PiRpcClient = class {
|
|
|
789
1055
|
const response = await this.#send({ type: "get_state" });
|
|
790
1056
|
return response.data ?? {};
|
|
791
1057
|
}
|
|
1058
|
+
async getSessionStats() {
|
|
1059
|
+
const response = await this.#send({ type: "get_session_stats" });
|
|
1060
|
+
return response.data ?? {};
|
|
1061
|
+
}
|
|
1062
|
+
async getAvailableModels() {
|
|
1063
|
+
const response = await this.#send({ type: "get_available_models" });
|
|
1064
|
+
const data = response.data;
|
|
1065
|
+
return data?.models ?? [];
|
|
1066
|
+
}
|
|
1067
|
+
async setModel(provider, modelId) {
|
|
1068
|
+
const response = await this.#send({ type: "set_model", provider, modelId });
|
|
1069
|
+
return response.data ?? {};
|
|
1070
|
+
}
|
|
792
1071
|
async setSessionName(name) {
|
|
793
1072
|
await this.#send({ type: "set_session_name", name });
|
|
794
1073
|
}
|
|
@@ -895,6 +1174,13 @@ function toPiSessionId(agentSessionId) {
|
|
|
895
1174
|
}
|
|
896
1175
|
|
|
897
1176
|
// src/modules/agent/pi-coding-agent/adapter/pi-coding-agent-adapter.ts
|
|
1177
|
+
var PiModelCommandError = class extends Error {
|
|
1178
|
+
kind;
|
|
1179
|
+
constructor(kind, message) {
|
|
1180
|
+
super(message);
|
|
1181
|
+
this.kind = kind;
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
898
1184
|
var PiCodingAgentAdapter = class {
|
|
899
1185
|
#agentSessionId;
|
|
900
1186
|
#piSessionId;
|
|
@@ -977,6 +1263,71 @@ var PiCodingAgentAdapter = class {
|
|
|
977
1263
|
async isBusy() {
|
|
978
1264
|
return this.#processing || this.#inputQueue.length > 0;
|
|
979
1265
|
}
|
|
1266
|
+
async getStatus() {
|
|
1267
|
+
if (!this.#client) {
|
|
1268
|
+
throw new Error("PiCodingAgentAdapter is not started");
|
|
1269
|
+
}
|
|
1270
|
+
const [state, stats] = await Promise.all([this.#client.getState(), this.#client.getSessionStats()]);
|
|
1271
|
+
const contextUsage = stats.contextUsage;
|
|
1272
|
+
return {
|
|
1273
|
+
sessionId: state.sessionId ?? this.#agentSessionId,
|
|
1274
|
+
provider: state.model?.provider,
|
|
1275
|
+
modelId: state.model?.id,
|
|
1276
|
+
thinkingLevel: state.thinkingLevel,
|
|
1277
|
+
context: contextUsage ? {
|
|
1278
|
+
tokens: contextUsage.tokens ?? null,
|
|
1279
|
+
contextWindow: contextUsage.contextWindow ?? null,
|
|
1280
|
+
percent: contextUsage.percent ?? null
|
|
1281
|
+
} : void 0
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
async getAvailableModels() {
|
|
1285
|
+
if (!this.#client) {
|
|
1286
|
+
throw new Error("PiCodingAgentAdapter is not started");
|
|
1287
|
+
}
|
|
1288
|
+
const [state, models] = await Promise.all([this.#client.getState(), this.#client.getAvailableModels()]);
|
|
1289
|
+
return models.filter((model) => typeof model.provider === "string" && typeof model.id === "string").map((model) => ({
|
|
1290
|
+
provider: model.provider,
|
|
1291
|
+
modelId: model.id,
|
|
1292
|
+
isCurrent: state.model?.provider === model.provider && state.model?.id === model.id
|
|
1293
|
+
}));
|
|
1294
|
+
}
|
|
1295
|
+
async setModel(target) {
|
|
1296
|
+
if (!this.#client) {
|
|
1297
|
+
throw new PiModelCommandError("agent.model.set.unavailable", "PiCodingAgentAdapter is not started");
|
|
1298
|
+
}
|
|
1299
|
+
const parsed = this.#parseModelTarget(target);
|
|
1300
|
+
const state = await this.#client.getState();
|
|
1301
|
+
if (state.isStreaming || state.isCompacting) {
|
|
1302
|
+
throw new PiModelCommandError("agent.model.busy", "Current session is busy, so the model cannot be switched. Please use /stop first.");
|
|
1303
|
+
}
|
|
1304
|
+
try {
|
|
1305
|
+
const result = await this.#client.setModel(parsed.provider, parsed.modelId);
|
|
1306
|
+
return {
|
|
1307
|
+
provider: result.provider ?? parsed.provider,
|
|
1308
|
+
modelId: result.id ?? parsed.modelId
|
|
1309
|
+
};
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1312
|
+
if (/Model not found:/i.test(message)) {
|
|
1313
|
+
throw new PiModelCommandError("agent.model.invalid", message);
|
|
1314
|
+
}
|
|
1315
|
+
throw new PiModelCommandError("agent.model.set.unavailable", message);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
#parseModelTarget(target) {
|
|
1319
|
+
const trimmed = target.trim();
|
|
1320
|
+
const slashIndex = trimmed.indexOf("/");
|
|
1321
|
+
if (slashIndex <= 0 || slashIndex === trimmed.length - 1) {
|
|
1322
|
+
throw new PiModelCommandError("agent.model.invalid", `Invalid model target: ${target}`);
|
|
1323
|
+
}
|
|
1324
|
+
const provider = trimmed.slice(0, slashIndex).trim();
|
|
1325
|
+
const modelId = trimmed.slice(slashIndex + 1).trim();
|
|
1326
|
+
if (!provider || !modelId) {
|
|
1327
|
+
throw new PiModelCommandError("agent.model.invalid", `Invalid model target: ${target}`);
|
|
1328
|
+
}
|
|
1329
|
+
return { provider, modelId };
|
|
1330
|
+
}
|
|
980
1331
|
async #drainInputQueue() {
|
|
981
1332
|
if (this.#processing) {
|
|
982
1333
|
return;
|
|
@@ -1088,7 +1439,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1088
1439
|
toolCallId,
|
|
1089
1440
|
toolInput,
|
|
1090
1441
|
toolLabel,
|
|
1091
|
-
text:
|
|
1442
|
+
text: void 0
|
|
1092
1443
|
});
|
|
1093
1444
|
return;
|
|
1094
1445
|
}
|
|
@@ -1109,7 +1460,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1109
1460
|
toolInput,
|
|
1110
1461
|
toolLabel,
|
|
1111
1462
|
partialResult: "partialResult" in rpcEvent ? rpcEvent.partialResult : void 0,
|
|
1112
|
-
text:
|
|
1463
|
+
text: void 0
|
|
1113
1464
|
});
|
|
1114
1465
|
return;
|
|
1115
1466
|
}
|
|
@@ -1127,7 +1478,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1127
1478
|
toolInput,
|
|
1128
1479
|
toolLabel,
|
|
1129
1480
|
result: "result" in rpcEvent ? rpcEvent.result : void 0,
|
|
1130
|
-
text:
|
|
1481
|
+
text: void 0
|
|
1131
1482
|
});
|
|
1132
1483
|
if (toolCallId) {
|
|
1133
1484
|
this.#toolLabelByCallId.delete(toolCallId);
|
|
@@ -1292,16 +1643,16 @@ function createPiCodingAgentConfigCollector() {
|
|
|
1292
1643
|
var piCodingAgentModule = {
|
|
1293
1644
|
type: "pi-coding-agent",
|
|
1294
1645
|
createConfigCollector: createPiCodingAgentConfigCollector,
|
|
1295
|
-
async createAgentSession({ config }) {
|
|
1646
|
+
async createAgentSession({ config, common }) {
|
|
1296
1647
|
const agentSessionId = `pi-coding-agent:${randomUUID()}`;
|
|
1297
|
-
logger.info(`creating agent session ${agentSessionId}`);
|
|
1648
|
+
logger.info(`creating agent session ${agentSessionId} for channel ${common.channelName}`);
|
|
1298
1649
|
return {
|
|
1299
1650
|
agentSessionId,
|
|
1300
1651
|
agentAdapter: buildAdapter(config, agentSessionId)
|
|
1301
1652
|
};
|
|
1302
1653
|
},
|
|
1303
|
-
async resumeAgentSession({ config, agentSessionId }) {
|
|
1304
|
-
logger.info(`resuming agent session ${agentSessionId}`);
|
|
1654
|
+
async resumeAgentSession({ config, common, agentSessionId }) {
|
|
1655
|
+
logger.info(`resuming agent session ${agentSessionId} for channel ${common.channelName}`);
|
|
1305
1656
|
return buildAdapter(config, agentSessionId);
|
|
1306
1657
|
}
|
|
1307
1658
|
};
|
|
@@ -1324,15 +1675,16 @@ function getTypedAgentModule(config) {
|
|
|
1324
1675
|
|
|
1325
1676
|
// src/modules/client/utils/progress-renderer.ts
|
|
1326
1677
|
var DEFAULT_COLLAPSE_THRESHOLD = 10;
|
|
1327
|
-
var MAX_TOOL_LABEL_DISPLAY_LENGTH =
|
|
1328
|
-
var NO_PROGRESS_MARKDOWN = "No progress yet.";
|
|
1678
|
+
var MAX_TOOL_LABEL_DISPLAY_LENGTH = 24;
|
|
1329
1679
|
var ProgressRenderer = class {
|
|
1330
1680
|
#collapseThreshold;
|
|
1681
|
+
#t;
|
|
1331
1682
|
#entries = /* @__PURE__ */ new Map();
|
|
1332
1683
|
#order = [];
|
|
1333
1684
|
#status = "running";
|
|
1334
1685
|
constructor(options = {}) {
|
|
1335
1686
|
this.#collapseThreshold = options.collapseThreshold ?? DEFAULT_COLLAPSE_THRESHOLD;
|
|
1687
|
+
this.#t = options.t ?? getTranslator("en-US");
|
|
1336
1688
|
}
|
|
1337
1689
|
/** Whether `event` should be recorded as progress (excludes assistant messages and thinking). */
|
|
1338
1690
|
isProgressEvent(event) {
|
|
@@ -1354,20 +1706,21 @@ var ProgressRenderer = class {
|
|
|
1354
1706
|
return {
|
|
1355
1707
|
markdown: this.#renderMarkdown(collapsedCount),
|
|
1356
1708
|
status: this.#status,
|
|
1357
|
-
collapsedCount
|
|
1709
|
+
collapsedCount,
|
|
1710
|
+
isEmpty: this.#order.length === 0
|
|
1358
1711
|
};
|
|
1359
1712
|
}
|
|
1360
1713
|
#renderMarkdown(collapsedCount) {
|
|
1361
1714
|
const contentLines = [];
|
|
1362
1715
|
if (collapsedCount > 0) {
|
|
1363
|
-
contentLines.push(`-
|
|
1716
|
+
contentLines.push(`- ${this.#t("progress.collapsed", { count: collapsedCount })}`);
|
|
1364
1717
|
}
|
|
1365
1718
|
for (const id of this.#visibleOrder()) {
|
|
1366
1719
|
const entry = this.#entries.get(id);
|
|
1367
1720
|
if (!entry) continue;
|
|
1368
1721
|
contentLines.push(entry.kind === "line" ? entry.line : this.#formatToolEntry(entry));
|
|
1369
1722
|
}
|
|
1370
|
-
return contentLines.length > 0 ? contentLines.join("\n") :
|
|
1723
|
+
return contentLines.length > 0 ? contentLines.join("\n") : this.#t("progress.noProgress");
|
|
1371
1724
|
}
|
|
1372
1725
|
#visibleOrder() {
|
|
1373
1726
|
return this.#order.slice(-this.#collapseThreshold);
|
|
@@ -1399,9 +1752,9 @@ var ProgressRenderer = class {
|
|
|
1399
1752
|
const subject = this.#formatToolSubject(entry.toolName, entry.toolLabel);
|
|
1400
1753
|
switch (entry.status) {
|
|
1401
1754
|
case "running":
|
|
1402
|
-
return `-
|
|
1755
|
+
return `- ${this.#t("progress.running", { subject })}`;
|
|
1403
1756
|
case "done":
|
|
1404
|
-
return `-
|
|
1757
|
+
return `- ${this.#t("progress.finished", { subject })}`;
|
|
1405
1758
|
case "error":
|
|
1406
1759
|
return this.#formatToolErrorLine(subject, entry.text);
|
|
1407
1760
|
}
|
|
@@ -1409,12 +1762,12 @@ var ProgressRenderer = class {
|
|
|
1409
1762
|
#formatProgressLine(event) {
|
|
1410
1763
|
switch (event.type) {
|
|
1411
1764
|
case "session.compacting":
|
|
1412
|
-
return `-
|
|
1765
|
+
return `- ${event.text ? this.#t("progress.compactingWithDetail", { detail: event.text }) : this.#t("progress.compacting")}`;
|
|
1413
1766
|
case "assistant.tool.running":
|
|
1414
1767
|
case "assistant.tool.update":
|
|
1415
|
-
return `-
|
|
1768
|
+
return `- ${this.#t("progress.running", { subject: this.#formatToolSubject(event.toolName, event.toolLabel) })}`;
|
|
1416
1769
|
case "assistant.tool.done":
|
|
1417
|
-
return `-
|
|
1770
|
+
return `- ${this.#t("progress.finished", { subject: this.#formatToolSubject(event.toolName, event.toolLabel) })}`;
|
|
1418
1771
|
case "assistant.tool.error":
|
|
1419
1772
|
return this.#formatToolErrorLine(this.#formatToolSubject(event.toolName, event.toolLabel), event.text);
|
|
1420
1773
|
}
|
|
@@ -1434,15 +1787,10 @@ var ProgressRenderer = class {
|
|
|
1434
1787
|
if (!normalizedText) {
|
|
1435
1788
|
return `- ${this.#humanizeToolError(toolName)}`;
|
|
1436
1789
|
}
|
|
1437
|
-
|
|
1438
|
-
const lowerToolName = toolName.toLowerCase();
|
|
1439
|
-
if (lowerText === lowerToolName || lowerText === `failed ${lowerToolName}` || lowerText === `running ${lowerToolName}` || lowerText === `finished ${lowerToolName}`) {
|
|
1440
|
-
return `- ${this.#humanizeToolError(toolName)}`;
|
|
1441
|
-
}
|
|
1442
|
-
return `- ${this.#humanizeToolError(toolName)}: ${normalizedText}`;
|
|
1790
|
+
return `- ${this.#t("progress.failedWithDetail", { subject: toolName, detail: normalizedText })}`;
|
|
1443
1791
|
}
|
|
1444
1792
|
#humanizeToolError(toolName) {
|
|
1445
|
-
return
|
|
1793
|
+
return this.#t("progress.failed", { subject: toolName });
|
|
1446
1794
|
}
|
|
1447
1795
|
#progressStatus(event) {
|
|
1448
1796
|
switch (event.type) {
|
|
@@ -1457,7 +1805,38 @@ var ProgressRenderer = class {
|
|
|
1457
1805
|
};
|
|
1458
1806
|
|
|
1459
1807
|
// src/modules/client/utils/slash-commands.ts
|
|
1808
|
+
function isHelpCommand(text2) {
|
|
1809
|
+
switch (text2.toLowerCase()) {
|
|
1810
|
+
case "/help":
|
|
1811
|
+
case "/h":
|
|
1812
|
+
return true;
|
|
1813
|
+
default:
|
|
1814
|
+
return false;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
function resolveHelpMarkdown(text2, t) {
|
|
1818
|
+
return isHelpCommand(text2) ? t("client.helpMessage") : null;
|
|
1819
|
+
}
|
|
1820
|
+
function parseModelCommand(text2, clientSessionId) {
|
|
1821
|
+
const match = text2.match(/^\/(model|m)(?:\s+(.*))?$/i);
|
|
1822
|
+
if (!match) {
|
|
1823
|
+
return null;
|
|
1824
|
+
}
|
|
1825
|
+
const target = match[2]?.trim();
|
|
1826
|
+
if (!target) {
|
|
1827
|
+
return { type: "command.session.model.list", clientSessionId };
|
|
1828
|
+
}
|
|
1829
|
+
return {
|
|
1830
|
+
type: "command.session.model.set",
|
|
1831
|
+
clientSessionId,
|
|
1832
|
+
target
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1460
1835
|
function parseSlashCommand(text2, clientSessionId) {
|
|
1836
|
+
const modelCommand = parseModelCommand(text2, clientSessionId);
|
|
1837
|
+
if (modelCommand) {
|
|
1838
|
+
return modelCommand;
|
|
1839
|
+
}
|
|
1461
1840
|
switch (text2.toLowerCase()) {
|
|
1462
1841
|
case "/new":
|
|
1463
1842
|
case "/n":
|
|
@@ -1466,12 +1845,89 @@ function parseSlashCommand(text2, clientSessionId) {
|
|
|
1466
1845
|
case "/c":
|
|
1467
1846
|
return { type: "command.session.compact", clientSessionId };
|
|
1468
1847
|
case "/stop":
|
|
1848
|
+
case "/s":
|
|
1469
1849
|
return { type: "command.session.stop", clientSessionId };
|
|
1850
|
+
case "/status":
|
|
1851
|
+
case "/st":
|
|
1852
|
+
return { type: "command.session.status", clientSessionId };
|
|
1470
1853
|
default:
|
|
1471
1854
|
return null;
|
|
1472
1855
|
}
|
|
1473
1856
|
}
|
|
1474
1857
|
|
|
1858
|
+
// src/modules/client/utils/status-markdown.ts
|
|
1859
|
+
function formatModel(status, t) {
|
|
1860
|
+
if (status.provider && status.modelId) {
|
|
1861
|
+
return `\`${status.provider}/${status.modelId}\``;
|
|
1862
|
+
}
|
|
1863
|
+
if (status.modelId) {
|
|
1864
|
+
return `\`${status.modelId}\``;
|
|
1865
|
+
}
|
|
1866
|
+
if (status.provider) {
|
|
1867
|
+
return `\`${status.provider}\``;
|
|
1868
|
+
}
|
|
1869
|
+
return t("client.statusUnavailableValue");
|
|
1870
|
+
}
|
|
1871
|
+
function formatContext(status, t) {
|
|
1872
|
+
const context = status.context;
|
|
1873
|
+
if (!context) {
|
|
1874
|
+
return t("client.statusUnavailableValue");
|
|
1875
|
+
}
|
|
1876
|
+
const { tokens, contextWindow, percent } = context;
|
|
1877
|
+
if (tokens == null || contextWindow == null || percent == null) {
|
|
1878
|
+
return t("client.statusUnavailableValue");
|
|
1879
|
+
}
|
|
1880
|
+
return `\`${tokens.toLocaleString()} / ${contextWindow.toLocaleString()} (${percent}%)\``;
|
|
1881
|
+
}
|
|
1882
|
+
function formatAvailableModel(model, t) {
|
|
1883
|
+
const label = `\`${model.provider}/${model.modelId}\``;
|
|
1884
|
+
return model.isCurrent ? `- ${label} \u2705 ${t("client.modelListCurrent")}` : `- ${label}`;
|
|
1885
|
+
}
|
|
1886
|
+
function formatErrorMarkdown(title, detail) {
|
|
1887
|
+
return [`**${title}**`, ...detail ? ["", detail] : []].join("\n");
|
|
1888
|
+
}
|
|
1889
|
+
function renderStatusMarkdown(event, t) {
|
|
1890
|
+
if (event.type === "agent.model.list") {
|
|
1891
|
+
return [
|
|
1892
|
+
`**${t("client.modelListTitle")}**`,
|
|
1893
|
+
"",
|
|
1894
|
+
...event.models.length > 0 ? event.models.map((model) => formatAvailableModel(model, t)) : [`- ${t("client.statusUnavailableValue")}`],
|
|
1895
|
+
"",
|
|
1896
|
+
t("client.modelListUsage")
|
|
1897
|
+
].join("\n");
|
|
1898
|
+
}
|
|
1899
|
+
if (event.type === "agent.model.updated") {
|
|
1900
|
+
return t("client.modelUpdated", { model: `${event.provider}/${event.modelId}` });
|
|
1901
|
+
}
|
|
1902
|
+
if (event.type === "agent.status.info") {
|
|
1903
|
+
return [
|
|
1904
|
+
`**${t("client.statusTitle")}**`,
|
|
1905
|
+
"",
|
|
1906
|
+
`- ${t("client.statusSessionId")}: \`${event.status.sessionId}\``,
|
|
1907
|
+
`- ${t("client.statusModel")}: ${formatModel(event.status, t)}`,
|
|
1908
|
+
`- ${t("client.statusThinkingLevel")}: ${event.status.thinkingLevel ? `\`${event.status.thinkingLevel}\`` : t("client.statusUnavailableValue")}`,
|
|
1909
|
+
`- ${t("client.statusContext")}: ${formatContext(event.status, t)}`
|
|
1910
|
+
].join("\n");
|
|
1911
|
+
}
|
|
1912
|
+
if (event.type === "error") {
|
|
1913
|
+
switch (event.kind) {
|
|
1914
|
+
case "agent.status.unavailable":
|
|
1915
|
+
return formatErrorMarkdown(t("client.statusUnavailable"), event.detail);
|
|
1916
|
+
case "agent.model.list.unavailable":
|
|
1917
|
+
return formatErrorMarkdown(t("client.modelListUnavailable"), event.detail);
|
|
1918
|
+
case "agent.model.set.unavailable":
|
|
1919
|
+
return formatErrorMarkdown(t("client.modelSetUnavailable"), event.detail);
|
|
1920
|
+
case "agent.model.invalid":
|
|
1921
|
+
return formatErrorMarkdown(t("client.modelInvalid"), event.detail);
|
|
1922
|
+
case "agent.model.busy":
|
|
1923
|
+
return event.detail ?? t("client.modelBusy");
|
|
1924
|
+
default:
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
return null;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1475
1931
|
// src/modules/client/feishu/adapter/feishu-client.ts
|
|
1476
1932
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
1477
1933
|
import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
|
|
@@ -1818,6 +2274,7 @@ function chunkText(text2, maxLen) {
|
|
|
1818
2274
|
var FeishuIMAdapter = class _FeishuIMAdapter {
|
|
1819
2275
|
#config;
|
|
1820
2276
|
#logger;
|
|
2277
|
+
#t;
|
|
1821
2278
|
#onOutput = null;
|
|
1822
2279
|
#client = null;
|
|
1823
2280
|
#egressQueue = [];
|
|
@@ -1842,18 +2299,17 @@ var FeishuIMAdapter = class _FeishuIMAdapter {
|
|
|
1842
2299
|
return;
|
|
1843
2300
|
}
|
|
1844
2301
|
const message = error instanceof Error ? error.message : String(error);
|
|
1845
|
-
const text2 =
|
|
1846
|
-
|
|
1847
|
-
${message}`;
|
|
2302
|
+
const text2 = formatSendFailureNotice(this.#t, message);
|
|
1848
2303
|
try {
|
|
1849
2304
|
await this.#client.sendText(chatId, text2);
|
|
1850
2305
|
} catch (notifyError) {
|
|
1851
2306
|
this.#logger.error("failed to notify send failure:", notifyError);
|
|
1852
2307
|
}
|
|
1853
2308
|
}
|
|
1854
|
-
constructor(config, logger3 = createLogger("feishu")) {
|
|
2309
|
+
constructor(config, logger3 = createLogger("feishu"), common) {
|
|
1855
2310
|
this.#config = config;
|
|
1856
2311
|
this.#logger = logger3;
|
|
2312
|
+
this.#t = getTranslatorForCommon(common);
|
|
1857
2313
|
}
|
|
1858
2314
|
async start(onOutput) {
|
|
1859
2315
|
this.#onOutput = onOutput;
|
|
@@ -1870,10 +2326,17 @@ ${message}`;
|
|
|
1870
2326
|
);
|
|
1871
2327
|
return;
|
|
1872
2328
|
}
|
|
2329
|
+
const normalizedText = text2.trim();
|
|
1873
2330
|
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
1874
2331
|
this.#resetProgressState(clientSessionId);
|
|
1875
2332
|
await this.#client?.startTyping(chatId, messageId);
|
|
1876
|
-
const
|
|
2333
|
+
const helpMarkdown = resolveHelpMarkdown(normalizedText, this.#t);
|
|
2334
|
+
if (helpMarkdown) {
|
|
2335
|
+
this.#logger.info(`received local help command ${normalizedText} (session=${clientSessionId})`);
|
|
2336
|
+
await this.#client?.sendText(chatId, helpMarkdown, messageId);
|
|
2337
|
+
await this.#client?.stopTyping(chatId);
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
1877
2340
|
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
1878
2341
|
if (commandEvent) {
|
|
1879
2342
|
this.#logger.info(`received command ${normalizedText} (session=${clientSessionId})`);
|
|
@@ -1925,6 +2388,13 @@ ${message}`;
|
|
|
1925
2388
|
try {
|
|
1926
2389
|
const target = parseFeishuSessionId(event.clientSessionId);
|
|
1927
2390
|
if (event.type !== "assistant.message") {
|
|
2391
|
+
const statusMarkdown = renderStatusMarkdown(event, this.#t);
|
|
2392
|
+
if (statusMarkdown) {
|
|
2393
|
+
const replyToMessageId2 = this.#lastInboundMessageIdBySession.get(event.clientSessionId);
|
|
2394
|
+
await this.#client.sendText(target.chatId, statusMarkdown, replyToMessageId2);
|
|
2395
|
+
await this.#client.stopTyping(target.chatId);
|
|
2396
|
+
continue;
|
|
2397
|
+
}
|
|
1928
2398
|
await this.#handleProgressEvent(target.chatId, event);
|
|
1929
2399
|
continue;
|
|
1930
2400
|
}
|
|
@@ -1966,7 +2436,7 @@ ${message}`;
|
|
|
1966
2436
|
return;
|
|
1967
2437
|
}
|
|
1968
2438
|
const state = this.#progressStateBySession.get(event.clientSessionId) ?? {
|
|
1969
|
-
renderer: new ProgressRenderer(),
|
|
2439
|
+
renderer: new ProgressRenderer({ t: this.#t }),
|
|
1970
2440
|
messageId: null,
|
|
1971
2441
|
creating: false
|
|
1972
2442
|
};
|
|
@@ -1996,7 +2466,7 @@ ${message}`;
|
|
|
1996
2466
|
}
|
|
1997
2467
|
#resetProgressState(clientSessionId) {
|
|
1998
2468
|
this.#progressStateBySession.set(clientSessionId, {
|
|
1999
|
-
renderer: new ProgressRenderer(),
|
|
2469
|
+
renderer: new ProgressRenderer({ t: this.#t }),
|
|
2000
2470
|
messageId: null,
|
|
2001
2471
|
creating: false
|
|
2002
2472
|
});
|
|
@@ -2048,8 +2518,8 @@ function createFeishuConfigCollector() {
|
|
|
2048
2518
|
var feishuClientModule = {
|
|
2049
2519
|
type: "feishu",
|
|
2050
2520
|
createConfigCollector: createFeishuConfigCollector,
|
|
2051
|
-
createClientAdapter(config) {
|
|
2052
|
-
return new FeishuIMAdapter(config);
|
|
2521
|
+
createClientAdapter({ config, common }) {
|
|
2522
|
+
return new FeishuIMAdapter(config, void 0, common);
|
|
2053
2523
|
}
|
|
2054
2524
|
};
|
|
2055
2525
|
|
|
@@ -2519,7 +2989,6 @@ function parseWecomSessionId(clientSessionId) {
|
|
|
2519
2989
|
|
|
2520
2990
|
// src/modules/client/wecom/adapter/wecom-im-adapter.ts
|
|
2521
2991
|
var MAX_TEXT_CHUNK2 = 4e3;
|
|
2522
|
-
var STARTING_MESSAGE = "Processing...";
|
|
2523
2992
|
function chunkText2(text2, maxLen) {
|
|
2524
2993
|
if (text2.length <= maxLen) return [text2];
|
|
2525
2994
|
const chunks = [];
|
|
@@ -2546,6 +3015,7 @@ function chunkText2(text2, maxLen) {
|
|
|
2546
3015
|
var WecomIMAdapter = class {
|
|
2547
3016
|
#config;
|
|
2548
3017
|
#logger;
|
|
3018
|
+
#t;
|
|
2549
3019
|
#onOutput = null;
|
|
2550
3020
|
#client = null;
|
|
2551
3021
|
#egressQueue = [];
|
|
@@ -2561,18 +3031,17 @@ var WecomIMAdapter = class {
|
|
|
2561
3031
|
return;
|
|
2562
3032
|
}
|
|
2563
3033
|
const message = error instanceof Error ? error.message : String(error);
|
|
2564
|
-
const text2 =
|
|
2565
|
-
|
|
2566
|
-
${message}`;
|
|
3034
|
+
const text2 = formatSendFailureNotice(this.#t, message);
|
|
2567
3035
|
try {
|
|
2568
3036
|
await this.#client.sendText(chatId, text2);
|
|
2569
3037
|
} catch (notifyError) {
|
|
2570
3038
|
this.#logger.error("failed to notify send failure:", notifyError);
|
|
2571
3039
|
}
|
|
2572
3040
|
}
|
|
2573
|
-
constructor(config, logger3 = createLogger("wecom")) {
|
|
3041
|
+
constructor(config, logger3 = createLogger("wecom"), common) {
|
|
2574
3042
|
this.#config = config;
|
|
2575
3043
|
this.#logger = logger3;
|
|
3044
|
+
this.#t = getTranslatorForCommon(common);
|
|
2576
3045
|
}
|
|
2577
3046
|
async start(onOutput) {
|
|
2578
3047
|
this.#onOutput = onOutput;
|
|
@@ -2598,14 +3067,19 @@ ${message}`;
|
|
|
2598
3067
|
return;
|
|
2599
3068
|
}
|
|
2600
3069
|
const normalizedText = text2.trim();
|
|
2601
|
-
const
|
|
2602
|
-
if (
|
|
2603
|
-
await this.#
|
|
3070
|
+
const helpMarkdown = resolveHelpMarkdown(normalizedText, this.#t);
|
|
3071
|
+
if (helpMarkdown) {
|
|
3072
|
+
await this.#client?.sendText(chatId, helpMarkdown, messageId);
|
|
2604
3073
|
return;
|
|
2605
3074
|
}
|
|
2606
3075
|
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
2607
3076
|
this.#resetProgressState(clientSessionId);
|
|
2608
3077
|
await this.#announceStart(chatId, messageId, clientSessionId);
|
|
3078
|
+
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
3079
|
+
if (commandEvent) {
|
|
3080
|
+
await this.#onOutput(commandEvent);
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
2609
3083
|
await this.#onOutput({
|
|
2610
3084
|
type: "user.message",
|
|
2611
3085
|
clientSessionId,
|
|
@@ -2650,6 +3124,13 @@ ${message}`;
|
|
|
2650
3124
|
try {
|
|
2651
3125
|
const target = parseWecomSessionId(event.clientSessionId);
|
|
2652
3126
|
if (event.type !== "assistant.message") {
|
|
3127
|
+
const statusMarkdown = renderStatusMarkdown(event, this.#t);
|
|
3128
|
+
if (statusMarkdown) {
|
|
3129
|
+
const replyToMessageId2 = this.#lastInboundMessageIdBySession.get(event.clientSessionId);
|
|
3130
|
+
await this.#finishProgressMessage(target.chatId, event.clientSessionId, replyToMessageId2);
|
|
3131
|
+
await this.#client.sendText(target.chatId, statusMarkdown, replyToMessageId2);
|
|
3132
|
+
continue;
|
|
3133
|
+
}
|
|
2653
3134
|
await this.#handleProgressEvent(target.chatId, event);
|
|
2654
3135
|
continue;
|
|
2655
3136
|
}
|
|
@@ -2688,7 +3169,7 @@ ${message}`;
|
|
|
2688
3169
|
if (!state || state.announced || !this.#client) {
|
|
2689
3170
|
return;
|
|
2690
3171
|
}
|
|
2691
|
-
await this.#client.sendStreamText(chatId,
|
|
3172
|
+
await this.#client.sendStreamText(chatId, this.#t("client.processing"), {
|
|
2692
3173
|
replyToMessageId: messageId,
|
|
2693
3174
|
finish: false
|
|
2694
3175
|
});
|
|
@@ -2699,7 +3180,7 @@ ${message}`;
|
|
|
2699
3180
|
return;
|
|
2700
3181
|
}
|
|
2701
3182
|
const state = this.#progressStateBySession.get(event.clientSessionId) ?? {
|
|
2702
|
-
renderer: new ProgressRenderer(),
|
|
3183
|
+
renderer: new ProgressRenderer({ t: this.#t }),
|
|
2703
3184
|
announced: false
|
|
2704
3185
|
};
|
|
2705
3186
|
this.#progressStateBySession.set(event.clientSessionId, state);
|
|
@@ -2722,7 +3203,7 @@ ${message}`;
|
|
|
2722
3203
|
}
|
|
2723
3204
|
this.#progressStateBySession.delete(clientSessionId);
|
|
2724
3205
|
const progress = state.renderer.getCurrentProgress();
|
|
2725
|
-
const body = progress.
|
|
3206
|
+
const body = progress.isEmpty ? this.#t("client.processing") : progress.markdown;
|
|
2726
3207
|
try {
|
|
2727
3208
|
await this.#client.sendStreamText(chatId, body, { replyToMessageId, finish: true });
|
|
2728
3209
|
} catch (error) {
|
|
@@ -2731,7 +3212,7 @@ ${message}`;
|
|
|
2731
3212
|
}
|
|
2732
3213
|
#resetProgressState(clientSessionId) {
|
|
2733
3214
|
this.#progressStateBySession.set(clientSessionId, {
|
|
2734
|
-
renderer: new ProgressRenderer(),
|
|
3215
|
+
renderer: new ProgressRenderer({ t: this.#t }),
|
|
2735
3216
|
announced: false
|
|
2736
3217
|
});
|
|
2737
3218
|
}
|
|
@@ -2778,8 +3259,8 @@ function createWecomConfigCollector() {
|
|
|
2778
3259
|
var wecomClientModule = {
|
|
2779
3260
|
type: "wecom",
|
|
2780
3261
|
createConfigCollector: createWecomConfigCollector,
|
|
2781
|
-
createClientAdapter(config) {
|
|
2782
|
-
return new WecomIMAdapter(config);
|
|
3262
|
+
createClientAdapter({ config, common }) {
|
|
3263
|
+
return new WecomIMAdapter(config, void 0, common);
|
|
2783
3264
|
}
|
|
2784
3265
|
};
|
|
2785
3266
|
|
|
@@ -3087,6 +3568,7 @@ function chunkText3(text2, maxLen) {
|
|
|
3087
3568
|
var WeixinIMAdapter = class {
|
|
3088
3569
|
#config;
|
|
3089
3570
|
#logger;
|
|
3571
|
+
#t;
|
|
3090
3572
|
#onOutput = null;
|
|
3091
3573
|
#client = null;
|
|
3092
3574
|
#egressQueue = [];
|
|
@@ -3097,9 +3579,10 @@ var WeixinIMAdapter = class {
|
|
|
3097
3579
|
#recentInboundContentKeys = /* @__PURE__ */ new Map();
|
|
3098
3580
|
#rateLimitEvents = [];
|
|
3099
3581
|
#rateLimitCircuitUntil = 0;
|
|
3100
|
-
constructor(config, logger3 = createLogger("weixin")) {
|
|
3582
|
+
constructor(config, logger3 = createLogger("weixin"), common) {
|
|
3101
3583
|
this.#config = config;
|
|
3102
3584
|
this.#logger = logger3;
|
|
3585
|
+
this.#t = getTranslatorForCommon(common);
|
|
3103
3586
|
}
|
|
3104
3587
|
async start(onOutput) {
|
|
3105
3588
|
this.#onOutput = onOutput;
|
|
@@ -3120,15 +3603,23 @@ var WeixinIMAdapter = class {
|
|
|
3120
3603
|
this.#logger.debug(`ignoring unsupported Weixin group message (session=${clientSessionId})`);
|
|
3121
3604
|
return;
|
|
3122
3605
|
}
|
|
3123
|
-
this.#resetProgressState(clientSessionId);
|
|
3124
3606
|
const normalizedText = text2.trim();
|
|
3607
|
+
this.#resetProgressState(clientSessionId);
|
|
3608
|
+
await this.#client?.sendTyping(chatId);
|
|
3609
|
+
this.#startTypingHeartbeat(clientSessionId, chatId);
|
|
3610
|
+
const helpMarkdown = resolveHelpMarkdown(normalizedText, this.#t);
|
|
3611
|
+
if (helpMarkdown) {
|
|
3612
|
+
await this.#client?.sendText(chatId, helpMarkdown);
|
|
3613
|
+
this.#stopProgressTimer(clientSessionId);
|
|
3614
|
+
this.#stopTypingHeartbeat(clientSessionId);
|
|
3615
|
+
await this.#client?.stopTyping(chatId);
|
|
3616
|
+
return;
|
|
3617
|
+
}
|
|
3125
3618
|
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
3126
3619
|
if (commandEvent) {
|
|
3127
3620
|
await this.#onOutput(commandEvent);
|
|
3128
3621
|
return;
|
|
3129
3622
|
}
|
|
3130
|
-
await this.#client?.sendTyping(chatId);
|
|
3131
|
-
this.#startTypingHeartbeat(clientSessionId, chatId);
|
|
3132
3623
|
await this.#onOutput({
|
|
3133
3624
|
type: "user.message",
|
|
3134
3625
|
clientSessionId,
|
|
@@ -3184,6 +3675,14 @@ var WeixinIMAdapter = class {
|
|
|
3184
3675
|
continue;
|
|
3185
3676
|
}
|
|
3186
3677
|
if (event.type !== "assistant.message") {
|
|
3678
|
+
const statusMarkdown = renderStatusMarkdown(event, this.#t);
|
|
3679
|
+
if (statusMarkdown) {
|
|
3680
|
+
await this.#sendTextWithProtection(target.chatId, statusMarkdown);
|
|
3681
|
+
this.#stopProgressTimer(event.clientSessionId);
|
|
3682
|
+
this.#stopTypingHeartbeat(event.clientSessionId);
|
|
3683
|
+
await this.#client.stopTyping(target.chatId);
|
|
3684
|
+
continue;
|
|
3685
|
+
}
|
|
3187
3686
|
this.#recordProgressEvent(event);
|
|
3188
3687
|
continue;
|
|
3189
3688
|
}
|
|
@@ -3241,9 +3740,7 @@ var WeixinIMAdapter = class {
|
|
|
3241
3740
|
return;
|
|
3242
3741
|
}
|
|
3243
3742
|
const message = error instanceof Error ? error.message : String(error);
|
|
3244
|
-
const text2 =
|
|
3245
|
-
|
|
3246
|
-
${message}`;
|
|
3743
|
+
const text2 = formatSendFailureNotice(this.#t, message);
|
|
3247
3744
|
try {
|
|
3248
3745
|
await this.#client.sendText(chatId, text2);
|
|
3249
3746
|
} catch (notifyError) {
|
|
@@ -3256,7 +3753,7 @@ ${message}`;
|
|
|
3256
3753
|
}
|
|
3257
3754
|
const now = Date.now();
|
|
3258
3755
|
if (this.#rateLimitCircuitUntil > now) {
|
|
3259
|
-
throw new Error("
|
|
3756
|
+
throw new Error(this.#t("client.weixinCooldown"));
|
|
3260
3757
|
}
|
|
3261
3758
|
if (this.#rateLimitCircuitUntil !== 0 && this.#rateLimitCircuitUntil <= now) {
|
|
3262
3759
|
this.#rateLimitCircuitUntil = 0;
|
|
@@ -3272,7 +3769,7 @@ ${message}`;
|
|
|
3272
3769
|
if (this.#isRateLimitError(error)) {
|
|
3273
3770
|
this.#recordRateLimitEvent(now);
|
|
3274
3771
|
if (this.#rateLimitCircuitUntil > now) {
|
|
3275
|
-
throw new Error("
|
|
3772
|
+
throw new Error(this.#t("client.weixinCooldown"));
|
|
3276
3773
|
}
|
|
3277
3774
|
}
|
|
3278
3775
|
throw error;
|
|
@@ -3301,7 +3798,7 @@ ${message}`;
|
|
|
3301
3798
|
}
|
|
3302
3799
|
#createProgressState() {
|
|
3303
3800
|
return {
|
|
3304
|
-
renderer: new ProgressRenderer(),
|
|
3801
|
+
renderer: new ProgressRenderer({ t: this.#t }),
|
|
3305
3802
|
dirty: false,
|
|
3306
3803
|
interval: null
|
|
3307
3804
|
};
|
|
@@ -3486,8 +3983,8 @@ function createWeixinConfigCollector() {
|
|
|
3486
3983
|
var weixinClientModule = {
|
|
3487
3984
|
type: "weixin",
|
|
3488
3985
|
createConfigCollector: createWeixinConfigCollector,
|
|
3489
|
-
createClientAdapter(config) {
|
|
3490
|
-
return new WeixinIMAdapter(config);
|
|
3986
|
+
createClientAdapter({ config, common }) {
|
|
3987
|
+
return new WeixinIMAdapter(config, void 0, common);
|
|
3491
3988
|
}
|
|
3492
3989
|
};
|
|
3493
3990
|
|
|
@@ -3516,14 +4013,19 @@ var logger2 = createLogger("runner");
|
|
|
3516
4013
|
async function runChannel({ channelName, channelConfig, defaults }) {
|
|
3517
4014
|
const clientModule = getTypedClientModule(channelConfig.client);
|
|
3518
4015
|
const agentModule = getTypedAgentModule(channelConfig.agent);
|
|
3519
|
-
const
|
|
4016
|
+
const common = {
|
|
4017
|
+
channelName,
|
|
4018
|
+
language: channelConfig.common.language
|
|
4019
|
+
};
|
|
4020
|
+
const imAdapter = clientModule.createClientAdapter({ config: channelConfig.client.config, common });
|
|
3520
4021
|
const bindingStore = createFileSessionBindingStore(getSessionBindingStorePath(channelName));
|
|
3521
4022
|
const core = new GatewayCore({
|
|
3522
4023
|
imAdapter,
|
|
3523
4024
|
agentModule,
|
|
3524
4025
|
agentConfig: channelConfig.agent.config,
|
|
3525
4026
|
agentIdleTimeoutMs: defaults.agentIdleTimeoutMs,
|
|
3526
|
-
bindingStore
|
|
4027
|
+
bindingStore,
|
|
4028
|
+
common
|
|
3527
4029
|
});
|
|
3528
4030
|
await core.start();
|
|
3529
4031
|
logger2.info(`channel ${channelName} started`);
|
|
@@ -3537,6 +4039,8 @@ async function runChannel({ channelName, channelConfig, defaults }) {
|
|
|
3537
4039
|
}
|
|
3538
4040
|
|
|
3539
4041
|
// src/cli.ts
|
|
4042
|
+
var require2 = createRequire(import.meta.url);
|
|
4043
|
+
var { version } = require2("../package.json");
|
|
3540
4044
|
async function selectModuleType(label, modules, ctx) {
|
|
3541
4045
|
if (modules.length === 0) {
|
|
3542
4046
|
throw new Error(`No modules available for ${label}`);
|
|
@@ -3558,6 +4062,15 @@ async function collectModuleConfig(module, ctx) {
|
|
|
3558
4062
|
await collector.validate(config);
|
|
3559
4063
|
return config;
|
|
3560
4064
|
}
|
|
4065
|
+
async function collectCommonChannelConfig(ctx) {
|
|
4066
|
+
const language = await ctx.select("Channel language", [
|
|
4067
|
+
{ label: "English (en-US)", value: "en-US" },
|
|
4068
|
+
{ label: "\u4E2D\u6587 (zh-CN)", value: "zh-CN" }
|
|
4069
|
+
]);
|
|
4070
|
+
return {
|
|
4071
|
+
language
|
|
4072
|
+
};
|
|
4073
|
+
}
|
|
3561
4074
|
async function addChannel(config) {
|
|
3562
4075
|
const ctx = createPromptContext();
|
|
3563
4076
|
try {
|
|
@@ -3569,6 +4082,7 @@ async function addChannel(config) {
|
|
|
3569
4082
|
return null;
|
|
3570
4083
|
}
|
|
3571
4084
|
});
|
|
4085
|
+
const commonConfig = await collectCommonChannelConfig(ctx);
|
|
3572
4086
|
const clientType = await selectModuleType("Select client module", listClientModules(), ctx);
|
|
3573
4087
|
const clientModule = getClientModule(clientType);
|
|
3574
4088
|
if (!clientModule) {
|
|
@@ -3582,6 +4096,7 @@ async function addChannel(config) {
|
|
|
3582
4096
|
}
|
|
3583
4097
|
const agentConfig = await collectModuleConfig(agentModule, ctx);
|
|
3584
4098
|
config.channels[name] = {
|
|
4099
|
+
common: commonConfig,
|
|
3585
4100
|
client: {
|
|
3586
4101
|
type: clientType,
|
|
3587
4102
|
config: clientConfig
|
|
@@ -3660,7 +4175,7 @@ async function startChannel(channelName) {
|
|
|
3660
4175
|
}
|
|
3661
4176
|
async function runCli(argv = process2.argv) {
|
|
3662
4177
|
const program = new Command();
|
|
3663
|
-
program.name("agent-bridge").description("IM to Agent bridge CLI").version(
|
|
4178
|
+
program.name("agent-bridge").description("IM to Agent bridge CLI").version(version);
|
|
3664
4179
|
program.command("add").description("Interactively add a channel").action(async () => {
|
|
3665
4180
|
const config = await loadConfig();
|
|
3666
4181
|
await addChannel(config);
|