@keyou007/agent-bot 0.1.15-alpha.0 → 0.1.15-alpha.1
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/CHANGELOG.md +10 -0
- package/README.md +23 -20
- package/README.zh.md +23 -20
- package/config.example.yaml +1 -0
- package/dist/cli/FeishuAppConfiguration.js +42 -3
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/feishu/CardRenderer.js +51 -8
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +17 -5
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/feishu/FeishuMarkdown.js +58 -6
- package/dist/feishu/FeishuMarkdown.js.map +1 -1
- package/dist/feishu/FeishuMessageClient.js +33 -4
- package/dist/feishu/FeishuMessageClient.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +7 -6
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/feishu/FinalResponseBranding.js +15 -0
- package/dist/feishu/FinalResponseBranding.js.map +1 -0
- package/dist/logging/DailyLogStream.js +73 -0
- package/dist/logging/DailyLogStream.js.map +1 -0
- package/dist/logging/logger.js +2 -1
- package/dist/logging/logger.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +192 -36
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +126 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/migrations.js +11 -0
- package/dist/state/migrations.js.map +1 -1
- package/dist/supervision/SupervisorDiagnostics.js +37 -21
- package/dist/supervision/SupervisorDiagnostics.js.map +1 -1
- package/dist/supervisor.js +11 -4
- package/dist/supervisor.js.map +1 -1
- package/dist/utils/executeShellCommand.js +83 -16
- package/dist/utils/executeShellCommand.js.map +1 -1
- package/docs/technical-reference.md +9 -8
- package/docs/technical-reference.zh.md +9 -8
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/cli/FeishuAppConfiguration.ts +76 -3
- package/src/cli.ts +6 -1
- package/src/feishu/CardRenderer.ts +75 -9
- package/src/feishu/FeishuConnector.ts +28 -6
- package/src/feishu/FeishuMarkdown.ts +64 -5
- package/src/feishu/FeishuMessageClient.ts +41 -12
- package/src/feishu/FeishuTurnPresenter.ts +9 -6
- package/src/feishu/FinalResponseBranding.ts +18 -0
- package/src/logging/DailyLogStream.ts +88 -0
- package/src/logging/logger.ts +2 -1
- package/src/proxy/ProxySessionController.ts +208 -35
- package/src/state/StateStore.ts +180 -0
- package/src/state/migrations.ts +11 -0
- package/src/supervision/SupervisorDiagnostics.ts +41 -25
- package/src/supervisor.ts +10 -4
- package/src/utils/executeShellCommand.ts +103 -15
|
@@ -6,12 +6,13 @@ import { resolveUserPath } from "../config/paths.js";
|
|
|
6
6
|
import { CommandRouter } from "../commands/CommandRouter.js";
|
|
7
7
|
import { baseChatContextKey, isThreadContextKey } from "../feishu/contextKey.js";
|
|
8
8
|
import { CardRenderer, } from "../feishu/CardRenderer.js";
|
|
9
|
+
import { CardUpdateScheduler } from "../feishu/CardUpdateScheduler.js";
|
|
9
10
|
import { generateGroupAvatarPng, resolveGroupAvatarProjectName } from "../feishu/GroupAvatarGenerator.js";
|
|
10
11
|
import { normalizeFeishuPostText } from "../feishu/InboundText.js";
|
|
11
12
|
import { allowsFeishuUser } from "../feishu/ownerAccess.js";
|
|
12
13
|
import { buildTurnGraphRows } from "../presentation/turnGraph.js";
|
|
13
14
|
import { createId } from "../utils/id.js";
|
|
14
|
-
import {
|
|
15
|
+
import { truncateMiddle, truncateText } from "../utils/markdown.js";
|
|
15
16
|
import { normalizeTaskTitle } from "../utils/taskTitle.js";
|
|
16
17
|
import { executeShellCommand, } from "../utils/executeShellCommand.js";
|
|
17
18
|
const MESSAGE_RECEIVED_REACTION = "OnIt";
|
|
@@ -24,6 +25,7 @@ const RESET_HISTORY_PAGE_SIZE = 10;
|
|
|
24
25
|
const MAX_LLM_TURN_RETRIES = 3;
|
|
25
26
|
const RECOVERY_ACTIVITY_WINDOW_MS = 5 * 60 * 1_000;
|
|
26
27
|
const RECOVERY_HEARTBEAT_INTERVAL_MS = 60 * 1_000;
|
|
28
|
+
const SHELL_COMMAND_ELAPSED_UPDATE_INTERVAL_MS = 3_000;
|
|
27
29
|
const FORWARD_ATTACHMENT_WINDOW_MS = 800;
|
|
28
30
|
const DEFAULT_MERGED_FORWARD_INSTRUCTION = "请参考以下内容回复用户";
|
|
29
31
|
const DEFAULT_REFERENCED_MESSAGE_INSTRUCTION = "请参考引用消息回复用户";
|
|
@@ -417,7 +419,7 @@ export class ProxySessionController {
|
|
|
417
419
|
if (isQueueIndependentCommand(command)) {
|
|
418
420
|
await this.outbound.withReplyTarget(message.contextKey, replyTarget, async () => {
|
|
419
421
|
try {
|
|
420
|
-
if (command.type
|
|
422
|
+
if (command.type === "prompt")
|
|
421
423
|
await this.ensureThreadFork(message);
|
|
422
424
|
await this.execute(message.contextKey, command, message.messageId, replyTarget, localImagePaths, message.userId, message);
|
|
423
425
|
if (!isPromptCommand(command))
|
|
@@ -433,7 +435,7 @@ export class ProxySessionController {
|
|
|
433
435
|
const previous = this.messageQueues.get(message.contextKey) ?? Promise.resolve();
|
|
434
436
|
const next = previous.catch(() => undefined).then(() => this.outbound.withReplyTarget(message.contextKey, replyTarget, async () => {
|
|
435
437
|
try {
|
|
436
|
-
if (command.type
|
|
438
|
+
if (command.type === "prompt")
|
|
437
439
|
await this.ensureThreadFork(message);
|
|
438
440
|
await this.execute(message.contextKey, command, message.messageId, replyTarget, localImagePaths, message.userId, message);
|
|
439
441
|
if (!isPromptCommand(command))
|
|
@@ -1095,8 +1097,7 @@ export class ProxySessionController {
|
|
|
1095
1097
|
if (!session.remoteSessionId || !this.isCodexSession(session)) {
|
|
1096
1098
|
throw new Error("The task is not an App Server task that can be reset.");
|
|
1097
1099
|
}
|
|
1098
|
-
const
|
|
1099
|
-
const rows = this.store.listCompletedTurnGraph(session.localSessionId, contextKey);
|
|
1100
|
+
const rows = this.store.listTaskTurnGraph(session.localSessionId);
|
|
1100
1101
|
const graph = buildTurnGraphRows(rows.map((turn) => ({ turnId: turn.turnId, parentTurnId: turn.parentTurnId })));
|
|
1101
1102
|
return {
|
|
1102
1103
|
session,
|
|
@@ -1219,6 +1220,9 @@ export class ProxySessionController {
|
|
|
1219
1220
|
}
|
|
1220
1221
|
async execute(contextKey, command, messageId, replyTarget, localImagePaths, userId, incomingMessage) {
|
|
1221
1222
|
const context = this.store.getOrCreateUserContext(contextKey, this.config.defaults.agent);
|
|
1223
|
+
if (!this.currentSession(contextKey) && isThreadContextKey(contextKey) && commandRequiresCurrentSession(command)) {
|
|
1224
|
+
throw new Error(unboundThreadTaskMessage());
|
|
1225
|
+
}
|
|
1222
1226
|
switch (command.type) {
|
|
1223
1227
|
case "shell":
|
|
1224
1228
|
await this.runShellCommand(contextKey, command.command);
|
|
@@ -2501,8 +2505,13 @@ export class ProxySessionController {
|
|
|
2501
2505
|
if (existing)
|
|
2502
2506
|
return { record, runtime, session: existing };
|
|
2503
2507
|
const pending = this.sessionLoads.get(record.localSessionId);
|
|
2504
|
-
if (pending)
|
|
2505
|
-
|
|
2508
|
+
if (pending) {
|
|
2509
|
+
const loaded = await pending;
|
|
2510
|
+
return {
|
|
2511
|
+
...loaded,
|
|
2512
|
+
record: { ...loaded.record, contextKey: record.contextKey },
|
|
2513
|
+
};
|
|
2514
|
+
}
|
|
2506
2515
|
this.outbound.registerSession(record.localSessionId, record.contextKey, record.title, record.cwd, this.agentLabel(record.agentName));
|
|
2507
2516
|
const loading = (async () => {
|
|
2508
2517
|
if (record.lastTurnId) {
|
|
@@ -2566,7 +2575,11 @@ export class ProxySessionController {
|
|
|
2566
2575
|
}
|
|
2567
2576
|
this.persistRuntimeSession(record, session, session.activeTurnId ? "running" : "ready");
|
|
2568
2577
|
const saved = this.store.getSession(record.localSessionId) ?? record;
|
|
2569
|
-
return {
|
|
2578
|
+
return {
|
|
2579
|
+
record: { ...saved, contextKey: record.contextKey },
|
|
2580
|
+
runtime,
|
|
2581
|
+
session,
|
|
2582
|
+
};
|
|
2570
2583
|
})();
|
|
2571
2584
|
this.sessionLoads.set(record.localSessionId, loading);
|
|
2572
2585
|
try {
|
|
@@ -2653,6 +2666,7 @@ export class ProxySessionController {
|
|
|
2653
2666
|
presentationError = error;
|
|
2654
2667
|
}
|
|
2655
2668
|
if (event.type === "turn_completed" || event.type === "turn_cancelled" || event.type === "turn_failed") {
|
|
2669
|
+
await this.refreshGoalCards(event.sessionId);
|
|
2656
2670
|
const terminalStatus = event.type === "turn_completed"
|
|
2657
2671
|
? "completed"
|
|
2658
2672
|
: event.type === "turn_cancelled"
|
|
@@ -2994,8 +3008,10 @@ export class ProxySessionController {
|
|
|
2994
3008
|
if (!current.remoteSessionId || !this.isCodexSession(current)) {
|
|
2995
3009
|
throw new Error("当前任务不是可 Reset 的 App Server 任务。");
|
|
2996
3010
|
}
|
|
2997
|
-
const
|
|
2998
|
-
|
|
3011
|
+
const historyTurn = this.store.listTaskTurnGraph(current.localSessionId)
|
|
3012
|
+
.find((turn) => turn.turnId === turnId);
|
|
3013
|
+
const snapshot = turnViewSnapshot(historyTurn?.snapshot);
|
|
3014
|
+
if (!snapshot || snapshot.status !== "completed") {
|
|
2999
3015
|
throw new Error("只能将当前任务 Reset 到已成功完成的轮次。");
|
|
3000
3016
|
}
|
|
3001
3017
|
if (current.lastTurnId === turnId && current.lastTurnStatus === "completed") {
|
|
@@ -3015,7 +3031,7 @@ export class ProxySessionController {
|
|
|
3015
3031
|
throw new Error("当前任务仍在执行。请等待任务完成或先停止任务,再点击 Reset。");
|
|
3016
3032
|
}
|
|
3017
3033
|
const origin = this.store.getTurnRuntimeOrigin(turnId);
|
|
3018
|
-
if (origin &&
|
|
3034
|
+
if (origin && origin.agentName !== current.agentName) {
|
|
3019
3035
|
throw new Error("这张思考卡片与当前任务不匹配,已拒绝 Reset。");
|
|
3020
3036
|
}
|
|
3021
3037
|
const sourceRemoteSessionId = origin?.remoteSessionId ?? current.remoteSessionId;
|
|
@@ -3383,6 +3399,21 @@ export class ProxySessionController {
|
|
|
3383
3399
|
await this.sendGoalCard(contextKey, goal, command.action === "edit" ? "Goal 已更新。" : "Goal 已启动,Agent 会持续执行直到完成、暂停或遇到阻塞。", record);
|
|
3384
3400
|
}
|
|
3385
3401
|
async sendGoalCard(contextKey, goal, notice, record = this.currentSession(contextKey)) {
|
|
3402
|
+
const card = this.renderGoalCard(goal, notice, record);
|
|
3403
|
+
if (!record) {
|
|
3404
|
+
await this.outbound.sendInteractiveCard(contextKey, card);
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
const existing = this.store.getGoalCardDelivery(record.localSessionId, contextKey);
|
|
3408
|
+
if (existing) {
|
|
3409
|
+
await this.outbound.updateInteractiveCard(contextKey, existing.messageId, card);
|
|
3410
|
+
return;
|
|
3411
|
+
}
|
|
3412
|
+
const messageId = await this.outbound.sendInteractiveCard(contextKey, card);
|
|
3413
|
+
if (messageId)
|
|
3414
|
+
this.store.saveGoalCardDelivery(record.localSessionId, contextKey, messageId);
|
|
3415
|
+
}
|
|
3416
|
+
renderGoalCard(goal, notice, record) {
|
|
3386
3417
|
const sections = [];
|
|
3387
3418
|
if (notice)
|
|
3388
3419
|
sections.push({ lines: [cardText(notice)] });
|
|
@@ -3405,23 +3436,133 @@ export class ProxySessionController {
|
|
|
3405
3436
|
"**/goal edit <新目标>** 修改 **/goal clear** 清除",
|
|
3406
3437
|
],
|
|
3407
3438
|
});
|
|
3408
|
-
|
|
3439
|
+
const title = goal ? `Agent Goal · ${goalStatusLabel(goal.status)}` : "Agent Goal";
|
|
3440
|
+
return this.cardRenderer.renderSectionsCard(title, sections);
|
|
3441
|
+
}
|
|
3442
|
+
async refreshGoalCards(localSessionId) {
|
|
3443
|
+
const deliveries = this.store.listGoalCardDeliveries(localSessionId);
|
|
3444
|
+
if (deliveries.length === 0)
|
|
3445
|
+
return;
|
|
3446
|
+
const record = this.store.getSession(localSessionId);
|
|
3447
|
+
if (!record)
|
|
3448
|
+
return;
|
|
3449
|
+
const runtime = this.runtimes.forAgent(record.agentName);
|
|
3450
|
+
if (runtime.kind !== "codex" || !runtime.getGoal)
|
|
3451
|
+
return;
|
|
3452
|
+
try {
|
|
3453
|
+
const loaded = runtime.getSession(localSessionId) ?? (await this.loadSession(record)).session;
|
|
3454
|
+
const goal = await runtime.getGoal(loaded.localSessionId);
|
|
3455
|
+
const card = this.renderGoalCard(goal, undefined, record);
|
|
3456
|
+
await Promise.all(deliveries.map(async (delivery) => {
|
|
3457
|
+
try {
|
|
3458
|
+
await this.outbound.updateInteractiveCard(delivery.contextKey, delivery.messageId, card);
|
|
3459
|
+
}
|
|
3460
|
+
catch (error) {
|
|
3461
|
+
this.logger.warn({ error, localSessionId, contextKey: delivery.contextKey, messageId: delivery.messageId }, "Failed to refresh an Agent Goal card.");
|
|
3462
|
+
}
|
|
3463
|
+
}));
|
|
3464
|
+
}
|
|
3465
|
+
catch (error) {
|
|
3466
|
+
this.logger.warn({ error, localSessionId }, "Failed to read the latest Agent Goal status.");
|
|
3467
|
+
}
|
|
3409
3468
|
}
|
|
3410
3469
|
async runShellCommand(contextKey, command) {
|
|
3411
3470
|
const cwd = this.currentSession(contextKey)?.cwd ?? this.config.defaults.cwd;
|
|
3412
|
-
const
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3471
|
+
const startedAt = Date.now();
|
|
3472
|
+
let latestOutput = {
|
|
3473
|
+
stdout: "",
|
|
3474
|
+
stderr: "",
|
|
3475
|
+
outputTruncated: false,
|
|
3476
|
+
};
|
|
3477
|
+
let runningState = {
|
|
3478
|
+
command,
|
|
3479
|
+
cwd,
|
|
3480
|
+
...latestOutput,
|
|
3481
|
+
status: "running",
|
|
3482
|
+
elapsedMs: 0,
|
|
3483
|
+
};
|
|
3484
|
+
const cardMessageId = await this.outbound.sendInteractiveCard(contextKey, this.cardRenderer.renderShellCommandCard(runningState));
|
|
3485
|
+
const scheduler = cardMessageId
|
|
3486
|
+
? new CardUpdateScheduler({
|
|
3487
|
+
render: (state) => this.cardRenderer.renderShellCommandCard(state),
|
|
3488
|
+
write: (card) => this.outbound.updateInteractiveCard(contextKey, cardMessageId, card),
|
|
3489
|
+
onError: (error) => this.logger.warn({ error, contextKey, messageId: cardMessageId }, "Failed to update a shell command card."),
|
|
3490
|
+
})
|
|
3491
|
+
: undefined;
|
|
3492
|
+
scheduler?.seed(runningState);
|
|
3493
|
+
const elapsedTimer = scheduler ? setInterval(() => {
|
|
3494
|
+
runningState = { ...runningState, elapsedMs: Date.now() - startedAt };
|
|
3495
|
+
scheduler.update(runningState);
|
|
3496
|
+
}, SHELL_COMMAND_ELAPSED_UPDATE_INTERVAL_MS) : undefined;
|
|
3497
|
+
elapsedTimer?.unref?.();
|
|
3498
|
+
const updateRunningOutput = (snapshot) => {
|
|
3499
|
+
latestOutput = snapshot;
|
|
3500
|
+
runningState = {
|
|
3501
|
+
...runningState,
|
|
3502
|
+
...snapshot,
|
|
3503
|
+
elapsedMs: Date.now() - startedAt,
|
|
3504
|
+
};
|
|
3505
|
+
scheduler?.update(runningState);
|
|
3506
|
+
};
|
|
3507
|
+
let result;
|
|
3508
|
+
try {
|
|
3509
|
+
result = await this.shellCommandExecutor(command, cwd, { onOutput: updateRunningOutput });
|
|
3510
|
+
}
|
|
3511
|
+
catch (error) {
|
|
3512
|
+
if (elapsedTimer)
|
|
3513
|
+
clearInterval(elapsedTimer);
|
|
3514
|
+
const failedCard = this.cardRenderer.renderShellCommandCard({
|
|
3515
|
+
command,
|
|
3516
|
+
cwd,
|
|
3517
|
+
...latestOutput,
|
|
3518
|
+
stderr: [latestOutput.stderr.trimEnd(), runtimeErrorMessage(error)].filter(Boolean).join("\n"),
|
|
3519
|
+
status: "failed",
|
|
3520
|
+
elapsedMs: Date.now() - startedAt,
|
|
3521
|
+
});
|
|
3522
|
+
if (scheduler) {
|
|
3523
|
+
try {
|
|
3524
|
+
await scheduler.flush({
|
|
3525
|
+
...runningState,
|
|
3526
|
+
stderr: [latestOutput.stderr.trimEnd(), runtimeErrorMessage(error)].filter(Boolean).join("\n"),
|
|
3527
|
+
status: "failed",
|
|
3528
|
+
elapsedMs: Date.now() - startedAt,
|
|
3529
|
+
});
|
|
3530
|
+
}
|
|
3531
|
+
catch (cardError) {
|
|
3532
|
+
this.logger.warn({ error: cardError, contextKey, messageId: cardMessageId }, "Failed to finalize a shell command card.");
|
|
3533
|
+
}
|
|
3534
|
+
finally {
|
|
3535
|
+
scheduler.dispose();
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
else {
|
|
3539
|
+
await this.outbound.sendInteractiveCard(contextKey, failedCard);
|
|
3540
|
+
}
|
|
3541
|
+
throw error;
|
|
3542
|
+
}
|
|
3543
|
+
if (elapsedTimer)
|
|
3544
|
+
clearInterval(elapsedTimer);
|
|
3545
|
+
const finalState = {
|
|
3546
|
+
command,
|
|
3547
|
+
cwd,
|
|
3548
|
+
stdout: result.stdout,
|
|
3549
|
+
stderr: result.stderr,
|
|
3550
|
+
status: result.timedOut ? "timed_out" : result.exitCode === 0 ? "completed" : "failed",
|
|
3551
|
+
exitCode: result.exitCode,
|
|
3552
|
+
elapsedMs: Date.now() - startedAt,
|
|
3553
|
+
outputTruncated: result.outputTruncated,
|
|
3554
|
+
};
|
|
3555
|
+
if (scheduler) {
|
|
3556
|
+
try {
|
|
3557
|
+
await scheduler.flush(finalState);
|
|
3558
|
+
}
|
|
3559
|
+
finally {
|
|
3560
|
+
scheduler.dispose();
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
else {
|
|
3564
|
+
await this.outbound.sendInteractiveCard(contextKey, this.cardRenderer.renderShellCommandCard(finalState));
|
|
3565
|
+
}
|
|
3425
3566
|
}
|
|
3426
3567
|
async setTitle(contextKey, title) {
|
|
3427
3568
|
const normalizedTitle = normalizeTaskTitle(title);
|
|
@@ -3532,7 +3673,7 @@ export class ProxySessionController {
|
|
|
3532
3673
|
if (!current.remoteSessionId || !this.isCodexSession(current)) {
|
|
3533
3674
|
throw new Error("当前任务不是可 Reset 的 App Server 任务。");
|
|
3534
3675
|
}
|
|
3535
|
-
const allTurns = this.store.
|
|
3676
|
+
const allTurns = this.store.listTaskTurnGraph(current.localSessionId);
|
|
3536
3677
|
const graphRows = buildTurnGraphRows(allTurns.map((turn) => ({
|
|
3537
3678
|
turnId: turn.turnId,
|
|
3538
3679
|
parentTurnId: turn.parentTurnId,
|
|
@@ -3668,7 +3809,6 @@ export class ProxySessionController {
|
|
|
3668
3809
|
contextKey,
|
|
3669
3810
|
},
|
|
3670
3811
|
},
|
|
3671
|
-
actions: directoryCreationActions(entry.path, contextKey),
|
|
3672
3812
|
}
|
|
3673
3813
|
: {
|
|
3674
3814
|
name: entry.name,
|
|
@@ -3693,7 +3833,7 @@ export class ProxySessionController {
|
|
|
3693
3833
|
footerLines: [
|
|
3694
3834
|
`第 ${page + 1}/${totalPages} 页 · ${directoryCount} 个目录 · ${entries.length - directoryCount} 个文件`,
|
|
3695
3835
|
"",
|
|
3696
|
-
">
|
|
3836
|
+
"> 点击目录名称进入,点击文件名称发送到当前会话;卡片顶部的 **New** 和 **NewGroup** 使用当前目录。",
|
|
3697
3837
|
],
|
|
3698
3838
|
});
|
|
3699
3839
|
if (options.updateMessageId) {
|
|
@@ -3722,7 +3862,6 @@ export class ProxySessionController {
|
|
|
3722
3862
|
contextKey,
|
|
3723
3863
|
},
|
|
3724
3864
|
},
|
|
3725
|
-
actions: directoryCreationActions(drive.root, contextKey),
|
|
3726
3865
|
}));
|
|
3727
3866
|
const card = this.cardRenderer.renderDirectoryBrowserCard({
|
|
3728
3867
|
directory: "此电脑",
|
|
@@ -3732,7 +3871,7 @@ export class ProxySessionController {
|
|
|
3732
3871
|
footerLines: [
|
|
3733
3872
|
`第 ${page + 1}/${totalPages} 页 · ${drives.length} 个磁盘`,
|
|
3734
3873
|
"",
|
|
3735
|
-
">
|
|
3874
|
+
"> 点击磁盘名称进入。",
|
|
3736
3875
|
],
|
|
3737
3876
|
});
|
|
3738
3877
|
if (options.updateMessageId) {
|
|
@@ -4350,7 +4489,9 @@ export class ProxySessionController {
|
|
|
4350
4489
|
let activeTurnId;
|
|
4351
4490
|
let queued = 0;
|
|
4352
4491
|
if (!current) {
|
|
4353
|
-
taskLines.push(
|
|
4492
|
+
taskLines.push(isThreadContextKey(contextKey)
|
|
4493
|
+
? unboundThreadTaskMessage()
|
|
4494
|
+
: "无。直接发送消息即可创建一个未指定项目的 Agent 任务。");
|
|
4354
4495
|
}
|
|
4355
4496
|
else {
|
|
4356
4497
|
const agent = this.ensureAgent(current.agentName);
|
|
@@ -4581,8 +4722,11 @@ export class ProxySessionController {
|
|
|
4581
4722
|
}
|
|
4582
4723
|
requireCurrentSession(contextKey) {
|
|
4583
4724
|
const record = this.currentSession(contextKey);
|
|
4584
|
-
if (!record)
|
|
4585
|
-
throw new Error(
|
|
4725
|
+
if (!record) {
|
|
4726
|
+
throw new Error(isThreadContextKey(contextKey)
|
|
4727
|
+
? unboundThreadTaskMessage()
|
|
4728
|
+
: "当前没有任务,直接发送一条消息即可自动创建。");
|
|
4729
|
+
}
|
|
4586
4730
|
return record;
|
|
4587
4731
|
}
|
|
4588
4732
|
requireSession(contextKey, sessionId) {
|
|
@@ -5050,6 +5194,19 @@ function isQueueIndependentCommand(command) {
|
|
|
5050
5194
|
return true;
|
|
5051
5195
|
return false;
|
|
5052
5196
|
}
|
|
5197
|
+
function commandRequiresCurrentSession(command) {
|
|
5198
|
+
if (["dismiss", "stop", "title", "turns", "model", "provider", "thinking", "permissions", "nosteer"].includes(command.type)) {
|
|
5199
|
+
return true;
|
|
5200
|
+
}
|
|
5201
|
+
if (command.type === "archive" || command.type === "fork")
|
|
5202
|
+
return command.sessionId === undefined;
|
|
5203
|
+
if (command.type === "goal")
|
|
5204
|
+
return command.action !== "set";
|
|
5205
|
+
return false;
|
|
5206
|
+
}
|
|
5207
|
+
function unboundThreadTaskMessage() {
|
|
5208
|
+
return "当前话题尚未绑定任务。发送普通消息后,Agent Bot 会从可识别的原始轮次创建分支;没有原始轮次时会创建全新任务。也可以使用 /new 创建全新任务,或使用 /sessions 绑定现有任务。";
|
|
5209
|
+
}
|
|
5053
5210
|
function parseSessionPage(value) {
|
|
5054
5211
|
const parsed = typeof value === "string" || typeof value === "number" ? Number(value) : Number.NaN;
|
|
5055
5212
|
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : 0;
|
|
@@ -5348,14 +5505,13 @@ const FEISHU_GROUP_NAME_MAX_LENGTH = 60;
|
|
|
5348
5505
|
const GROUP_PROJECT_DIRECTORY_MAX_LENGTH = 15;
|
|
5349
5506
|
function formatGroupProjectDirectory(projectCwd) {
|
|
5350
5507
|
const value = abbreviateHomeDirectory(projectCwd);
|
|
5351
|
-
if (Array.from(value).length <= GROUP_PROJECT_DIRECTORY_MAX_LENGTH)
|
|
5352
|
-
return `[${value}]`;
|
|
5353
5508
|
const levels = value
|
|
5354
5509
|
.replace(/[\\/]+$/, "")
|
|
5355
5510
|
.split(/[\\/]+/)
|
|
5356
5511
|
.filter(Boolean)
|
|
5357
5512
|
.slice(-2);
|
|
5358
|
-
|
|
5513
|
+
const separator = value.includes("\\") ? path.win32.sep : path.posix.sep;
|
|
5514
|
+
return `[${fitTrailingPathLevels(levels, GROUP_PROJECT_DIRECTORY_MAX_LENGTH, separator)}]`;
|
|
5359
5515
|
}
|
|
5360
5516
|
function abbreviateHomeDirectory(value) {
|
|
5361
5517
|
const homeDirectory = os.homedir();
|