@keyou007/agent-bot 0.1.15-alpha.3 → 0.1.15-alpha.5
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 +11 -0
- package/README.md +4 -1
- package/README.zh.md +5 -2
- package/dist/cli/VerificationOutput.js +1 -6
- package/dist/cli/VerificationOutput.js.map +1 -1
- package/dist/cli.js +7 -13
- package/dist/cli.js.map +1 -1
- package/dist/commands/CommandRouter.js +7 -0
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +76 -9
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FinalResponseBranding.js +1 -1
- package/dist/feishu/FinalResponseBranding.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +161 -83
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/shell/ShellCommandJobManager.js +264 -0
- package/dist/shell/ShellCommandJobManager.js.map +1 -0
- package/dist/shell/shellCommandRunner.js +157 -0
- package/dist/shell/shellCommandRunner.js.map +1 -0
- package/dist/utils/executeShellCommand.js +1 -1
- package/dist/utils/executeShellCommand.js.map +1 -1
- package/docs/technical-reference.md +5 -3
- package/docs/technical-reference.zh.md +5 -3
- package/npm-shrinkwrap.json +2 -19
- package/package.json +1 -3
- package/src/cli/VerificationOutput.ts +1 -12
- package/src/cli.ts +6 -22
- package/src/commands/CommandRouter.ts +6 -0
- package/src/commands/commandTypes.ts +1 -0
- package/src/feishu/CardRenderer.ts +91 -17
- package/src/feishu/FinalResponseBranding.ts +2 -1
- package/src/proxy/ProxySessionController.ts +196 -89
- package/src/shell/ShellCommandJobManager.ts +341 -0
- package/src/shell/shellCommandRunner.ts +163 -0
- package/src/utils/executeShellCommand.ts +1 -1
|
@@ -12,6 +12,7 @@ import { generateGroupAvatarPng, resolveGroupAvatarProjectName } from "../feishu
|
|
|
12
12
|
import { normalizeFeishuPostText } from "../feishu/InboundText.js";
|
|
13
13
|
import { allowsFeishuUser } from "../feishu/ownerAccess.js";
|
|
14
14
|
import { buildTurnGraphRows } from "../presentation/turnGraph.js";
|
|
15
|
+
import { isActiveShellCommandJob, ShellCommandJobManager, } from "../shell/ShellCommandJobManager.js";
|
|
15
16
|
import { createId } from "../utils/id.js";
|
|
16
17
|
import { truncateMiddle, truncateText } from "../utils/markdown.js";
|
|
17
18
|
import { normalizeTaskTitle } from "../utils/taskTitle.js";
|
|
@@ -27,7 +28,8 @@ const RESET_HISTORY_PAGE_SIZE = 10;
|
|
|
27
28
|
const MAX_LLM_TURN_RETRIES = 3;
|
|
28
29
|
const RECOVERY_ACTIVITY_WINDOW_MS = 5 * 60 * 1_000;
|
|
29
30
|
const RECOVERY_HEARTBEAT_INTERVAL_MS = 60 * 1_000;
|
|
30
|
-
const
|
|
31
|
+
const SHELL_COMMAND_JOB_POLL_INTERVAL_MS = 1_000;
|
|
32
|
+
const SHELL_COMMAND_JOB_RELAUNCH_DELAY_MS = 5_000;
|
|
31
33
|
const FORWARD_ATTACHMENT_WINDOW_MS = 800;
|
|
32
34
|
const DEFAULT_MERGED_FORWARD_INSTRUCTION = "请参考以下内容回复用户";
|
|
33
35
|
const DEFAULT_REFERENCED_MESSAGE_INSTRUCTION = "请参考引用消息回复用户";
|
|
@@ -61,6 +63,12 @@ const HELP_COMMAND_SECTIONS = [
|
|
|
61
63
|
usage: "[目录]",
|
|
62
64
|
description: "浏览、发送文件,并从选定目录创建任务或群",
|
|
63
65
|
},
|
|
66
|
+
{
|
|
67
|
+
command: "/file",
|
|
68
|
+
usage: "<文件路径>",
|
|
69
|
+
description: "将指定文件发送到当前飞书会话",
|
|
70
|
+
requiresArgument: true,
|
|
71
|
+
},
|
|
64
72
|
{
|
|
65
73
|
command: "/forkgroup",
|
|
66
74
|
usage: "[title]",
|
|
@@ -170,6 +178,7 @@ export class ProxySessionController {
|
|
|
170
178
|
lifecycle;
|
|
171
179
|
shellCommandExecutor;
|
|
172
180
|
windowsDriveLister;
|
|
181
|
+
shellCommandJobs;
|
|
173
182
|
router = new CommandRouter();
|
|
174
183
|
cardRenderer = new CardRenderer();
|
|
175
184
|
messageQueues = new Map();
|
|
@@ -186,11 +195,12 @@ export class ProxySessionController {
|
|
|
186
195
|
pendingMergedForwards = new Map();
|
|
187
196
|
pendingResourceForwards = new Map();
|
|
188
197
|
relatedReactionMessageIds = new Map();
|
|
198
|
+
shellCommandJobMonitors = new Map();
|
|
189
199
|
unsubscribe = [];
|
|
190
200
|
recoveryActivityHeartbeat;
|
|
191
201
|
startupRecovery;
|
|
192
202
|
queuedPromptsRestored = false;
|
|
193
|
-
constructor(config, store, runtimes, outbound, logger, lifecycle, shellCommandExecutor = executeShellCommand, windowsDriveLister = listWindowsDriveRoots) {
|
|
203
|
+
constructor(config, store, runtimes, outbound, logger, lifecycle, shellCommandExecutor = executeShellCommand, windowsDriveLister = listWindowsDriveRoots, shellCommandJobs = new ShellCommandJobManager(path.join(path.dirname(config.storage.sqlitePath), "command-jobs"))) {
|
|
194
204
|
this.config = config;
|
|
195
205
|
this.store = store;
|
|
196
206
|
this.runtimes = runtimes;
|
|
@@ -199,6 +209,7 @@ export class ProxySessionController {
|
|
|
199
209
|
this.lifecycle = lifecycle;
|
|
200
210
|
this.shellCommandExecutor = shellCommandExecutor;
|
|
201
211
|
this.windowsDriveLister = windowsDriveLister;
|
|
212
|
+
this.shellCommandJobs = shellCommandJobs;
|
|
202
213
|
for (const [, runtime] of this.runtimes.entries()) {
|
|
203
214
|
this.unsubscribe.push(runtime.onEvent((event) => {
|
|
204
215
|
void this.handleRuntimeEvent(event).catch((error) => {
|
|
@@ -424,8 +435,9 @@ export class ProxySessionController {
|
|
|
424
435
|
if (command.type === "prompt")
|
|
425
436
|
await this.ensureThreadFork(message);
|
|
426
437
|
await this.execute(message.contextKey, command, message.messageId, replyTarget, localImagePaths, message.userId, message);
|
|
427
|
-
if (!
|
|
438
|
+
if (!commandDefersReactionFinalization(command)) {
|
|
428
439
|
await this.finalizeStandaloneMessageReaction(message.messageId, "completed");
|
|
440
|
+
}
|
|
429
441
|
}
|
|
430
442
|
catch (error) {
|
|
431
443
|
await this.finalizeStandaloneMessageReaction(message.messageId, "failed");
|
|
@@ -440,8 +452,9 @@ export class ProxySessionController {
|
|
|
440
452
|
if (command.type === "prompt")
|
|
441
453
|
await this.ensureThreadFork(message);
|
|
442
454
|
await this.execute(message.contextKey, command, message.messageId, replyTarget, localImagePaths, message.userId, message);
|
|
443
|
-
if (!
|
|
455
|
+
if (!commandDefersReactionFinalization(command)) {
|
|
444
456
|
await this.finalizeStandaloneMessageReaction(message.messageId, "completed");
|
|
457
|
+
}
|
|
445
458
|
}
|
|
446
459
|
catch (error) {
|
|
447
460
|
await this.finalizeStandaloneMessageReaction(message.messageId, "failed");
|
|
@@ -493,6 +506,9 @@ export class ProxySessionController {
|
|
|
493
506
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
494
507
|
await this.cancelSession(this.requireSession(contextKey, sessionId));
|
|
495
508
|
}
|
|
509
|
+
else if (kind === "shell_command_cancel") {
|
|
510
|
+
await this.cancelShellCommandJob(contextKey, String(scopedAction.value.jobId ?? ""));
|
|
511
|
+
}
|
|
496
512
|
else if (kind === "turn_reset") {
|
|
497
513
|
const sessionId = String(scopedAction.value.sessionId ?? "");
|
|
498
514
|
await this.resetCurrentSessionToTurn(contextKey, sessionId, String(scopedAction.value.turnId ?? ""));
|
|
@@ -753,6 +769,8 @@ export class ProxySessionController {
|
|
|
753
769
|
for (const timer of this.recoveryRetryTimers.values())
|
|
754
770
|
clearTimeout(timer);
|
|
755
771
|
this.recoveryRetryTimers.clear();
|
|
772
|
+
for (const [jobId] of this.shellCommandJobMonitors)
|
|
773
|
+
this.stopShellCommandJobMonitor(jobId);
|
|
756
774
|
clearInterval(this.recoveryActivityHeartbeat);
|
|
757
775
|
}
|
|
758
776
|
async controlStopTask(localSessionId) {
|
|
@@ -1269,11 +1287,14 @@ export class ProxySessionController {
|
|
|
1269
1287
|
}
|
|
1270
1288
|
switch (command.type) {
|
|
1271
1289
|
case "shell":
|
|
1272
|
-
await this.runShellCommand(contextKey, command.command);
|
|
1290
|
+
await this.runShellCommand(contextKey, command.command, messageId);
|
|
1273
1291
|
return;
|
|
1274
1292
|
case "dir":
|
|
1275
1293
|
await this.openDirectoryBrowser(contextKey, command.directory);
|
|
1276
1294
|
return;
|
|
1295
|
+
case "file":
|
|
1296
|
+
await this.sendCurrentTaskFile(contextKey, command.filePath);
|
|
1297
|
+
return;
|
|
1277
1298
|
case "new":
|
|
1278
1299
|
if (command.projectless && this.ensureAgent(context.defaultAgent).kind !== "app-server") {
|
|
1279
1300
|
throw new Error("/new --nodir 仅支持 App Server Agent。");
|
|
@@ -1416,6 +1437,7 @@ export class ProxySessionController {
|
|
|
1416
1437
|
return recovery;
|
|
1417
1438
|
}
|
|
1418
1439
|
async runStartupRecovery() {
|
|
1440
|
+
await this.recoverShellCommandJobs();
|
|
1419
1441
|
this.backfillTurnAttemptsForUpgrade();
|
|
1420
1442
|
const attempts = this.store.listIncompleteTurnAttempts();
|
|
1421
1443
|
const recoveryCutoff = Date.now() - RECOVERY_ACTIVITY_WINDOW_MS;
|
|
@@ -3510,102 +3532,138 @@ export class ProxySessionController {
|
|
|
3510
3532
|
this.logger.warn({ error, localSessionId }, "Failed to read the latest Agent Goal status.");
|
|
3511
3533
|
}
|
|
3512
3534
|
}
|
|
3513
|
-
async runShellCommand(contextKey, command) {
|
|
3535
|
+
async runShellCommand(contextKey, command, sourceMessageId) {
|
|
3514
3536
|
const cwd = this.currentSession(contextKey)?.cwd ?? this.config.defaults.cwd;
|
|
3515
|
-
const
|
|
3516
|
-
let
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
};
|
|
3521
|
-
let runningState = {
|
|
3537
|
+
const created = await this.shellCommandJobs.createJob({ contextKey, sourceMessageId, command, cwd });
|
|
3538
|
+
let cardMessageId;
|
|
3539
|
+
const initialState = {
|
|
3540
|
+
jobId: created.id,
|
|
3541
|
+
contextKey,
|
|
3522
3542
|
command,
|
|
3523
3543
|
cwd,
|
|
3524
|
-
|
|
3544
|
+
output: "",
|
|
3545
|
+
outputTruncated: false,
|
|
3525
3546
|
status: "running",
|
|
3526
3547
|
elapsedMs: 0,
|
|
3527
3548
|
};
|
|
3528
|
-
const cardMessageId = await this.outbound.sendInteractiveCard(contextKey, this.cardRenderer.renderShellCommandCard(runningState));
|
|
3529
|
-
const scheduler = cardMessageId
|
|
3530
|
-
? new CardUpdateScheduler({
|
|
3531
|
-
render: (state) => this.cardRenderer.renderShellCommandCard(state),
|
|
3532
|
-
write: (card) => this.outbound.updateInteractiveCard(contextKey, cardMessageId, card),
|
|
3533
|
-
onError: (error) => this.logger.warn({ error, contextKey, messageId: cardMessageId }, "Failed to update a shell command card."),
|
|
3534
|
-
})
|
|
3535
|
-
: undefined;
|
|
3536
|
-
scheduler?.seed(runningState);
|
|
3537
|
-
const elapsedTimer = scheduler ? setInterval(() => {
|
|
3538
|
-
runningState = { ...runningState, elapsedMs: Date.now() - startedAt };
|
|
3539
|
-
scheduler.update(runningState);
|
|
3540
|
-
}, SHELL_COMMAND_ELAPSED_UPDATE_INTERVAL_MS) : undefined;
|
|
3541
|
-
elapsedTimer?.unref?.();
|
|
3542
|
-
const updateRunningOutput = (snapshot) => {
|
|
3543
|
-
latestOutput = snapshot;
|
|
3544
|
-
runningState = {
|
|
3545
|
-
...runningState,
|
|
3546
|
-
...snapshot,
|
|
3547
|
-
elapsedMs: Date.now() - startedAt,
|
|
3548
|
-
};
|
|
3549
|
-
scheduler?.update(runningState);
|
|
3550
|
-
};
|
|
3551
|
-
let result;
|
|
3552
3549
|
try {
|
|
3553
|
-
|
|
3550
|
+
cardMessageId = await this.outbound.sendInteractiveCard(contextKey, this.cardRenderer.renderShellCommandCard(initialState));
|
|
3551
|
+
if (!cardMessageId)
|
|
3552
|
+
throw new Error("Failed to create the shell command card.");
|
|
3553
|
+
const bound = await this.shellCommandJobs.bindCard(created.id, cardMessageId);
|
|
3554
|
+
await this.shellCommandJobs.startJob(created.id);
|
|
3555
|
+
this.monitorShellCommandJob(bound, initialState);
|
|
3554
3556
|
}
|
|
3555
3557
|
catch (error) {
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
const failedCard = this.cardRenderer.renderShellCommandCard({
|
|
3559
|
-
command,
|
|
3560
|
-
cwd,
|
|
3561
|
-
...latestOutput,
|
|
3562
|
-
stderr: [latestOutput.stderr.trimEnd(), runtimeErrorMessage(error)].filter(Boolean).join("\n"),
|
|
3563
|
-
status: "failed",
|
|
3564
|
-
elapsedMs: Date.now() - startedAt,
|
|
3565
|
-
});
|
|
3566
|
-
if (scheduler) {
|
|
3558
|
+
await this.shellCommandJobs.failJob(created.id, runtimeErrorMessage(error)).catch(() => undefined);
|
|
3559
|
+
if (cardMessageId) {
|
|
3567
3560
|
try {
|
|
3568
|
-
await
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
status: "failed",
|
|
3572
|
-
elapsedMs: Date.now() - startedAt,
|
|
3573
|
-
});
|
|
3561
|
+
const failed = await this.shellCommandJobs.readJob(created.id);
|
|
3562
|
+
await this.outbound.updateInteractiveCard(contextKey, cardMessageId, this.cardRenderer.renderShellCommandCard(shellCommandJobCardView(failed)));
|
|
3563
|
+
await this.shellCommandJobs.markPresented(created.id);
|
|
3574
3564
|
}
|
|
3575
3565
|
catch (cardError) {
|
|
3576
|
-
this.logger.warn({ error: cardError, contextKey, messageId: cardMessageId }, "Failed to finalize a shell command card.");
|
|
3577
|
-
}
|
|
3578
|
-
finally {
|
|
3579
|
-
scheduler.dispose();
|
|
3566
|
+
this.logger.warn({ error: cardError, jobId: created.id, contextKey, messageId: cardMessageId }, "Failed to finalize a shell command card after the runner failed to start.");
|
|
3580
3567
|
}
|
|
3581
3568
|
}
|
|
3582
3569
|
else {
|
|
3583
|
-
await this.
|
|
3570
|
+
await this.shellCommandJobs.markPresented(created.id).catch(() => undefined);
|
|
3584
3571
|
}
|
|
3585
3572
|
throw error;
|
|
3586
3573
|
}
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3574
|
+
}
|
|
3575
|
+
monitorShellCommandJob(job, seededState) {
|
|
3576
|
+
if (!job.cardMessageId || this.shellCommandJobMonitors.has(job.id))
|
|
3577
|
+
return;
|
|
3578
|
+
const scheduler = new CardUpdateScheduler({
|
|
3579
|
+
render: (state) => this.cardRenderer.renderShellCommandCard(state),
|
|
3580
|
+
write: (card) => this.outbound.updateInteractiveCard(job.contextKey, job.cardMessageId, card),
|
|
3581
|
+
onError: (error) => this.logger.warn({ error, jobId: job.id, contextKey: job.contextKey, messageId: job.cardMessageId }, "Failed to update a background shell command card."),
|
|
3582
|
+
});
|
|
3583
|
+
if (seededState)
|
|
3584
|
+
scheduler.seed(seededState);
|
|
3585
|
+
const monitor = {
|
|
3586
|
+
scheduler,
|
|
3587
|
+
refreshing: false,
|
|
3588
|
+
timer: setInterval(() => {
|
|
3589
|
+
void this.refreshShellCommandJob(job.id);
|
|
3590
|
+
}, SHELL_COMMAND_JOB_POLL_INTERVAL_MS),
|
|
3598
3591
|
};
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3592
|
+
monitor.timer.unref?.();
|
|
3593
|
+
this.shellCommandJobMonitors.set(job.id, monitor);
|
|
3594
|
+
void this.refreshShellCommandJob(job.id, seededState === undefined);
|
|
3595
|
+
}
|
|
3596
|
+
async refreshShellCommandJob(jobId, critical = false) {
|
|
3597
|
+
const monitor = this.shellCommandJobMonitors.get(jobId);
|
|
3598
|
+
if (!monitor || monitor.refreshing)
|
|
3599
|
+
return;
|
|
3600
|
+
monitor.refreshing = true;
|
|
3601
|
+
try {
|
|
3602
|
+
const job = await this.shellCommandJobs.readJob(jobId);
|
|
3603
|
+
const view = shellCommandJobCardView(job);
|
|
3604
|
+
if (isActiveShellCommandJob(job.status)) {
|
|
3605
|
+
monitor.scheduler.update(view, critical ? "critical" : "normal");
|
|
3606
|
+
return;
|
|
3602
3607
|
}
|
|
3603
|
-
|
|
3604
|
-
|
|
3608
|
+
await monitor.scheduler.flush(view);
|
|
3609
|
+
if (job.sourceMessageId) {
|
|
3610
|
+
await this.finalizeStandaloneMessageReaction(job.sourceMessageId, job.status === "completed" ? "completed" : job.status === "cancelled" ? "cancelled" : "failed");
|
|
3605
3611
|
}
|
|
3612
|
+
await this.shellCommandJobs.markPresented(jobId);
|
|
3613
|
+
this.stopShellCommandJobMonitor(jobId);
|
|
3606
3614
|
}
|
|
3607
|
-
|
|
3608
|
-
|
|
3615
|
+
catch (error) {
|
|
3616
|
+
this.logger.warn({ error, jobId }, "Failed to refresh a background shell command job.");
|
|
3617
|
+
}
|
|
3618
|
+
finally {
|
|
3619
|
+
const current = this.shellCommandJobMonitors.get(jobId);
|
|
3620
|
+
if (current)
|
|
3621
|
+
current.refreshing = false;
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
stopShellCommandJobMonitor(jobId) {
|
|
3625
|
+
const monitor = this.shellCommandJobMonitors.get(jobId);
|
|
3626
|
+
if (!monitor)
|
|
3627
|
+
return;
|
|
3628
|
+
clearInterval(monitor.timer);
|
|
3629
|
+
monitor.scheduler.dispose();
|
|
3630
|
+
this.shellCommandJobMonitors.delete(jobId);
|
|
3631
|
+
}
|
|
3632
|
+
async cancelShellCommandJob(contextKey, jobId) {
|
|
3633
|
+
if (!jobId)
|
|
3634
|
+
throw new Error("命令任务无效,请使用最新的命令卡片。");
|
|
3635
|
+
const job = await this.shellCommandJobs.readJob(jobId);
|
|
3636
|
+
if (job.contextKey !== contextKey)
|
|
3637
|
+
throw new Error("该命令不属于当前会话。");
|
|
3638
|
+
const requested = await this.shellCommandJobs.requestCancellation(jobId);
|
|
3639
|
+
if (!requested) {
|
|
3640
|
+
await this.outbound.sendText(contextKey, "该命令已经结束。");
|
|
3641
|
+
return;
|
|
3642
|
+
}
|
|
3643
|
+
if (!this.shellCommandJobMonitors.has(jobId))
|
|
3644
|
+
this.monitorShellCommandJob(job);
|
|
3645
|
+
await this.refreshShellCommandJob(jobId, true);
|
|
3646
|
+
}
|
|
3647
|
+
async recoverShellCommandJobs() {
|
|
3648
|
+
const jobs = await this.shellCommandJobs.listRecoverableJobs();
|
|
3649
|
+
if (jobs.length > 0) {
|
|
3650
|
+
this.logger.warn({ jobIds: jobs.map((job) => job.id) }, "Recovering background shell command jobs after startup.");
|
|
3651
|
+
}
|
|
3652
|
+
for (const job of jobs) {
|
|
3653
|
+
if (!job.cardMessageId || !this.outbound.canRoute(job.contextKey))
|
|
3654
|
+
continue;
|
|
3655
|
+
if (job.status === "starting"
|
|
3656
|
+
&& job.runnerPid === undefined
|
|
3657
|
+
&& Date.now() - job.updatedAt >= SHELL_COMMAND_JOB_RELAUNCH_DELAY_MS) {
|
|
3658
|
+
try {
|
|
3659
|
+
await this.shellCommandJobs.startJob(job.id);
|
|
3660
|
+
}
|
|
3661
|
+
catch (error) {
|
|
3662
|
+
await this.shellCommandJobs.failJob(job.id, runtimeErrorMessage(error)).catch(() => undefined);
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
const refreshed = await this.shellCommandJobs.readJob(job.id);
|
|
3666
|
+
this.monitorShellCommandJob(refreshed);
|
|
3609
3667
|
}
|
|
3610
3668
|
}
|
|
3611
3669
|
async setTitle(contextKey, title) {
|
|
@@ -3969,6 +4027,12 @@ export class ProxySessionController {
|
|
|
3969
4027
|
if (!stats.isFile())
|
|
3970
4028
|
throw new Error(`这不是普通文件:${filePath}`);
|
|
3971
4029
|
}
|
|
4030
|
+
async sendCurrentTaskFile(contextKey, requestedFilePath) {
|
|
4031
|
+
const record = this.requireCurrentSession(contextKey);
|
|
4032
|
+
const filePath = resolveUserPath(requestedFilePath, record.cwd);
|
|
4033
|
+
await this.assertSendableFile(filePath);
|
|
4034
|
+
await this.outbound.sendFile(contextKey, filePath);
|
|
4035
|
+
}
|
|
3972
4036
|
async listSessions(contextKey, searchTerm, options = {}) {
|
|
3973
4037
|
const context = this.store.getOrCreateUserContext(contextKey, this.config.defaults.agent);
|
|
3974
4038
|
const normalizedSearch = searchTerm?.trim().toLowerCase();
|
|
@@ -5304,7 +5368,7 @@ function isBotOwnedActiveTurn(record, remote) {
|
|
|
5304
5368
|
&& record.lastTurnId === remote.lastTurnId;
|
|
5305
5369
|
}
|
|
5306
5370
|
function isQueueIndependentCommand(command) {
|
|
5307
|
-
if (["archive", "dismiss", "stop", "status", "restart", "mute", "help", "sessions", "dir", "goal", "nosteer"].includes(command.type))
|
|
5371
|
+
if (["archive", "dismiss", "stop", "status", "restart", "mute", "help", "sessions", "dir", "file", "goal", "nosteer", "shell"].includes(command.type))
|
|
5308
5372
|
return true;
|
|
5309
5373
|
if (command.type === "agent")
|
|
5310
5374
|
return command.agent === undefined;
|
|
@@ -5312,8 +5376,22 @@ function isQueueIndependentCommand(command) {
|
|
|
5312
5376
|
return true;
|
|
5313
5377
|
return false;
|
|
5314
5378
|
}
|
|
5379
|
+
function shellCommandJobCardView(job) {
|
|
5380
|
+
const status = job.status === "starting" ? "running" : job.status;
|
|
5381
|
+
return {
|
|
5382
|
+
jobId: job.id,
|
|
5383
|
+
contextKey: job.contextKey,
|
|
5384
|
+
command: job.command,
|
|
5385
|
+
cwd: job.cwd,
|
|
5386
|
+
output: [job.output.trimEnd(), job.error].filter(Boolean).join("\n"),
|
|
5387
|
+
status,
|
|
5388
|
+
exitCode: job.exitCode,
|
|
5389
|
+
elapsedMs: Math.max(0, (job.completedAt ?? Date.now()) - job.startedAt),
|
|
5390
|
+
outputTruncated: job.outputTruncated,
|
|
5391
|
+
};
|
|
5392
|
+
}
|
|
5315
5393
|
function commandRequiresCurrentSession(command) {
|
|
5316
|
-
if (["dismiss", "stop", "title", "turns", "model", "provider", "thinking", "permissions", "nosteer"].includes(command.type)) {
|
|
5394
|
+
if (["dismiss", "stop", "title", "turns", "file", "model", "provider", "thinking", "permissions", "nosteer"].includes(command.type)) {
|
|
5317
5395
|
return true;
|
|
5318
5396
|
}
|
|
5319
5397
|
if (command.type === "archive" || command.type === "fork")
|
|
@@ -5485,8 +5563,8 @@ async function listWindowsDriveRoots() {
|
|
|
5485
5563
|
throw new Error("没有检测到可用的 Windows 磁盘。");
|
|
5486
5564
|
return drives;
|
|
5487
5565
|
}
|
|
5488
|
-
function
|
|
5489
|
-
return command.type === "prompt" || command.type === "nosteer";
|
|
5566
|
+
function commandDefersReactionFinalization(command) {
|
|
5567
|
+
return command.type === "prompt" || command.type === "nosteer" || command.type === "shell";
|
|
5490
5568
|
}
|
|
5491
5569
|
function runtimePrompt(text, localImagePaths) {
|
|
5492
5570
|
return localImagePaths?.length ? { text, localImagePaths } : text;
|