@hopgoldy/agent-bridge 0.3.4 → 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/dist/agent-bridge.js +402 -30
- package/dist/cli.js +402 -30
- package/dist/index.d.ts +47 -1
- package/package.json +1 -1
package/dist/agent-bridge.js
CHANGED
|
@@ -116,18 +116,33 @@ var resources = {
|
|
|
116
116
|
progress: {
|
|
117
117
|
noProgress: "No progress yet.",
|
|
118
118
|
collapsed: "Collapsed {{count}} earlier updates.",
|
|
119
|
-
running: "
|
|
120
|
-
finished: "
|
|
121
|
-
failed: "
|
|
122
|
-
failedWithDetail: "
|
|
119
|
+
running: "\u23F3 {{subject}}",
|
|
120
|
+
finished: "\u2705 {{subject}}",
|
|
121
|
+
failed: "\u274C {{subject}}",
|
|
122
|
+
failedWithDetail: "\u274C {{subject}}: {{detail}}",
|
|
123
123
|
compacting: "Compacting session",
|
|
124
124
|
compactingWithDetail: "Compacting session: {{detail}}"
|
|
125
125
|
},
|
|
126
126
|
client: {
|
|
127
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- `/help` (`/h`) - Show this help message",
|
|
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
129
|
messageDeliveryFailedTitle: "[agent-bridge error] Message delivery failed",
|
|
130
|
-
weixinCooldown: "Weixin send is cooling down after rate limiting. Please try again shortly."
|
|
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}}`."
|
|
131
146
|
},
|
|
132
147
|
gateway: {
|
|
133
148
|
noActiveSessionToCompact: "No active agent session to compact.",
|
|
@@ -143,18 +158,33 @@ var resources = {
|
|
|
143
158
|
progress: {
|
|
144
159
|
noProgress: "\u6682\u65E0\u8FDB\u5EA6\u3002",
|
|
145
160
|
collapsed: "\u5DF2\u6298\u53E0 {{count}} \u6761\u8F83\u65E9\u66F4\u65B0\u3002",
|
|
146
|
-
running: "\
|
|
147
|
-
finished: "\
|
|
148
|
-
failed: "{{subject}}
|
|
149
|
-
failedWithDetail: "{{subject}}
|
|
161
|
+
running: "\u23F3 {{subject}}",
|
|
162
|
+
finished: "\u2705 {{subject}}",
|
|
163
|
+
failed: "\u274C {{subject}}",
|
|
164
|
+
failedWithDetail: "\u274C {{subject}}: {{detail}}",
|
|
150
165
|
compacting: "\u6B63\u5728\u538B\u7F29\u4F1A\u8BDD",
|
|
151
166
|
compactingWithDetail: "\u6B63\u5728\u538B\u7F29\u4F1A\u8BDD: {{detail}}"
|
|
152
167
|
},
|
|
153
168
|
client: {
|
|
154
169
|
processing: "\u6B63\u5728\u5904\u7406\u4E2D...",
|
|
155
|
-
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- `/help` (`/h`) - \u67E5\u770B\u8FD9\u6761\u5E2E\u52A9\u4FE1\u606F",
|
|
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",
|
|
156
171
|
messageDeliveryFailedTitle: "[agent-bridge \u9519\u8BEF] \u6D88\u606F\u53D1\u9001\u5931\u8D25",
|
|
157
|
-
weixinCooldown: "\u5FAE\u4FE1\u53D1\u9001\u56E0\u9650\u6D41\u5DF2\u8FDB\u5165\u51B7\u5374\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002"
|
|
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"
|
|
158
188
|
},
|
|
159
189
|
gateway: {
|
|
160
190
|
noActiveSessionToCompact: "\u5F53\u524D\u6CA1\u6709\u53EF\u538B\u7F29\u7684\u667A\u80FD\u4F53\u4F1A\u8BDD\u3002",
|
|
@@ -335,6 +365,18 @@ var GatewayCore = class {
|
|
|
335
365
|
await this.#handleSessionStop(event.clientSessionId);
|
|
336
366
|
return;
|
|
337
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
|
+
}
|
|
338
380
|
await this.#handleUserMessage(event.clientSessionId, event.text);
|
|
339
381
|
}
|
|
340
382
|
async #handleUserMessage(clientSessionId, text2) {
|
|
@@ -389,6 +431,123 @@ var GatewayCore = class {
|
|
|
389
431
|
}
|
|
390
432
|
await runtime.agentAdapter.abort();
|
|
391
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
|
+
}
|
|
392
551
|
async #handleSessionNew(clientSessionId) {
|
|
393
552
|
const previousAgentSessionId = this.#clientToAgentSession.get(clientSessionId);
|
|
394
553
|
if (previousAgentSessionId) {
|
|
@@ -482,6 +641,18 @@ var GatewayCore = class {
|
|
|
482
641
|
this.#logger.error("failed to persist session bindings:", error);
|
|
483
642
|
}
|
|
484
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
|
+
}
|
|
485
656
|
async #handleAgentOutput(event) {
|
|
486
657
|
const agentSessionId = event.agentSessionId;
|
|
487
658
|
const runtime = this.#agentRuntimes.get(agentSessionId);
|
|
@@ -884,6 +1055,19 @@ var PiRpcClient = class {
|
|
|
884
1055
|
const response = await this.#send({ type: "get_state" });
|
|
885
1056
|
return response.data ?? {};
|
|
886
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
|
+
}
|
|
887
1071
|
async setSessionName(name) {
|
|
888
1072
|
await this.#send({ type: "set_session_name", name });
|
|
889
1073
|
}
|
|
@@ -990,6 +1174,13 @@ function toPiSessionId(agentSessionId) {
|
|
|
990
1174
|
}
|
|
991
1175
|
|
|
992
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
|
+
};
|
|
993
1184
|
var PiCodingAgentAdapter = class {
|
|
994
1185
|
#agentSessionId;
|
|
995
1186
|
#piSessionId;
|
|
@@ -1072,6 +1263,71 @@ var PiCodingAgentAdapter = class {
|
|
|
1072
1263
|
async isBusy() {
|
|
1073
1264
|
return this.#processing || this.#inputQueue.length > 0;
|
|
1074
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
|
+
}
|
|
1075
1331
|
async #drainInputQueue() {
|
|
1076
1332
|
if (this.#processing) {
|
|
1077
1333
|
return;
|
|
@@ -1183,7 +1439,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1183
1439
|
toolCallId,
|
|
1184
1440
|
toolInput,
|
|
1185
1441
|
toolLabel,
|
|
1186
|
-
text:
|
|
1442
|
+
text: void 0
|
|
1187
1443
|
});
|
|
1188
1444
|
return;
|
|
1189
1445
|
}
|
|
@@ -1204,7 +1460,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1204
1460
|
toolInput,
|
|
1205
1461
|
toolLabel,
|
|
1206
1462
|
partialResult: "partialResult" in rpcEvent ? rpcEvent.partialResult : void 0,
|
|
1207
|
-
text:
|
|
1463
|
+
text: void 0
|
|
1208
1464
|
});
|
|
1209
1465
|
return;
|
|
1210
1466
|
}
|
|
@@ -1222,7 +1478,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1222
1478
|
toolInput,
|
|
1223
1479
|
toolLabel,
|
|
1224
1480
|
result: "result" in rpcEvent ? rpcEvent.result : void 0,
|
|
1225
|
-
text:
|
|
1481
|
+
text: void 0
|
|
1226
1482
|
});
|
|
1227
1483
|
if (toolCallId) {
|
|
1228
1484
|
this.#toolLabelByCallId.delete(toolCallId);
|
|
@@ -1419,7 +1675,7 @@ function getTypedAgentModule(config) {
|
|
|
1419
1675
|
|
|
1420
1676
|
// src/modules/client/utils/progress-renderer.ts
|
|
1421
1677
|
var DEFAULT_COLLAPSE_THRESHOLD = 10;
|
|
1422
|
-
var MAX_TOOL_LABEL_DISPLAY_LENGTH =
|
|
1678
|
+
var MAX_TOOL_LABEL_DISPLAY_LENGTH = 24;
|
|
1423
1679
|
var ProgressRenderer = class {
|
|
1424
1680
|
#collapseThreshold;
|
|
1425
1681
|
#t;
|
|
@@ -1531,11 +1787,6 @@ var ProgressRenderer = class {
|
|
|
1531
1787
|
if (!normalizedText) {
|
|
1532
1788
|
return `- ${this.#humanizeToolError(toolName)}`;
|
|
1533
1789
|
}
|
|
1534
|
-
const lowerText = normalizedText.toLowerCase();
|
|
1535
|
-
const lowerToolName = toolName.toLowerCase();
|
|
1536
|
-
if (lowerText === lowerToolName || lowerText === this.#t("progress.failed", { subject: toolName }).toLowerCase() || lowerText === this.#t("progress.running", { subject: toolName }).toLowerCase() || lowerText === this.#t("progress.finished", { subject: toolName }).toLowerCase()) {
|
|
1537
|
-
return `- ${this.#humanizeToolError(toolName)}`;
|
|
1538
|
-
}
|
|
1539
1790
|
return `- ${this.#t("progress.failedWithDetail", { subject: toolName, detail: normalizedText })}`;
|
|
1540
1791
|
}
|
|
1541
1792
|
#humanizeToolError(toolName) {
|
|
@@ -1566,7 +1817,26 @@ function isHelpCommand(text2) {
|
|
|
1566
1817
|
function resolveHelpMarkdown(text2, t) {
|
|
1567
1818
|
return isHelpCommand(text2) ? t("client.helpMessage") : null;
|
|
1568
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
|
+
}
|
|
1569
1835
|
function parseSlashCommand(text2, clientSessionId) {
|
|
1836
|
+
const modelCommand = parseModelCommand(text2, clientSessionId);
|
|
1837
|
+
if (modelCommand) {
|
|
1838
|
+
return modelCommand;
|
|
1839
|
+
}
|
|
1570
1840
|
switch (text2.toLowerCase()) {
|
|
1571
1841
|
case "/new":
|
|
1572
1842
|
case "/n":
|
|
@@ -1577,11 +1847,87 @@ function parseSlashCommand(text2, clientSessionId) {
|
|
|
1577
1847
|
case "/stop":
|
|
1578
1848
|
case "/s":
|
|
1579
1849
|
return { type: "command.session.stop", clientSessionId };
|
|
1850
|
+
case "/status":
|
|
1851
|
+
case "/st":
|
|
1852
|
+
return { type: "command.session.status", clientSessionId };
|
|
1580
1853
|
default:
|
|
1581
1854
|
return null;
|
|
1582
1855
|
}
|
|
1583
1856
|
}
|
|
1584
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
|
+
|
|
1585
1931
|
// src/modules/client/feishu/adapter/feishu-client.ts
|
|
1586
1932
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
1587
1933
|
import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
|
|
@@ -1981,10 +2327,14 @@ var FeishuIMAdapter = class _FeishuIMAdapter {
|
|
|
1981
2327
|
return;
|
|
1982
2328
|
}
|
|
1983
2329
|
const normalizedText = text2.trim();
|
|
2330
|
+
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
2331
|
+
this.#resetProgressState(clientSessionId);
|
|
2332
|
+
await this.#client?.startTyping(chatId, messageId);
|
|
1984
2333
|
const helpMarkdown = resolveHelpMarkdown(normalizedText, this.#t);
|
|
1985
2334
|
if (helpMarkdown) {
|
|
1986
2335
|
this.#logger.info(`received local help command ${normalizedText} (session=${clientSessionId})`);
|
|
1987
2336
|
await this.#client?.sendText(chatId, helpMarkdown, messageId);
|
|
2337
|
+
await this.#client?.stopTyping(chatId);
|
|
1988
2338
|
return;
|
|
1989
2339
|
}
|
|
1990
2340
|
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
@@ -1993,9 +2343,6 @@ var FeishuIMAdapter = class _FeishuIMAdapter {
|
|
|
1993
2343
|
await this.#onOutput(commandEvent);
|
|
1994
2344
|
return;
|
|
1995
2345
|
}
|
|
1996
|
-
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
1997
|
-
this.#resetProgressState(clientSessionId);
|
|
1998
|
-
await this.#client?.startTyping(chatId, messageId);
|
|
1999
2346
|
this.#logger.info(`received user message (session=${clientSessionId}): ${normalizedText}`);
|
|
2000
2347
|
await this.#onOutput({
|
|
2001
2348
|
type: "user.message",
|
|
@@ -2041,6 +2388,13 @@ var FeishuIMAdapter = class _FeishuIMAdapter {
|
|
|
2041
2388
|
try {
|
|
2042
2389
|
const target = parseFeishuSessionId(event.clientSessionId);
|
|
2043
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
|
+
}
|
|
2044
2398
|
await this.#handleProgressEvent(target.chatId, event);
|
|
2045
2399
|
continue;
|
|
2046
2400
|
}
|
|
@@ -2718,14 +3072,14 @@ var WecomIMAdapter = class {
|
|
|
2718
3072
|
await this.#client?.sendText(chatId, helpMarkdown, messageId);
|
|
2719
3073
|
return;
|
|
2720
3074
|
}
|
|
3075
|
+
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
3076
|
+
this.#resetProgressState(clientSessionId);
|
|
3077
|
+
await this.#announceStart(chatId, messageId, clientSessionId);
|
|
2721
3078
|
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
2722
3079
|
if (commandEvent) {
|
|
2723
3080
|
await this.#onOutput(commandEvent);
|
|
2724
3081
|
return;
|
|
2725
3082
|
}
|
|
2726
|
-
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
2727
|
-
this.#resetProgressState(clientSessionId);
|
|
2728
|
-
await this.#announceStart(chatId, messageId, clientSessionId);
|
|
2729
3083
|
await this.#onOutput({
|
|
2730
3084
|
type: "user.message",
|
|
2731
3085
|
clientSessionId,
|
|
@@ -2770,6 +3124,13 @@ var WecomIMAdapter = class {
|
|
|
2770
3124
|
try {
|
|
2771
3125
|
const target = parseWecomSessionId(event.clientSessionId);
|
|
2772
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
|
+
}
|
|
2773
3134
|
await this.#handleProgressEvent(target.chatId, event);
|
|
2774
3135
|
continue;
|
|
2775
3136
|
}
|
|
@@ -3243,19 +3604,22 @@ var WeixinIMAdapter = class {
|
|
|
3243
3604
|
return;
|
|
3244
3605
|
}
|
|
3245
3606
|
const normalizedText = text2.trim();
|
|
3607
|
+
this.#resetProgressState(clientSessionId);
|
|
3608
|
+
await this.#client?.sendTyping(chatId);
|
|
3609
|
+
this.#startTypingHeartbeat(clientSessionId, chatId);
|
|
3246
3610
|
const helpMarkdown = resolveHelpMarkdown(normalizedText, this.#t);
|
|
3247
3611
|
if (helpMarkdown) {
|
|
3248
3612
|
await this.#client?.sendText(chatId, helpMarkdown);
|
|
3613
|
+
this.#stopProgressTimer(clientSessionId);
|
|
3614
|
+
this.#stopTypingHeartbeat(clientSessionId);
|
|
3615
|
+
await this.#client?.stopTyping(chatId);
|
|
3249
3616
|
return;
|
|
3250
3617
|
}
|
|
3251
|
-
this.#resetProgressState(clientSessionId);
|
|
3252
3618
|
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
3253
3619
|
if (commandEvent) {
|
|
3254
3620
|
await this.#onOutput(commandEvent);
|
|
3255
3621
|
return;
|
|
3256
3622
|
}
|
|
3257
|
-
await this.#client?.sendTyping(chatId);
|
|
3258
|
-
this.#startTypingHeartbeat(clientSessionId, chatId);
|
|
3259
3623
|
await this.#onOutput({
|
|
3260
3624
|
type: "user.message",
|
|
3261
3625
|
clientSessionId,
|
|
@@ -3311,6 +3675,14 @@ var WeixinIMAdapter = class {
|
|
|
3311
3675
|
continue;
|
|
3312
3676
|
}
|
|
3313
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
|
+
}
|
|
3314
3686
|
this.#recordProgressEvent(event);
|
|
3315
3687
|
continue;
|
|
3316
3688
|
}
|
package/dist/cli.js
CHANGED
|
@@ -116,18 +116,33 @@ var resources = {
|
|
|
116
116
|
progress: {
|
|
117
117
|
noProgress: "No progress yet.",
|
|
118
118
|
collapsed: "Collapsed {{count}} earlier updates.",
|
|
119
|
-
running: "
|
|
120
|
-
finished: "
|
|
121
|
-
failed: "
|
|
122
|
-
failedWithDetail: "
|
|
119
|
+
running: "\u23F3 {{subject}}",
|
|
120
|
+
finished: "\u2705 {{subject}}",
|
|
121
|
+
failed: "\u274C {{subject}}",
|
|
122
|
+
failedWithDetail: "\u274C {{subject}}: {{detail}}",
|
|
123
123
|
compacting: "Compacting session",
|
|
124
124
|
compactingWithDetail: "Compacting session: {{detail}}"
|
|
125
125
|
},
|
|
126
126
|
client: {
|
|
127
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- `/help` (`/h`) - Show this help message",
|
|
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
129
|
messageDeliveryFailedTitle: "[agent-bridge error] Message delivery failed",
|
|
130
|
-
weixinCooldown: "Weixin send is cooling down after rate limiting. Please try again shortly."
|
|
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}}`."
|
|
131
146
|
},
|
|
132
147
|
gateway: {
|
|
133
148
|
noActiveSessionToCompact: "No active agent session to compact.",
|
|
@@ -143,18 +158,33 @@ var resources = {
|
|
|
143
158
|
progress: {
|
|
144
159
|
noProgress: "\u6682\u65E0\u8FDB\u5EA6\u3002",
|
|
145
160
|
collapsed: "\u5DF2\u6298\u53E0 {{count}} \u6761\u8F83\u65E9\u66F4\u65B0\u3002",
|
|
146
|
-
running: "\
|
|
147
|
-
finished: "\
|
|
148
|
-
failed: "{{subject}}
|
|
149
|
-
failedWithDetail: "{{subject}}
|
|
161
|
+
running: "\u23F3 {{subject}}",
|
|
162
|
+
finished: "\u2705 {{subject}}",
|
|
163
|
+
failed: "\u274C {{subject}}",
|
|
164
|
+
failedWithDetail: "\u274C {{subject}}: {{detail}}",
|
|
150
165
|
compacting: "\u6B63\u5728\u538B\u7F29\u4F1A\u8BDD",
|
|
151
166
|
compactingWithDetail: "\u6B63\u5728\u538B\u7F29\u4F1A\u8BDD: {{detail}}"
|
|
152
167
|
},
|
|
153
168
|
client: {
|
|
154
169
|
processing: "\u6B63\u5728\u5904\u7406\u4E2D...",
|
|
155
|
-
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- `/help` (`/h`) - \u67E5\u770B\u8FD9\u6761\u5E2E\u52A9\u4FE1\u606F",
|
|
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",
|
|
156
171
|
messageDeliveryFailedTitle: "[agent-bridge \u9519\u8BEF] \u6D88\u606F\u53D1\u9001\u5931\u8D25",
|
|
157
|
-
weixinCooldown: "\u5FAE\u4FE1\u53D1\u9001\u56E0\u9650\u6D41\u5DF2\u8FDB\u5165\u51B7\u5374\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002"
|
|
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"
|
|
158
188
|
},
|
|
159
189
|
gateway: {
|
|
160
190
|
noActiveSessionToCompact: "\u5F53\u524D\u6CA1\u6709\u53EF\u538B\u7F29\u7684\u667A\u80FD\u4F53\u4F1A\u8BDD\u3002",
|
|
@@ -335,6 +365,18 @@ var GatewayCore = class {
|
|
|
335
365
|
await this.#handleSessionStop(event.clientSessionId);
|
|
336
366
|
return;
|
|
337
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
|
+
}
|
|
338
380
|
await this.#handleUserMessage(event.clientSessionId, event.text);
|
|
339
381
|
}
|
|
340
382
|
async #handleUserMessage(clientSessionId, text2) {
|
|
@@ -389,6 +431,123 @@ var GatewayCore = class {
|
|
|
389
431
|
}
|
|
390
432
|
await runtime.agentAdapter.abort();
|
|
391
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
|
+
}
|
|
392
551
|
async #handleSessionNew(clientSessionId) {
|
|
393
552
|
const previousAgentSessionId = this.#clientToAgentSession.get(clientSessionId);
|
|
394
553
|
if (previousAgentSessionId) {
|
|
@@ -482,6 +641,18 @@ var GatewayCore = class {
|
|
|
482
641
|
this.#logger.error("failed to persist session bindings:", error);
|
|
483
642
|
}
|
|
484
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
|
+
}
|
|
485
656
|
async #handleAgentOutput(event) {
|
|
486
657
|
const agentSessionId = event.agentSessionId;
|
|
487
658
|
const runtime = this.#agentRuntimes.get(agentSessionId);
|
|
@@ -884,6 +1055,19 @@ var PiRpcClient = class {
|
|
|
884
1055
|
const response = await this.#send({ type: "get_state" });
|
|
885
1056
|
return response.data ?? {};
|
|
886
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
|
+
}
|
|
887
1071
|
async setSessionName(name) {
|
|
888
1072
|
await this.#send({ type: "set_session_name", name });
|
|
889
1073
|
}
|
|
@@ -990,6 +1174,13 @@ function toPiSessionId(agentSessionId) {
|
|
|
990
1174
|
}
|
|
991
1175
|
|
|
992
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
|
+
};
|
|
993
1184
|
var PiCodingAgentAdapter = class {
|
|
994
1185
|
#agentSessionId;
|
|
995
1186
|
#piSessionId;
|
|
@@ -1072,6 +1263,71 @@ var PiCodingAgentAdapter = class {
|
|
|
1072
1263
|
async isBusy() {
|
|
1073
1264
|
return this.#processing || this.#inputQueue.length > 0;
|
|
1074
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
|
+
}
|
|
1075
1331
|
async #drainInputQueue() {
|
|
1076
1332
|
if (this.#processing) {
|
|
1077
1333
|
return;
|
|
@@ -1183,7 +1439,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1183
1439
|
toolCallId,
|
|
1184
1440
|
toolInput,
|
|
1185
1441
|
toolLabel,
|
|
1186
|
-
text:
|
|
1442
|
+
text: void 0
|
|
1187
1443
|
});
|
|
1188
1444
|
return;
|
|
1189
1445
|
}
|
|
@@ -1204,7 +1460,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1204
1460
|
toolInput,
|
|
1205
1461
|
toolLabel,
|
|
1206
1462
|
partialResult: "partialResult" in rpcEvent ? rpcEvent.partialResult : void 0,
|
|
1207
|
-
text:
|
|
1463
|
+
text: void 0
|
|
1208
1464
|
});
|
|
1209
1465
|
return;
|
|
1210
1466
|
}
|
|
@@ -1222,7 +1478,7 @@ var PiCodingAgentAdapter = class {
|
|
|
1222
1478
|
toolInput,
|
|
1223
1479
|
toolLabel,
|
|
1224
1480
|
result: "result" in rpcEvent ? rpcEvent.result : void 0,
|
|
1225
|
-
text:
|
|
1481
|
+
text: void 0
|
|
1226
1482
|
});
|
|
1227
1483
|
if (toolCallId) {
|
|
1228
1484
|
this.#toolLabelByCallId.delete(toolCallId);
|
|
@@ -1419,7 +1675,7 @@ function getTypedAgentModule(config) {
|
|
|
1419
1675
|
|
|
1420
1676
|
// src/modules/client/utils/progress-renderer.ts
|
|
1421
1677
|
var DEFAULT_COLLAPSE_THRESHOLD = 10;
|
|
1422
|
-
var MAX_TOOL_LABEL_DISPLAY_LENGTH =
|
|
1678
|
+
var MAX_TOOL_LABEL_DISPLAY_LENGTH = 24;
|
|
1423
1679
|
var ProgressRenderer = class {
|
|
1424
1680
|
#collapseThreshold;
|
|
1425
1681
|
#t;
|
|
@@ -1531,11 +1787,6 @@ var ProgressRenderer = class {
|
|
|
1531
1787
|
if (!normalizedText) {
|
|
1532
1788
|
return `- ${this.#humanizeToolError(toolName)}`;
|
|
1533
1789
|
}
|
|
1534
|
-
const lowerText = normalizedText.toLowerCase();
|
|
1535
|
-
const lowerToolName = toolName.toLowerCase();
|
|
1536
|
-
if (lowerText === lowerToolName || lowerText === this.#t("progress.failed", { subject: toolName }).toLowerCase() || lowerText === this.#t("progress.running", { subject: toolName }).toLowerCase() || lowerText === this.#t("progress.finished", { subject: toolName }).toLowerCase()) {
|
|
1537
|
-
return `- ${this.#humanizeToolError(toolName)}`;
|
|
1538
|
-
}
|
|
1539
1790
|
return `- ${this.#t("progress.failedWithDetail", { subject: toolName, detail: normalizedText })}`;
|
|
1540
1791
|
}
|
|
1541
1792
|
#humanizeToolError(toolName) {
|
|
@@ -1566,7 +1817,26 @@ function isHelpCommand(text2) {
|
|
|
1566
1817
|
function resolveHelpMarkdown(text2, t) {
|
|
1567
1818
|
return isHelpCommand(text2) ? t("client.helpMessage") : null;
|
|
1568
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
|
+
}
|
|
1569
1835
|
function parseSlashCommand(text2, clientSessionId) {
|
|
1836
|
+
const modelCommand = parseModelCommand(text2, clientSessionId);
|
|
1837
|
+
if (modelCommand) {
|
|
1838
|
+
return modelCommand;
|
|
1839
|
+
}
|
|
1570
1840
|
switch (text2.toLowerCase()) {
|
|
1571
1841
|
case "/new":
|
|
1572
1842
|
case "/n":
|
|
@@ -1577,11 +1847,87 @@ function parseSlashCommand(text2, clientSessionId) {
|
|
|
1577
1847
|
case "/stop":
|
|
1578
1848
|
case "/s":
|
|
1579
1849
|
return { type: "command.session.stop", clientSessionId };
|
|
1850
|
+
case "/status":
|
|
1851
|
+
case "/st":
|
|
1852
|
+
return { type: "command.session.status", clientSessionId };
|
|
1580
1853
|
default:
|
|
1581
1854
|
return null;
|
|
1582
1855
|
}
|
|
1583
1856
|
}
|
|
1584
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
|
+
|
|
1585
1931
|
// src/modules/client/feishu/adapter/feishu-client.ts
|
|
1586
1932
|
import * as Lark from "@larksuiteoapi/node-sdk";
|
|
1587
1933
|
import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
|
|
@@ -1981,10 +2327,14 @@ var FeishuIMAdapter = class _FeishuIMAdapter {
|
|
|
1981
2327
|
return;
|
|
1982
2328
|
}
|
|
1983
2329
|
const normalizedText = text2.trim();
|
|
2330
|
+
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
2331
|
+
this.#resetProgressState(clientSessionId);
|
|
2332
|
+
await this.#client?.startTyping(chatId, messageId);
|
|
1984
2333
|
const helpMarkdown = resolveHelpMarkdown(normalizedText, this.#t);
|
|
1985
2334
|
if (helpMarkdown) {
|
|
1986
2335
|
this.#logger.info(`received local help command ${normalizedText} (session=${clientSessionId})`);
|
|
1987
2336
|
await this.#client?.sendText(chatId, helpMarkdown, messageId);
|
|
2337
|
+
await this.#client?.stopTyping(chatId);
|
|
1988
2338
|
return;
|
|
1989
2339
|
}
|
|
1990
2340
|
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
@@ -1993,9 +2343,6 @@ var FeishuIMAdapter = class _FeishuIMAdapter {
|
|
|
1993
2343
|
await this.#onOutput(commandEvent);
|
|
1994
2344
|
return;
|
|
1995
2345
|
}
|
|
1996
|
-
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
1997
|
-
this.#resetProgressState(clientSessionId);
|
|
1998
|
-
await this.#client?.startTyping(chatId, messageId);
|
|
1999
2346
|
this.#logger.info(`received user message (session=${clientSessionId}): ${normalizedText}`);
|
|
2000
2347
|
await this.#onOutput({
|
|
2001
2348
|
type: "user.message",
|
|
@@ -2041,6 +2388,13 @@ var FeishuIMAdapter = class _FeishuIMAdapter {
|
|
|
2041
2388
|
try {
|
|
2042
2389
|
const target = parseFeishuSessionId(event.clientSessionId);
|
|
2043
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
|
+
}
|
|
2044
2398
|
await this.#handleProgressEvent(target.chatId, event);
|
|
2045
2399
|
continue;
|
|
2046
2400
|
}
|
|
@@ -2718,14 +3072,14 @@ var WecomIMAdapter = class {
|
|
|
2718
3072
|
await this.#client?.sendText(chatId, helpMarkdown, messageId);
|
|
2719
3073
|
return;
|
|
2720
3074
|
}
|
|
3075
|
+
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
3076
|
+
this.#resetProgressState(clientSessionId);
|
|
3077
|
+
await this.#announceStart(chatId, messageId, clientSessionId);
|
|
2721
3078
|
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
2722
3079
|
if (commandEvent) {
|
|
2723
3080
|
await this.#onOutput(commandEvent);
|
|
2724
3081
|
return;
|
|
2725
3082
|
}
|
|
2726
|
-
this.#lastInboundMessageIdBySession.set(clientSessionId, messageId);
|
|
2727
|
-
this.#resetProgressState(clientSessionId);
|
|
2728
|
-
await this.#announceStart(chatId, messageId, clientSessionId);
|
|
2729
3083
|
await this.#onOutput({
|
|
2730
3084
|
type: "user.message",
|
|
2731
3085
|
clientSessionId,
|
|
@@ -2770,6 +3124,13 @@ var WecomIMAdapter = class {
|
|
|
2770
3124
|
try {
|
|
2771
3125
|
const target = parseWecomSessionId(event.clientSessionId);
|
|
2772
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
|
+
}
|
|
2773
3134
|
await this.#handleProgressEvent(target.chatId, event);
|
|
2774
3135
|
continue;
|
|
2775
3136
|
}
|
|
@@ -3243,19 +3604,22 @@ var WeixinIMAdapter = class {
|
|
|
3243
3604
|
return;
|
|
3244
3605
|
}
|
|
3245
3606
|
const normalizedText = text2.trim();
|
|
3607
|
+
this.#resetProgressState(clientSessionId);
|
|
3608
|
+
await this.#client?.sendTyping(chatId);
|
|
3609
|
+
this.#startTypingHeartbeat(clientSessionId, chatId);
|
|
3246
3610
|
const helpMarkdown = resolveHelpMarkdown(normalizedText, this.#t);
|
|
3247
3611
|
if (helpMarkdown) {
|
|
3248
3612
|
await this.#client?.sendText(chatId, helpMarkdown);
|
|
3613
|
+
this.#stopProgressTimer(clientSessionId);
|
|
3614
|
+
this.#stopTypingHeartbeat(clientSessionId);
|
|
3615
|
+
await this.#client?.stopTyping(chatId);
|
|
3249
3616
|
return;
|
|
3250
3617
|
}
|
|
3251
|
-
this.#resetProgressState(clientSessionId);
|
|
3252
3618
|
const commandEvent = parseSlashCommand(normalizedText, clientSessionId);
|
|
3253
3619
|
if (commandEvent) {
|
|
3254
3620
|
await this.#onOutput(commandEvent);
|
|
3255
3621
|
return;
|
|
3256
3622
|
}
|
|
3257
|
-
await this.#client?.sendTyping(chatId);
|
|
3258
|
-
this.#startTypingHeartbeat(clientSessionId, chatId);
|
|
3259
3623
|
await this.#onOutput({
|
|
3260
3624
|
type: "user.message",
|
|
3261
3625
|
clientSessionId,
|
|
@@ -3311,6 +3675,14 @@ var WeixinIMAdapter = class {
|
|
|
3311
3675
|
continue;
|
|
3312
3676
|
}
|
|
3313
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
|
+
}
|
|
3314
3686
|
this.#recordProgressEvent(event);
|
|
3315
3687
|
continue;
|
|
3316
3688
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,16 @@ type ClientOutputEvent = {
|
|
|
11
11
|
} | {
|
|
12
12
|
type: "command.session.stop";
|
|
13
13
|
clientSessionId: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: "command.session.status";
|
|
16
|
+
clientSessionId: string;
|
|
17
|
+
} | {
|
|
18
|
+
type: "command.session.model.list";
|
|
19
|
+
clientSessionId: string;
|
|
20
|
+
} | {
|
|
21
|
+
type: "command.session.model.set";
|
|
22
|
+
clientSessionId: string;
|
|
23
|
+
target: string;
|
|
14
24
|
};
|
|
15
25
|
type AgentInputEvent = {
|
|
16
26
|
type: "user.message";
|
|
@@ -24,6 +34,22 @@ interface OutboundAttachment {
|
|
|
24
34
|
fileName?: string;
|
|
25
35
|
caption?: string;
|
|
26
36
|
}
|
|
37
|
+
interface AgentSessionStatus {
|
|
38
|
+
sessionId: string;
|
|
39
|
+
provider?: string;
|
|
40
|
+
modelId?: string;
|
|
41
|
+
thinkingLevel?: string;
|
|
42
|
+
context?: {
|
|
43
|
+
tokens: number | null;
|
|
44
|
+
contextWindow: number | null;
|
|
45
|
+
percent: number | null;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
interface AgentAvailableModel {
|
|
49
|
+
provider: string;
|
|
50
|
+
modelId: string;
|
|
51
|
+
isCurrent: boolean;
|
|
52
|
+
}
|
|
27
53
|
type ToolProgressPayload = {
|
|
28
54
|
toolName: string;
|
|
29
55
|
toolCallId?: string;
|
|
@@ -38,6 +64,20 @@ type AgentOutputPayload = {
|
|
|
38
64
|
} | {
|
|
39
65
|
type: "assistant.thinking";
|
|
40
66
|
text?: string;
|
|
67
|
+
} | {
|
|
68
|
+
type: "agent.status.info";
|
|
69
|
+
status: AgentSessionStatus;
|
|
70
|
+
} | {
|
|
71
|
+
type: "agent.model.list";
|
|
72
|
+
models: AgentAvailableModel[];
|
|
73
|
+
} | {
|
|
74
|
+
type: "agent.model.updated";
|
|
75
|
+
provider: string;
|
|
76
|
+
modelId: string;
|
|
77
|
+
} | {
|
|
78
|
+
type: "error";
|
|
79
|
+
kind: string;
|
|
80
|
+
detail?: string;
|
|
41
81
|
} | ({
|
|
42
82
|
type: "assistant.tool.running";
|
|
43
83
|
} & ToolProgressPayload) | ({
|
|
@@ -70,6 +110,12 @@ interface AgentAdapter {
|
|
|
70
110
|
start(onOutput: (event: AgentOutputEvent) => Promise<void> | void): Promise<void>;
|
|
71
111
|
stop(): Promise<void>;
|
|
72
112
|
abort?(): Promise<void>;
|
|
113
|
+
getStatus?(): Promise<AgentSessionStatus>;
|
|
114
|
+
getAvailableModels?(): Promise<AgentAvailableModel[]>;
|
|
115
|
+
setModel?(target: string): Promise<{
|
|
116
|
+
provider: string;
|
|
117
|
+
modelId: string;
|
|
118
|
+
}>;
|
|
73
119
|
input(event: AgentInputEvent): Promise<void>;
|
|
74
120
|
isBusy(): Promise<boolean>;
|
|
75
121
|
}
|
|
@@ -222,4 +268,4 @@ interface WeixinInboundMessage {
|
|
|
222
268
|
raw?: unknown;
|
|
223
269
|
}
|
|
224
270
|
|
|
225
|
-
export type { AgentAdapter, AgentConfig, AgentInputEvent, AgentModule, AgentOutputEvent, AppConfig, AppDefaults, ChannelCommonConfig, ChannelCommonContext, ChannelConfig, ChannelRunner, ClientConfig, ClientInputEvent, ClientModule, ClientOutputEvent, ConfigAdapter, ConfigCollectContext, ConfigInputOptions, ConfigSelectOption, FeishuClientConfig, FeishuInboundMessage, GatewayCoreOptions, IMAdapter, LegacyAgentInputEvent, LocaleCode, OutboundAttachment, PiCodingAgentConfig, RunChannelOptions, SessionBindingStore, WecomClientConfig, WecomInboundMessage, WeixinClientConfig, WeixinInboundMessage };
|
|
271
|
+
export type { AgentAdapter, AgentAvailableModel, AgentConfig, AgentInputEvent, AgentModule, AgentOutputEvent, AgentSessionStatus, AppConfig, AppDefaults, ChannelCommonConfig, ChannelCommonContext, ChannelConfig, ChannelRunner, ClientConfig, ClientInputEvent, ClientModule, ClientOutputEvent, ConfigAdapter, ConfigCollectContext, ConfigInputOptions, ConfigSelectOption, FeishuClientConfig, FeishuInboundMessage, GatewayCoreOptions, IMAdapter, LegacyAgentInputEvent, LocaleCode, OutboundAttachment, PiCodingAgentConfig, RunChannelOptions, SessionBindingStore, WecomClientConfig, WecomInboundMessage, WeixinClientConfig, WeixinInboundMessage };
|