@keyou007/agent-bot 0.1.13-alpha.0 → 0.1.13-alpha.2
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/.env.example +2 -2
- package/CHANGELOG.md +15 -0
- package/README.md +107 -98
- package/README.zh.md +81 -72
- package/dist/acp/AcpProcessManager.js +8 -3
- package/dist/acp/AcpProcessManager.js.map +1 -1
- package/dist/acp/AcpSessionManager.js +15 -0
- package/dist/acp/AcpSessionManager.js.map +1 -1
- package/dist/cli/ServerStarter.js +17 -2
- package/dist/cli/ServerStarter.js.map +1 -1
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/help.js +16 -4
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/serverStatus.js +27 -0
- package/dist/cli/serverStatus.js.map +1 -1
- package/dist/cli/taskListOutput.js +72 -0
- package/dist/cli/taskListOutput.js.map +1 -0
- package/dist/cli.js +88 -54
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexProcessManager.js +34 -4
- package/dist/codex/CodexProcessManager.js.map +1 -1
- package/dist/codex/CodexRuntime.js +3 -0
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +5 -0
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/config/FeishuUserOpenIdStore.js +76 -0
- package/dist/config/FeishuUserOpenIdStore.js.map +1 -0
- package/dist/config/loadConfig.js +3 -0
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/feishu/CardRenderer.js +123 -22
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +16 -5
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/index.js +125 -26
- package/dist/index.js.map +1 -1
- package/dist/presentation/turnGraph.js +36 -0
- package/dist/presentation/turnGraph.js.map +1 -0
- package/dist/proxy/ProxySessionController.js +425 -104
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/runtime/AcpRuntimeAdapter.js +3 -0
- package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
- package/dist/runtime/agentEnvironment.js +24 -4
- package/dist/runtime/agentEnvironment.js.map +1 -1
- package/dist/runtime/createAgentRuntimeRegistry.js +7 -3
- package/dist/runtime/createAgentRuntimeRegistry.js.map +1 -1
- package/dist/startup/StartupNotifier.js +16 -7
- package/dist/startup/StartupNotifier.js.map +1 -1
- package/dist/startup/startFeishu.js +2 -2
- package/dist/startup/startFeishu.js.map +1 -1
- package/dist/state/StateStore.js +172 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/migrations.js +21 -0
- package/dist/state/migrations.js.map +1 -1
- package/dist/supervision/SafeRestartNotifier.js +46 -3
- package/dist/supervision/SafeRestartNotifier.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +65 -6
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/dist/supervision/replacementSupervisor.js +27 -2
- package/dist/supervision/replacementSupervisor.js.map +1 -1
- package/dist/supervisor.js +27 -2
- package/dist/supervisor.js.map +1 -1
- package/docs/technical-reference.md +20 -7
- package/docs/technical-reference.zh.md +20 -7
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +63 -26
- package/src/acp/AcpProcessManager.ts +16 -3
- package/src/acp/AcpSessionManager.ts +14 -0
- package/src/cli/ServerStarter.ts +25 -2
- package/src/cli/controlProtocol.ts +6 -1
- package/src/cli/help.ts +16 -4
- package/src/cli/serverStatus.ts +28 -0
- package/src/cli/taskListOutput.ts +91 -0
- package/src/cli.ts +123 -51
- package/src/codex/CodexProcessManager.ts +38 -3
- package/src/codex/CodexRuntime.ts +6 -0
- package/src/commands/CommandRouter.ts +4 -0
- package/src/commands/commandTypes.ts +1 -0
- package/src/config/FeishuUserOpenIdStore.ts +81 -0
- package/src/config/loadConfig.ts +3 -0
- package/src/feishu/CardRenderer.ts +169 -22
- package/src/feishu/FeishuConnector.ts +13 -4
- package/src/index.ts +195 -23
- package/src/presentation/turnGraph.ts +48 -0
- package/src/proxy/ProxySessionController.ts +507 -112
- package/src/runtime/AcpRuntimeAdapter.ts +5 -0
- package/src/runtime/agentEnvironment.ts +41 -3
- package/src/runtime/createAgentRuntimeRegistry.ts +18 -3
- package/src/runtime/types.ts +5 -0
- package/src/startup/StartupNotifier.ts +23 -7
- package/src/startup/startFeishu.ts +3 -2
- package/src/state/StateStore.ts +224 -0
- package/src/state/migrations.ts +21 -0
- package/src/supervision/SafeRestartNotifier.ts +53 -4
- package/src/supervision/SafeRestartScheduler.ts +78 -7
- package/src/supervision/replacementSupervisor.ts +27 -1
- package/src/supervisor.ts +33 -2
|
@@ -6,6 +6,7 @@ import { CommandRouter } from "../commands/CommandRouter.js";
|
|
|
6
6
|
import { baseChatContextKey, isThreadContextKey } from "../feishu/contextKey.js";
|
|
7
7
|
import { CardRenderer, } from "../feishu/CardRenderer.js";
|
|
8
8
|
import { generateGroupAvatarPng, resolveGroupAvatarProjectName } from "../feishu/GroupAvatarGenerator.js";
|
|
9
|
+
import { buildTurnGraphRows } from "../presentation/turnGraph.js";
|
|
9
10
|
import { createId } from "../utils/id.js";
|
|
10
11
|
import { asInlineCode, codeBlock, truncateMiddle, truncateText } from "../utils/markdown.js";
|
|
11
12
|
import { normalizeTaskTitle } from "../utils/taskTitle.js";
|
|
@@ -15,7 +16,111 @@ const MESSAGE_COMPLETED_REACTION = "DONE";
|
|
|
15
16
|
const MESSAGE_FAILED_REACTION = "ERROR";
|
|
16
17
|
const MESSAGE_CANCELLED_REACTION = "CrossMark";
|
|
17
18
|
const SESSION_PAGE_SIZE = 5;
|
|
19
|
+
const RESET_HISTORY_PAGE_SIZE = 10;
|
|
18
20
|
const REMOTE_SESSION_REFERENCE_PREFIX = "agent-runtime:";
|
|
21
|
+
const HELP_COMMAND_SECTIONS = [
|
|
22
|
+
{
|
|
23
|
+
title: "任务管理",
|
|
24
|
+
commands: [
|
|
25
|
+
{
|
|
26
|
+
command: "/new",
|
|
27
|
+
usage: "[title] [--dir <cwd> | --nodir]",
|
|
28
|
+
description: "使用默认 Agent 创建任务;--nodir 创建 Projectless 任务",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
command: "/newgroup",
|
|
32
|
+
usage: "[title] [--dir <cwd> | --nodir]",
|
|
33
|
+
description: "创建飞书群和新任务",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
command: "/forkgroup",
|
|
37
|
+
usage: "[title]",
|
|
38
|
+
description: "从当前任务最近完成轮次创建分支群",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
command: "/fork",
|
|
42
|
+
usage: "[序号或任务 ID]",
|
|
43
|
+
description: "从当前或指定任务最近完成轮次创建分支",
|
|
44
|
+
},
|
|
45
|
+
{ command: "/turns", description: "浏览历史轮次,并可 Reset 对话上下文" },
|
|
46
|
+
{
|
|
47
|
+
command: "/title",
|
|
48
|
+
usage: "<新标题>",
|
|
49
|
+
description: "修改当前任务标题",
|
|
50
|
+
requiresArgument: true,
|
|
51
|
+
},
|
|
52
|
+
{ command: "/sessions", usage: "[关键词]", description: "查找本机任务" },
|
|
53
|
+
{
|
|
54
|
+
command: "/switch",
|
|
55
|
+
usage: "[序号或任务 ID]",
|
|
56
|
+
description: "切换任务;不填参数切回上一个任务",
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
title: "执行设置",
|
|
62
|
+
commands: [
|
|
63
|
+
{
|
|
64
|
+
command: "!",
|
|
65
|
+
usage: "<命令>",
|
|
66
|
+
description: "在当前任务目录执行本地命令",
|
|
67
|
+
requiresArgument: true,
|
|
68
|
+
},
|
|
69
|
+
{ command: "/stop", description: "停止当前执行" },
|
|
70
|
+
{
|
|
71
|
+
command: "/queue",
|
|
72
|
+
usage: "<prompt>",
|
|
73
|
+
description: "将 Prompt 排队为后续轮次",
|
|
74
|
+
requiresArgument: true,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
command: "/nosteer",
|
|
78
|
+
usage: "<prompt>",
|
|
79
|
+
description: "与 /queue 相同",
|
|
80
|
+
requiresArgument: true,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
command: "/goal",
|
|
84
|
+
usage: "<目标或操作>",
|
|
85
|
+
description: "查看或创建长任务 Goal;支持 pause、resume、edit、clear",
|
|
86
|
+
requiresArgument: true,
|
|
87
|
+
},
|
|
88
|
+
{ command: "/provider", description: "选择 AI 服务提供商" },
|
|
89
|
+
{ command: "/model", description: "选择当前任务使用的模型" },
|
|
90
|
+
{ command: "/thinking", description: "设置模型的思考强度" },
|
|
91
|
+
{ command: "/permissions", description: "设置执行工具前是否需要确认" },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
title: "Agent",
|
|
96
|
+
commands: [
|
|
97
|
+
{
|
|
98
|
+
command: "/agent",
|
|
99
|
+
usage: "[name]",
|
|
100
|
+
description: "选择新任务使用的默认 Agent",
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
title: "系统",
|
|
106
|
+
commands: [
|
|
107
|
+
{
|
|
108
|
+
command: "/status",
|
|
109
|
+
usage: "[序号或任务 ID]",
|
|
110
|
+
description: "查看当前或指定任务状态",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
command: "/restart",
|
|
114
|
+
usage: "[--force]",
|
|
115
|
+
description: "默认安全重启;--force 立即重启",
|
|
116
|
+
},
|
|
117
|
+
{ command: "/help", description: "显示本帮助" },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
const HELP_DEFAULT_COMMANDS = new Set(HELP_COMMAND_SECTIONS.flatMap((section) => section.commands)
|
|
122
|
+
.filter((command) => !command.requiresArgument)
|
|
123
|
+
.map((command) => command.command));
|
|
19
124
|
export class ProxySessionController {
|
|
20
125
|
config;
|
|
21
126
|
store;
|
|
@@ -31,6 +136,7 @@ export class ProxySessionController {
|
|
|
31
136
|
queuedPromptStarts = new Map();
|
|
32
137
|
queuedPromptCards = new Map();
|
|
33
138
|
queuedPromptCardWrites = new Map();
|
|
139
|
+
sessionResets = new Map();
|
|
34
140
|
lastSessionListings = new Map();
|
|
35
141
|
threadInitializations = new Map();
|
|
36
142
|
unsubscribe = [];
|
|
@@ -75,8 +181,18 @@ export class ProxySessionController {
|
|
|
75
181
|
catch (error) {
|
|
76
182
|
this.logger.warn({ error, messageId: message.messageId, contextKey: message.contextKey }, "Failed to acknowledge the incoming Feishu message with a reaction.");
|
|
77
183
|
}
|
|
184
|
+
if (message.chatType === "p2p" && message.userId?.startsWith("ou_")) {
|
|
185
|
+
try {
|
|
186
|
+
await this.lifecycle?.rememberFeishuUserOpenId?.(message.userId);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
this.logger.warn({ error, messageId: message.messageId }, "Failed to persist the first private-chat Lark user Open ID.");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
78
192
|
if (message.chatId && message.chatType) {
|
|
79
|
-
|
|
193
|
+
const chatContextKey = baseChatContextKey(message.contextKey);
|
|
194
|
+
this.store.recordChatContext(chatContextKey, message.chatType);
|
|
195
|
+
this.store.markChatActive(chatContextKey);
|
|
80
196
|
}
|
|
81
197
|
const replyTarget = message.replyInThread
|
|
82
198
|
? { messageId: message.messageId, replyInThread: true }
|
|
@@ -87,9 +203,6 @@ export class ProxySessionController {
|
|
|
87
203
|
text: message.text,
|
|
88
204
|
...(imageCount > 0 ? { imageCount } : {}),
|
|
89
205
|
});
|
|
90
|
-
if (imageCount > 0 && message.chatId && message.chatType) {
|
|
91
|
-
this.store.markChatActive(baseChatContextKey(message.contextKey));
|
|
92
|
-
}
|
|
93
206
|
let localImagePaths;
|
|
94
207
|
if (imageCount > 0) {
|
|
95
208
|
try {
|
|
@@ -112,12 +225,6 @@ export class ProxySessionController {
|
|
|
112
225
|
await this.outbound.withReplyTarget(message.contextKey, replyTarget, () => this.sendError(message.contextKey, error));
|
|
113
226
|
return;
|
|
114
227
|
}
|
|
115
|
-
if (message.chatId
|
|
116
|
-
&& message.chatType
|
|
117
|
-
&& (command.type === "shell"
|
|
118
|
-
|| (command.type === "prompt" && !message.text.trimStart().startsWith("/")))) {
|
|
119
|
-
this.store.markChatActive(baseChatContextKey(message.contextKey));
|
|
120
|
-
}
|
|
121
228
|
// Operational and read-only commands must remain available even if a prompt operation is slow.
|
|
122
229
|
if (isQueueIndependentCommand(command)) {
|
|
123
230
|
await this.outbound.withReplyTarget(message.contextKey, replyTarget, async () => {
|
|
@@ -165,7 +272,10 @@ export class ProxySessionController {
|
|
|
165
272
|
await this.outbound.withReplyTarget(contextKey, replyTarget, async () => {
|
|
166
273
|
try {
|
|
167
274
|
const kind = String(scopedAction.value.action ?? "");
|
|
168
|
-
if (kind === "
|
|
275
|
+
if (kind === "help_command") {
|
|
276
|
+
await this.executeHelpCommandAction(scopedAction, contextKey, replyTarget);
|
|
277
|
+
}
|
|
278
|
+
else if (kind === "turn_details") {
|
|
169
279
|
await this.outbound.showDetails(contextKey, String(scopedAction.value.turnId ?? ""));
|
|
170
280
|
}
|
|
171
281
|
else if (kind === "activity_history") {
|
|
@@ -177,6 +287,24 @@ export class ProxySessionController {
|
|
|
177
287
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
178
288
|
await this.cancelSession(this.requireSession(contextKey, sessionId));
|
|
179
289
|
}
|
|
290
|
+
else if (kind === "turn_reset") {
|
|
291
|
+
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
292
|
+
await this.resetCurrentSessionToTurn(contextKey, sessionId, String(scopedAction.value.turnId ?? ""));
|
|
293
|
+
if (scopedAction.value.cardView === "reset_history") {
|
|
294
|
+
await this.openResetHistory(contextKey, {
|
|
295
|
+
expectedSessionId: sessionId,
|
|
296
|
+
updateMessageId: requiredCardMessageId(scopedAction.messageId),
|
|
297
|
+
page: resetHistoryPageValue(scopedAction.value.page),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else if (kind === "turn_reset_page") {
|
|
302
|
+
await this.openResetHistory(contextKey, {
|
|
303
|
+
expectedSessionId: String(scopedAction.value.sessionId ?? ""),
|
|
304
|
+
updateMessageId: requiredCardMessageId(scopedAction.messageId),
|
|
305
|
+
page: resetHistoryPageValue(scopedAction.value.page),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
180
308
|
else if (kind === "queued_prompt_cancel") {
|
|
181
309
|
await this.cancelQueuedPrompt(scopedAction);
|
|
182
310
|
}
|
|
@@ -193,8 +321,8 @@ export class ProxySessionController {
|
|
|
193
321
|
await this.outbound.sendText(contextKey, "该安全重启计划已失效,请查看最新状态卡片。");
|
|
194
322
|
}
|
|
195
323
|
}
|
|
196
|
-
else if (kind === "
|
|
197
|
-
await this.refreshSessionsCardFromAction(scopedAction
|
|
324
|
+
else if (kind === "session_page") {
|
|
325
|
+
await this.refreshSessionsCardFromAction(scopedAction);
|
|
198
326
|
}
|
|
199
327
|
else if (kind === "session_switch") {
|
|
200
328
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
@@ -202,12 +330,12 @@ export class ProxySessionController {
|
|
|
202
330
|
if (scopedAction.value.cardView === "status")
|
|
203
331
|
await this.refreshStatusCardFromAction(scopedAction);
|
|
204
332
|
else
|
|
205
|
-
await this.refreshSessionsCardFromAction(scopedAction);
|
|
333
|
+
await this.refreshSessionsCardFromAction(scopedAction, { page: 0 });
|
|
206
334
|
}
|
|
207
335
|
else if (kind === "session_fork") {
|
|
208
336
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
209
337
|
await this.forkSessionReference(contextKey, sessionId);
|
|
210
|
-
await this.refreshSessionsCardFromAction(scopedAction);
|
|
338
|
+
await this.refreshSessionsCardFromAction(scopedAction, { page: 0 });
|
|
211
339
|
}
|
|
212
340
|
else if (kind === "session_fork_group") {
|
|
213
341
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
@@ -217,7 +345,7 @@ export class ProxySessionController {
|
|
|
217
345
|
else if (kind === "session_new") {
|
|
218
346
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
219
347
|
await this.createProjectSessionFromReference(contextKey, sessionId);
|
|
220
|
-
await this.refreshSessionsCardFromAction(scopedAction);
|
|
348
|
+
await this.refreshSessionsCardFromAction(scopedAction, { page: 0 });
|
|
221
349
|
}
|
|
222
350
|
else if (kind === "session_new_group") {
|
|
223
351
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
@@ -230,7 +358,7 @@ export class ProxySessionController {
|
|
|
230
358
|
if (scopedAction.value.cardView === "status")
|
|
231
359
|
await this.refreshStatusCardFromAction(scopedAction, sessionId);
|
|
232
360
|
else
|
|
233
|
-
await this.refreshSessionsCardFromAction(scopedAction, sessionId);
|
|
361
|
+
await this.refreshSessionsCardFromAction(scopedAction, { forceSwitchTaskId: sessionId });
|
|
234
362
|
}
|
|
235
363
|
else if (kind === "session_status") {
|
|
236
364
|
await this.status(contextKey, String(scopedAction.value.sessionId ?? ""));
|
|
@@ -355,6 +483,7 @@ export class ProxySessionController {
|
|
|
355
483
|
this.lastSessionListings.clear();
|
|
356
484
|
this.queuedPromptCards.clear();
|
|
357
485
|
this.queuedPromptCardWrites.clear();
|
|
486
|
+
this.sessionResets.clear();
|
|
358
487
|
}
|
|
359
488
|
async controlStopTask(localSessionId) {
|
|
360
489
|
const record = this.store.getSession(localSessionId);
|
|
@@ -588,7 +717,10 @@ export class ProxySessionController {
|
|
|
588
717
|
case "restart":
|
|
589
718
|
if (!this.lifecycle)
|
|
590
719
|
throw new Error("当前运行方式不支持自动重启。");
|
|
591
|
-
await this.lifecycle.restart(contextKey, command.force === true);
|
|
720
|
+
await this.lifecycle.restart(contextKey, command.force === true, replyTarget);
|
|
721
|
+
return;
|
|
722
|
+
case "turns":
|
|
723
|
+
await this.openResetHistory(contextKey);
|
|
592
724
|
return;
|
|
593
725
|
case "model":
|
|
594
726
|
await this.openExecutionSettings(contextKey, "model");
|
|
@@ -1532,6 +1664,15 @@ export class ProxySessionController {
|
|
|
1532
1664
|
});
|
|
1533
1665
|
}
|
|
1534
1666
|
async handleRuntimeEvent(event) {
|
|
1667
|
+
if ("turnId" in event) {
|
|
1668
|
+
const source = this.store.getSession(event.sessionId);
|
|
1669
|
+
if (source?.remoteSessionId && this.isCodexSession(source)) {
|
|
1670
|
+
this.store.saveTurnRuntimeOrigin(event.turnId, source.localSessionId, source.agentName, source.remoteSessionId);
|
|
1671
|
+
}
|
|
1672
|
+
if (event.type === "turn_started") {
|
|
1673
|
+
this.store.saveTurnParent(event.turnId, event.sessionId, this.completedParentTurnId(source));
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1535
1676
|
if (event.type === "session_metadata_updated") {
|
|
1536
1677
|
this.store.updateRuntimeSession(event.sessionId, { title: event.title });
|
|
1537
1678
|
this.outbound.updateSessionTitle(event.sessionId, event.title);
|
|
@@ -1623,6 +1764,14 @@ export class ProxySessionController {
|
|
|
1623
1764
|
}
|
|
1624
1765
|
}
|
|
1625
1766
|
}
|
|
1767
|
+
completedParentTurnId(source) {
|
|
1768
|
+
if (!source?.lastTurnId)
|
|
1769
|
+
return undefined;
|
|
1770
|
+
if (source.lastTurnStatus === "completed")
|
|
1771
|
+
return source.lastTurnId;
|
|
1772
|
+
const snapshot = turnViewSnapshot(this.store.getTurnSnapshot(source.lastTurnId));
|
|
1773
|
+
return snapshot?.status === "completed" ? source.lastTurnId : undefined;
|
|
1774
|
+
}
|
|
1626
1775
|
async finalizeStandaloneMessageReaction(messageId, status) {
|
|
1627
1776
|
const reaction = this.store.claimMessageReaction(messageId);
|
|
1628
1777
|
if (reaction)
|
|
@@ -1718,6 +1867,88 @@ export class ProxySessionController {
|
|
|
1718
1867
|
}
|
|
1719
1868
|
await loaded.runtime.cancelTurn(record.localSessionId, turnId);
|
|
1720
1869
|
}
|
|
1870
|
+
async resetCurrentSessionToTurn(contextKey, sessionId, turnId) {
|
|
1871
|
+
if (!sessionId || !turnId)
|
|
1872
|
+
throw new Error("无效的 Reset 请求。请使用最新的思考卡片重试。");
|
|
1873
|
+
const previous = this.sessionResets.get(sessionId) ?? Promise.resolve();
|
|
1874
|
+
const reset = previous.catch(() => undefined).then(() => this.performCurrentSessionReset(contextKey, sessionId, turnId));
|
|
1875
|
+
this.sessionResets.set(sessionId, reset);
|
|
1876
|
+
try {
|
|
1877
|
+
await reset;
|
|
1878
|
+
}
|
|
1879
|
+
finally {
|
|
1880
|
+
if (this.sessionResets.get(sessionId) === reset)
|
|
1881
|
+
this.sessionResets.delete(sessionId);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
async performCurrentSessionReset(contextKey, sessionId, turnId) {
|
|
1885
|
+
const current = this.requireCurrentSession(contextKey);
|
|
1886
|
+
if (current.localSessionId !== sessionId) {
|
|
1887
|
+
throw new Error("这张思考卡片不属于当前任务。请先切换回对应任务,再点击 Reset。");
|
|
1888
|
+
}
|
|
1889
|
+
if (!current.remoteSessionId || !this.isCodexSession(current)) {
|
|
1890
|
+
throw new Error("当前任务不是可 Reset 的 App Server 任务。");
|
|
1891
|
+
}
|
|
1892
|
+
const snapshot = turnViewSnapshot(this.store.getTurnSnapshot(turnId));
|
|
1893
|
+
if (!snapshot || snapshot.sessionId !== sessionId || snapshot.status !== "completed") {
|
|
1894
|
+
throw new Error("只能将当前任务 Reset 到已成功完成的轮次。");
|
|
1895
|
+
}
|
|
1896
|
+
if (current.lastTurnId === turnId && current.lastTurnStatus === "completed") {
|
|
1897
|
+
await this.outbound.sendText(contextKey, "当前任务已经处于本轮完成后的对话状态,无需 Reset。");
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
const runtime = this.runtimes.forAgent(current.agentName);
|
|
1901
|
+
if (runtime.kind !== "codex" || !runtime.forkSession) {
|
|
1902
|
+
throw new Error("当前 App Server Agent 不支持 Reset。");
|
|
1903
|
+
}
|
|
1904
|
+
const loaded = await this.loadSession(current);
|
|
1905
|
+
const activity = await this.assertSessionTurnOwnership(current, runtime);
|
|
1906
|
+
const active = activity?.active
|
|
1907
|
+
?? Boolean(loaded.session.activeTurnId || current.status === "running" || current.lastTurnStatus === "running");
|
|
1908
|
+
if (active) {
|
|
1909
|
+
throw new Error("当前任务仍在执行。请等待任务完成或先停止任务,再点击 Reset。");
|
|
1910
|
+
}
|
|
1911
|
+
const origin = this.store.getTurnRuntimeOrigin(turnId);
|
|
1912
|
+
if (origin && (origin.localSessionId !== sessionId || origin.agentName !== current.agentName)) {
|
|
1913
|
+
throw new Error("这张思考卡片与当前任务不匹配,已拒绝 Reset。");
|
|
1914
|
+
}
|
|
1915
|
+
const sourceRemoteSessionId = origin?.remoteSessionId ?? current.remoteSessionId;
|
|
1916
|
+
const previousRemoteSessionId = current.remoteSessionId;
|
|
1917
|
+
const forked = await runtime.forkSession({
|
|
1918
|
+
localSessionId: current.localSessionId,
|
|
1919
|
+
remoteSessionId: sourceRemoteSessionId,
|
|
1920
|
+
lastTurnId: turnId,
|
|
1921
|
+
agentName: current.agentName,
|
|
1922
|
+
cwd: current.cwd,
|
|
1923
|
+
title: current.title,
|
|
1924
|
+
modelProvider: current.modelProvider,
|
|
1925
|
+
model: current.model,
|
|
1926
|
+
reasoningEffort: current.reasoningEffort,
|
|
1927
|
+
permissionMode: current.permissionMode ?? "auto",
|
|
1928
|
+
});
|
|
1929
|
+
this.persistRuntimeSession(current, forked, "ready");
|
|
1930
|
+
this.store.updateRuntimeSession(current.localSessionId, {
|
|
1931
|
+
lastTurnId: turnId,
|
|
1932
|
+
lastTurnStatus: "completed",
|
|
1933
|
+
});
|
|
1934
|
+
this.outbound.registerSession(current.localSessionId, contextKey, forked.title ?? current.title, current.cwd, this.agentLabel(current.agentName));
|
|
1935
|
+
this.store.audit(contextKey, "session_reset_to_turn", {
|
|
1936
|
+
localSessionId: current.localSessionId,
|
|
1937
|
+
previousRemoteSessionId,
|
|
1938
|
+
sourceRemoteSessionId,
|
|
1939
|
+
resetTurnId: turnId,
|
|
1940
|
+
forkedRemoteSessionId: forked.remoteSessionId,
|
|
1941
|
+
});
|
|
1942
|
+
const targetSummary = truncateText((snapshot.prompt ?? snapshot.taskTitle ?? "未记录对话内容").replace(/\s+/g, " ").trim(), 100) || "未记录对话内容";
|
|
1943
|
+
const targetTime = snapshot.completedAt ?? snapshot.startedAt;
|
|
1944
|
+
await this.outbound.sendText(contextKey, [
|
|
1945
|
+
"已将当前任务重置到:",
|
|
1946
|
+
targetSummary,
|
|
1947
|
+
`完成时间:${targetTime === undefined ? "未知" : formatResetTurnTime(targetTime)}`,
|
|
1948
|
+
`Turn ID:${turnId}`,
|
|
1949
|
+
"后续对话将从该轮完成后的状态继续;本地文件没有回退。",
|
|
1950
|
+
].join("\n"));
|
|
1951
|
+
}
|
|
1721
1952
|
async resolveApproval(action) {
|
|
1722
1953
|
const sessionId = String(action.value.sessionId ?? "");
|
|
1723
1954
|
const requestId = String(action.value.requestId ?? "");
|
|
@@ -2185,10 +2416,92 @@ export class ProxySessionController {
|
|
|
2185
2416
|
}
|
|
2186
2417
|
await this.outbound.sendText(contextKey, mode === "auto" ? "已切换为自动执行模式。" : "已切换为执行前确认模式。");
|
|
2187
2418
|
}
|
|
2419
|
+
async openResetHistory(contextKey, options = {}) {
|
|
2420
|
+
const current = this.requireCurrentSession(contextKey);
|
|
2421
|
+
if (options.expectedSessionId && current.localSessionId !== options.expectedSessionId) {
|
|
2422
|
+
throw new Error("这张历史轮次卡片不属于当前任务。请先切换回对应任务,再重新发送 /turns。");
|
|
2423
|
+
}
|
|
2424
|
+
if (!current.remoteSessionId || !this.isCodexSession(current)) {
|
|
2425
|
+
throw new Error("当前任务不是可 Reset 的 App Server 任务。");
|
|
2426
|
+
}
|
|
2427
|
+
const allTurns = this.store.listCompletedTurnGraph(current.localSessionId, contextKey);
|
|
2428
|
+
const graphRows = buildTurnGraphRows(allTurns.map((turn) => ({
|
|
2429
|
+
turnId: turn.turnId,
|
|
2430
|
+
parentTurnId: turn.parentTurnId,
|
|
2431
|
+
})));
|
|
2432
|
+
const total = allTurns.length;
|
|
2433
|
+
const totalPages = Math.max(1, Math.ceil(total / RESET_HISTORY_PAGE_SIZE));
|
|
2434
|
+
const page = Math.max(0, Math.min(Math.trunc(options.page ?? 0), totalPages - 1));
|
|
2435
|
+
const offset = page * RESET_HISTORY_PAGE_SIZE;
|
|
2436
|
+
const turns = allTurns.slice(offset, offset + RESET_HISTORY_PAGE_SIZE);
|
|
2437
|
+
const entries = turns.flatMap((turn, index) => {
|
|
2438
|
+
const snapshot = turnViewSnapshot(turn.snapshot);
|
|
2439
|
+
if (!snapshot)
|
|
2440
|
+
return [];
|
|
2441
|
+
const graph = graphRows[offset + index];
|
|
2442
|
+
const summary = truncateText((snapshot.prompt ?? snapshot.taskTitle ?? "未记录对话内容").replace(/\s+/g, " ").trim(), 100);
|
|
2443
|
+
const isCurrent = current.lastTurnId === turn.turnId && current.lastTurnStatus === "completed";
|
|
2444
|
+
return [{
|
|
2445
|
+
sequence: graph.sequence,
|
|
2446
|
+
graphNodeLine: graph.nodeLine,
|
|
2447
|
+
graphConnectorLine: graph.connectorLine,
|
|
2448
|
+
lines: [
|
|
2449
|
+
`**${cardText(summary || "未记录对话内容")}**`,
|
|
2450
|
+
`${formatResetTurnTime(snapshot.completedAt ?? snapshot.startedAt)} · ${cardCode(turn.turnId)}`,
|
|
2451
|
+
],
|
|
2452
|
+
current: isCurrent,
|
|
2453
|
+
actions: isCurrent ? undefined : [{
|
|
2454
|
+
text: "Reset",
|
|
2455
|
+
value: {
|
|
2456
|
+
action: "turn_reset",
|
|
2457
|
+
cardView: "reset_history",
|
|
2458
|
+
sessionId: current.localSessionId,
|
|
2459
|
+
turnId: turn.turnId,
|
|
2460
|
+
contextKey,
|
|
2461
|
+
page: String(page),
|
|
2462
|
+
},
|
|
2463
|
+
}],
|
|
2464
|
+
}];
|
|
2465
|
+
});
|
|
2466
|
+
const pageActions = [
|
|
2467
|
+
...(page > 0 ? [{
|
|
2468
|
+
text: "Previous",
|
|
2469
|
+
value: {
|
|
2470
|
+
action: "turn_reset_page",
|
|
2471
|
+
sessionId: current.localSessionId,
|
|
2472
|
+
contextKey,
|
|
2473
|
+
page: String(page - 1),
|
|
2474
|
+
},
|
|
2475
|
+
}] : []),
|
|
2476
|
+
...(page < totalPages - 1 ? [{
|
|
2477
|
+
text: "Next",
|
|
2478
|
+
value: {
|
|
2479
|
+
action: "turn_reset_page",
|
|
2480
|
+
sessionId: current.localSessionId,
|
|
2481
|
+
contextKey,
|
|
2482
|
+
page: String(page + 1),
|
|
2483
|
+
},
|
|
2484
|
+
}] : []),
|
|
2485
|
+
];
|
|
2486
|
+
const card = this.cardRenderer.renderResetHistoryCard({
|
|
2487
|
+
entries,
|
|
2488
|
+
footerLines: [
|
|
2489
|
+
`第 ${page + 1}/${totalPages} 页 · 共 ${total} 个已完成 turn`,
|
|
2490
|
+
],
|
|
2491
|
+
pageActions,
|
|
2492
|
+
});
|
|
2493
|
+
if (options.updateMessageId) {
|
|
2494
|
+
await this.outbound.updateInteractiveCard(contextKey, options.updateMessageId, card);
|
|
2495
|
+
}
|
|
2496
|
+
else {
|
|
2497
|
+
await this.outbound.sendInteractiveCard(contextKey, card);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2188
2500
|
async listSessions(contextKey, searchTerm, options = {}) {
|
|
2189
2501
|
const context = this.store.getOrCreateUserContext(contextKey, this.config.defaults.agent);
|
|
2190
2502
|
const normalizedSearch = searchTerm?.trim().toLowerCase();
|
|
2191
|
-
const
|
|
2503
|
+
const requestedPage = Math.max(0, Math.trunc(options.page ?? 0));
|
|
2504
|
+
const fetchLimit = (requestedPage + 1) * SESSION_PAGE_SIZE;
|
|
2192
2505
|
const localSessions = this.store.listSessions(contextKey).filter((session) => session.status !== "closed" && this.isCodexSession(session));
|
|
2193
2506
|
const remoteSessions = [];
|
|
2194
2507
|
const remoteErrors = [];
|
|
@@ -2197,9 +2510,9 @@ export class ProxySessionController {
|
|
|
2197
2510
|
if (!runtime.listRemoteSessions)
|
|
2198
2511
|
return;
|
|
2199
2512
|
try {
|
|
2200
|
-
const
|
|
2201
|
-
remoteSessions.push(...
|
|
2202
|
-
remoteHasMore ||= Boolean(
|
|
2513
|
+
const result = await runtime.listRemoteSessions({ searchTerm, limit: fetchLimit });
|
|
2514
|
+
remoteSessions.push(...result.sessions.map((session) => ({ agentName, session })));
|
|
2515
|
+
remoteHasMore ||= Boolean(result.nextCursor);
|
|
2203
2516
|
}
|
|
2204
2517
|
catch (error) {
|
|
2205
2518
|
this.logger.warn({ error, contextKey, agentName }, "Failed to list App Server sessions for an Agent.");
|
|
@@ -2217,9 +2530,13 @@ export class ProxySessionController {
|
|
|
2217
2530
|
|| Number(right.active) - Number(left.active)
|
|
2218
2531
|
|| right.updatedAt - left.updatedAt);
|
|
2219
2532
|
const activeCount = entries.filter((entry) => entry.active).length;
|
|
2220
|
-
const
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2533
|
+
const lastKnownPage = Math.max(0, Math.ceil(entries.length / SESSION_PAGE_SIZE) - 1);
|
|
2534
|
+
const page = remoteHasMore ? requestedPage : Math.min(requestedPage, lastKnownPage);
|
|
2535
|
+
const offset = page * SESSION_PAGE_SIZE;
|
|
2536
|
+
const visibleEntries = entries.slice(offset, offset + SESSION_PAGE_SIZE);
|
|
2537
|
+
const hasPrevious = page > 0;
|
|
2538
|
+
const hasNext = remoteHasMore || entries.length > offset + SESSION_PAGE_SIZE;
|
|
2539
|
+
this.lastSessionListings.set(contextKey, entries.map((entry) => entry.reference));
|
|
2223
2540
|
const cardEntries = visibleEntries.map((entry, index) => {
|
|
2224
2541
|
const marker = entry.current ? "✅" : entry.active ? "🟢 **活跃**" : "•";
|
|
2225
2542
|
const showStop = entry.status === "外部执行中"
|
|
@@ -2232,7 +2549,7 @@ export class ProxySessionController {
|
|
|
2232
2549
|
action: showStop ? "session_stop" : "session_switch",
|
|
2233
2550
|
sessionId: entry.reference,
|
|
2234
2551
|
...(searchTerm ? { searchTerm } : {}),
|
|
2235
|
-
|
|
2552
|
+
page: String(page),
|
|
2236
2553
|
contextKey,
|
|
2237
2554
|
},
|
|
2238
2555
|
}];
|
|
@@ -2242,7 +2559,7 @@ export class ProxySessionController {
|
|
|
2242
2559
|
action: "session_new",
|
|
2243
2560
|
sessionId: entry.reference,
|
|
2244
2561
|
...(searchTerm ? { searchTerm } : {}),
|
|
2245
|
-
|
|
2562
|
+
page: String(page),
|
|
2246
2563
|
contextKey,
|
|
2247
2564
|
},
|
|
2248
2565
|
});
|
|
@@ -2252,7 +2569,7 @@ export class ProxySessionController {
|
|
|
2252
2569
|
action: "session_new_group",
|
|
2253
2570
|
sessionId: entry.reference,
|
|
2254
2571
|
...(searchTerm ? { searchTerm } : {}),
|
|
2255
|
-
|
|
2572
|
+
page: String(page),
|
|
2256
2573
|
contextKey,
|
|
2257
2574
|
},
|
|
2258
2575
|
});
|
|
@@ -2262,7 +2579,7 @@ export class ProxySessionController {
|
|
|
2262
2579
|
action: "session_fork",
|
|
2263
2580
|
sessionId: entry.reference,
|
|
2264
2581
|
...(searchTerm ? { searchTerm } : {}),
|
|
2265
|
-
|
|
2582
|
+
page: String(page),
|
|
2266
2583
|
contextKey,
|
|
2267
2584
|
},
|
|
2268
2585
|
});
|
|
@@ -2272,7 +2589,7 @@ export class ProxySessionController {
|
|
|
2272
2589
|
action: "session_fork_group",
|
|
2273
2590
|
sessionId: entry.reference,
|
|
2274
2591
|
...(searchTerm ? { searchTerm } : {}),
|
|
2275
|
-
|
|
2592
|
+
page: String(page),
|
|
2276
2593
|
contextKey,
|
|
2277
2594
|
},
|
|
2278
2595
|
});
|
|
@@ -2286,7 +2603,7 @@ export class ProxySessionController {
|
|
|
2286
2603
|
});
|
|
2287
2604
|
return {
|
|
2288
2605
|
lines: [
|
|
2289
|
-
`**${index + 1}.** ${marker} **${cardText(entry.title)}** ${cardText(entry.id)}`,
|
|
2606
|
+
`**${offset + index + 1}.** ${marker} **${cardText(entry.title)}** ${cardText(entry.id)}`,
|
|
2290
2607
|
`${entry.status} · ${entry.updatedLabel} · ${cardText(entry.agentName)} · ${cardText(entry.cwd || "目录未知")}`,
|
|
2291
2608
|
],
|
|
2292
2609
|
actions,
|
|
@@ -2294,21 +2611,30 @@ export class ProxySessionController {
|
|
|
2294
2611
|
});
|
|
2295
2612
|
const card = this.cardRenderer.renderTaskListCard(searchTerm ? `App Server 任务:${searchTerm}` : "App Server 任务", activeCount > 0 ? `任务(${activeCount} 个活跃)` : "任务", cardEntries, [
|
|
2296
2613
|
...(remoteHint ? [remoteHint] : []),
|
|
2297
|
-
|
|
2298
|
-
"
|
|
2299
|
-
"
|
|
2300
|
-
"
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2614
|
+
`第 ${page + 1} 页 · 每页 ${SESSION_PAGE_SIZE} 个任务${hasNext ? "" : ` · 当前共 ${entries.length} 个任务`}`,
|
|
2615
|
+
"",
|
|
2616
|
+
"> **Switch** 切换任务;**Stop** 停止运行;**Status** 查看状态。",
|
|
2617
|
+
"> **New** / **Fork** 新建 / 分支任务;**NewGroup** / **ForkGroup** 新建 / 分支群。",
|
|
2618
|
+
], [
|
|
2619
|
+
...(hasPrevious ? [{
|
|
2620
|
+
text: "Previous",
|
|
2621
|
+
value: {
|
|
2622
|
+
action: "session_page",
|
|
2623
|
+
...(searchTerm ? { searchTerm } : {}),
|
|
2624
|
+
page: String(page - 1),
|
|
2625
|
+
contextKey,
|
|
2626
|
+
},
|
|
2627
|
+
}] : []),
|
|
2628
|
+
...(hasNext ? [{
|
|
2629
|
+
text: "Next",
|
|
2630
|
+
value: {
|
|
2631
|
+
action: "session_page",
|
|
2632
|
+
...(searchTerm ? { searchTerm } : {}),
|
|
2633
|
+
page: String(page + 1),
|
|
2634
|
+
contextKey,
|
|
2635
|
+
},
|
|
2636
|
+
}] : []),
|
|
2637
|
+
]);
|
|
2312
2638
|
if (options.updateMessageId) {
|
|
2313
2639
|
await this.outbound.updateInteractiveCard(contextKey, options.updateMessageId, card);
|
|
2314
2640
|
}
|
|
@@ -2316,17 +2642,16 @@ export class ProxySessionController {
|
|
|
2316
2642
|
await this.outbound.sendInteractiveCard(contextKey, card);
|
|
2317
2643
|
}
|
|
2318
2644
|
}
|
|
2319
|
-
async refreshSessionsCardFromAction(action,
|
|
2645
|
+
async refreshSessionsCardFromAction(action, options = {}) {
|
|
2320
2646
|
if (!action.messageId)
|
|
2321
2647
|
return;
|
|
2322
2648
|
const searchTerm = typeof action.value.searchTerm === "string" && action.value.searchTerm.trim()
|
|
2323
2649
|
? action.value.searchTerm
|
|
2324
2650
|
: undefined;
|
|
2325
|
-
const currentVisibleCount = parseSessionVisibleCount(action.value.visibleCount);
|
|
2326
2651
|
await this.listSessions(action.contextKey, searchTerm, {
|
|
2327
2652
|
updateMessageId: action.messageId,
|
|
2328
|
-
forceSwitchTaskId,
|
|
2329
|
-
|
|
2653
|
+
forceSwitchTaskId: options.forceSwitchTaskId,
|
|
2654
|
+
page: options.page ?? parseSessionPage(action.value.page),
|
|
2330
2655
|
});
|
|
2331
2656
|
}
|
|
2332
2657
|
async refreshStatusCardFromAction(action, forceSwitchTaskId) {
|
|
@@ -2702,58 +3027,45 @@ export class ProxySessionController {
|
|
|
2702
3027
|
else
|
|
2703
3028
|
await this.outbound.sendInteractiveCard(contextKey, card);
|
|
2704
3029
|
}
|
|
3030
|
+
async executeHelpCommandAction(action, contextKey, replyTarget) {
|
|
3031
|
+
const commandText = typeof action.value.command === "string"
|
|
3032
|
+
? action.value.command.trim()
|
|
3033
|
+
: "";
|
|
3034
|
+
if (!HELP_DEFAULT_COMMANDS.has(commandText)) {
|
|
3035
|
+
throw new Error("帮助卡片中的命令无效,请发送 /help 获取最新卡片。");
|
|
3036
|
+
}
|
|
3037
|
+
const command = this.router.parse(commandText);
|
|
3038
|
+
await this.execute(contextKey, command, undefined, replyTarget, undefined, action.userId);
|
|
3039
|
+
}
|
|
2705
3040
|
async help(contextKey) {
|
|
2706
|
-
const sections =
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
"**/goal [目标]** 查看或创建长任务 Goal;支持 pause、resume、edit、clear",
|
|
2735
|
-
"**/provider** 选择 AI 服务提供商;没有其他候选时仅显示当前 Provider",
|
|
2736
|
-
"**/model** 选择当前任务使用的模型",
|
|
2737
|
-
"**/thinking** 设置模型的思考强度",
|
|
2738
|
-
"**/permissions** 设置执行工具前是否需要确认",
|
|
2739
|
-
],
|
|
2740
|
-
},
|
|
2741
|
-
{
|
|
2742
|
-
title: "Agent",
|
|
2743
|
-
lines: [
|
|
2744
|
-
"**/agent [name]** 选择新任务使用的默认 Agent;仅配置一个时直接显示当前 Agent",
|
|
2745
|
-
],
|
|
2746
|
-
},
|
|
2747
|
-
{
|
|
2748
|
-
title: "系统",
|
|
2749
|
-
lines: [
|
|
2750
|
-
"**/status [序号或任务 ID]** 查看当前或指定任务的详细状态",
|
|
2751
|
-
"**/restart [--force]** 默认安全重启;--force 立即重启",
|
|
2752
|
-
"**/help** 显示本帮助",
|
|
2753
|
-
],
|
|
2754
|
-
},
|
|
2755
|
-
];
|
|
2756
|
-
await this.outbound.sendInteractiveCard(contextKey, this.cardRenderer.renderSectionsCard("Agent Bot 使用帮助", sections));
|
|
3041
|
+
const sections = HELP_COMMAND_SECTIONS.map((section) => ({
|
|
3042
|
+
title: section.title,
|
|
3043
|
+
commands: section.commands.map((command) => ({
|
|
3044
|
+
text: command.command,
|
|
3045
|
+
...(command.requiresArgument
|
|
3046
|
+
? {}
|
|
3047
|
+
: {
|
|
3048
|
+
action: {
|
|
3049
|
+
text: command.command,
|
|
3050
|
+
value: {
|
|
3051
|
+
action: "help_command",
|
|
3052
|
+
command: command.command,
|
|
3053
|
+
contextKey,
|
|
3054
|
+
},
|
|
3055
|
+
},
|
|
3056
|
+
}),
|
|
3057
|
+
...(command.usage ? { usage: command.usage } : {}),
|
|
3058
|
+
description: command.description,
|
|
3059
|
+
})),
|
|
3060
|
+
}));
|
|
3061
|
+
await this.outbound.sendInteractiveCard(contextKey, this.cardRenderer.renderHelpCard("Agent Bot 使用帮助", [
|
|
3062
|
+
"直接发送消息即可继续当前任务;执行中发送的新消息会追加到本次任务。",
|
|
3063
|
+
"点击命令按钮可执行默认形式;有必填参数的命令需要手动发送。",
|
|
3064
|
+
"> 命令前缀示例:/sess 等同于 /sessions。",
|
|
3065
|
+
"> 命令缩写示例:/fg 等同于 /forkgroup。",
|
|
3066
|
+
"前缀或缩写匹配多个命令时,需要输入更长的形式。",
|
|
3067
|
+
"**[参数]** 可选 **<参数>** 必填",
|
|
3068
|
+
], sections));
|
|
2757
3069
|
}
|
|
2758
3070
|
ensureAgent(agentName) {
|
|
2759
3071
|
const agent = this.config.agents[agentName];
|
|
@@ -3090,6 +3402,9 @@ function formatStatusTime(value) {
|
|
|
3090
3402
|
hourCycle: "h23",
|
|
3091
3403
|
}).format(date);
|
|
3092
3404
|
}
|
|
3405
|
+
function formatResetTurnTime(value) {
|
|
3406
|
+
return value === undefined ? "完成时间未知" : formatStatusTime(new Date(value).toISOString());
|
|
3407
|
+
}
|
|
3093
3408
|
function cardText(value) {
|
|
3094
3409
|
return value.replaceAll("&", "&").replaceAll("`", "`").replaceAll("<", "<").replaceAll(">", ">");
|
|
3095
3410
|
}
|
|
@@ -3106,10 +3421,16 @@ function executionSettingsTabValue(value) {
|
|
|
3106
3421
|
return value;
|
|
3107
3422
|
throw new Error("设置卡片的 tab 无效,请重新发送设置命令。");
|
|
3108
3423
|
}
|
|
3424
|
+
function resetHistoryPageValue(value) {
|
|
3425
|
+
const page = Number(value);
|
|
3426
|
+
if (!Number.isSafeInteger(page) || page < 0)
|
|
3427
|
+
throw new Error("历史轮次卡片页码无效,请重新发送 /turns。");
|
|
3428
|
+
return page;
|
|
3429
|
+
}
|
|
3109
3430
|
function requiredCardMessageId(value) {
|
|
3110
3431
|
if (value)
|
|
3111
3432
|
return value;
|
|
3112
|
-
throw new Error("
|
|
3433
|
+
throw new Error("无法更新卡片,请重新发送对应命令。");
|
|
3113
3434
|
}
|
|
3114
3435
|
function remoteSessionStatusLabel(status) {
|
|
3115
3436
|
const labels = {
|
|
@@ -3206,9 +3527,9 @@ function isQueueIndependentCommand(command) {
|
|
|
3206
3527
|
return true;
|
|
3207
3528
|
return false;
|
|
3208
3529
|
}
|
|
3209
|
-
function
|
|
3530
|
+
function parseSessionPage(value) {
|
|
3210
3531
|
const parsed = typeof value === "string" || typeof value === "number" ? Number(value) : Number.NaN;
|
|
3211
|
-
return Number.isSafeInteger(parsed) && parsed >=
|
|
3532
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : 0;
|
|
3212
3533
|
}
|
|
3213
3534
|
function isPromptCommand(command) {
|
|
3214
3535
|
return command.type === "prompt" || command.type === "nosteer";
|