@keyou007/agent-bot 0.1.14-alpha.0 → 0.1.14-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/CHANGELOG.md +26 -0
- package/README.md +82 -55
- package/README.zh.md +82 -55
- package/config.example.yaml +5 -1
- package/dist/cli/AutostartManager.js +431 -0
- package/dist/cli/AutostartManager.js.map +1 -0
- package/dist/cli/FeishuAppConfiguration.js +17 -13
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli/Initializer.js +35 -0
- package/dist/cli/Initializer.js.map +1 -1
- package/dist/cli/SelfUpdateRunner.js +552 -0
- package/dist/cli/SelfUpdateRunner.js.map +1 -0
- package/dist/cli/SelfUpdater.js +431 -0
- package/dist/cli/SelfUpdater.js.map +1 -0
- package/dist/cli/autostartOutput.js +63 -0
- package/dist/cli/autostartOutput.js.map +1 -0
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/help.js +92 -18
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/taskGroupOptions.js +14 -11
- package/dist/cli/taskGroupOptions.js.map +1 -1
- package/dist/cli/taskListOutput.js +3 -0
- package/dist/cli/taskListOutput.js.map +1 -1
- package/dist/cli/taskTarget.js +99 -0
- package/dist/cli/taskTarget.js.map +1 -0
- package/dist/cli.js +643 -58
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexRuntime.js +16 -5
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +20 -1
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/config/loadConfig.js +2 -0
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/schema.js +2 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/console/ConsoleTurnPresenter.js +2 -0
- package/dist/console/ConsoleTurnPresenter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +584 -16
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +29 -8
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/feishu/FeishuMarkdown.js +25 -1
- package/dist/feishu/FeishuMarkdown.js.map +1 -1
- package/dist/feishu/FeishuMessageClient.js +43 -0
- package/dist/feishu/FeishuMessageClient.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +29 -1
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/feishu/ownerAccess.js +7 -0
- package/dist/feishu/ownerAccess.js.map +1 -0
- package/dist/index.js +148 -19
- package/dist/index.js.map +1 -1
- package/dist/presentation/OutboundRouter.js +19 -1
- package/dist/presentation/OutboundRouter.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +1444 -66
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/startup/StartupNotifier.js +65 -16
- 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 +268 -67
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/migrations.js +26 -0
- package/dist/state/migrations.js.map +1 -1
- package/dist/supervision/SafeRestartNotifier.js +17 -16
- package/dist/supervision/SafeRestartNotifier.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +12 -7
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/dist/supervision/replacementSupervisor.js +45 -4
- package/dist/supervision/replacementSupervisor.js.map +1 -1
- package/dist/supervisor.js +30 -18
- package/dist/supervisor.js.map +1 -1
- package/docs/technical-reference.md +33 -10
- package/docs/technical-reference.zh.md +33 -10
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
- package/skills/agent-bot/SKILL.md +107 -182
- package/src/cli/AutostartManager.ts +579 -0
- package/src/cli/FeishuAppConfiguration.ts +22 -7
- package/src/cli/Initializer.ts +57 -0
- package/src/cli/SelfUpdateRunner.ts +817 -0
- package/src/cli/SelfUpdater.ts +688 -0
- package/src/cli/autostartOutput.ts +75 -0
- package/src/cli/controlProtocol.ts +111 -1
- package/src/cli/help.ts +92 -18
- package/src/cli/taskGroupOptions.ts +27 -17
- package/src/cli/taskListOutput.ts +10 -0
- package/src/cli/taskTarget.ts +168 -0
- package/src/cli.ts +781 -65
- package/src/codex/CodexRuntime.ts +17 -5
- package/src/commands/CommandRouter.ts +19 -1
- package/src/commands/commandTypes.ts +3 -0
- package/src/config/loadConfig.ts +2 -0
- package/src/config/schema.ts +2 -0
- package/src/console/ConsoleTurnPresenter.ts +4 -1
- package/src/feishu/CardRenderer.ts +716 -16
- package/src/feishu/FeishuConnector.ts +44 -10
- package/src/feishu/FeishuMarkdown.ts +27 -1
- package/src/feishu/FeishuMessageClient.ts +58 -0
- package/src/feishu/FeishuTurnPresenter.ts +35 -2
- package/src/feishu/ownerAccess.ts +7 -0
- package/src/feishu/types.ts +7 -0
- package/src/index.ts +192 -22
- package/src/presentation/OutboundRouter.ts +28 -3
- package/src/proxy/ProxySessionController.ts +1707 -70
- package/src/runtime/types.ts +1 -0
- package/src/startup/StartupNotifier.ts +67 -17
- package/src/startup/startFeishu.ts +5 -3
- package/src/state/StateStore.ts +413 -65
- package/src/state/migrations.ts +26 -0
- package/src/supervision/SafeRestartNotifier.ts +17 -17
- package/src/supervision/SafeRestartScheduler.ts +13 -6
- package/src/supervision/replacementSupervisor.ts +50 -4
- package/src/supervisor.ts +35 -16
package/src/index.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { ConsoleFeishuClient } from "./feishu/ConsoleFeishuClient.js";
|
|
|
19
19
|
import { FeishuConnector } from "./feishu/FeishuConnector.js";
|
|
20
20
|
import { FeishuMessageClient } from "./feishu/FeishuMessageClient.js";
|
|
21
21
|
import { FeishuTurnPresenter } from "./feishu/FeishuTurnPresenter.js";
|
|
22
|
-
import {
|
|
22
|
+
import { isThreadContextKey } from "./feishu/contextKey.js";
|
|
23
23
|
import type { MessageReplyTarget } from "./feishu/types.js";
|
|
24
24
|
import { requireServerFeishuTransport } from "./feishu/transport.js";
|
|
25
25
|
import { createLogger } from "./logging/logger.js";
|
|
@@ -34,8 +34,10 @@ import { startFeishu } from "./startup/startFeishu.js";
|
|
|
34
34
|
import { STOP_EXIT_CODE } from "./supervision/restartPolicy.js";
|
|
35
35
|
import {
|
|
36
36
|
RESTART_GROUP_CONTEXTS_ENV,
|
|
37
|
+
RESTART_NOTIFICATION_TARGETS_ENV,
|
|
37
38
|
replacementSupervisorEnvironment,
|
|
38
39
|
restartGroupContextKeysFromEnvironment,
|
|
40
|
+
restartNotificationTargetsFromEnvironment,
|
|
39
41
|
} from "./supervision/replacementSupervisor.js";
|
|
40
42
|
import { SafeRestartScheduler } from "./supervision/SafeRestartScheduler.js";
|
|
41
43
|
import type { RestartNotificationTarget } from "./supervision/SafeRestartScheduler.js";
|
|
@@ -46,14 +48,22 @@ import {
|
|
|
46
48
|
resolveSupervisorDiagnosticsPaths,
|
|
47
49
|
} from "./supervision/SupervisorDiagnostics.js";
|
|
48
50
|
import { refreshedSystemEnvironment } from "./supervision/systemEnvironment.js";
|
|
51
|
+
import {
|
|
52
|
+
assertSelfUpdatePlanPath,
|
|
53
|
+
launchSelfUpdateRunner,
|
|
54
|
+
readPendingSelfUpdate,
|
|
55
|
+
releaseSelfUpdatePlan,
|
|
56
|
+
} from "./cli/SelfUpdater.js";
|
|
49
57
|
|
|
50
58
|
const processStartedAt = new Date();
|
|
51
59
|
const agentBotVersion = readPackageVersion();
|
|
52
60
|
const supervised = process.env.AGENT_BOT_SUPERVISED === "1";
|
|
53
61
|
const startupReason = process.env.AGENT_BOT_RESTART_REASON?.trim()
|
|
54
62
|
|| (supervised ? "Supervisor 启动" : "直接启动");
|
|
55
|
-
const
|
|
56
|
-
process.env[
|
|
63
|
+
const restartNotificationTargets = mergeRestartNotificationTargets(
|
|
64
|
+
restartNotificationTargetsFromEnvironment(process.env[RESTART_NOTIFICATION_TARGETS_ENV]),
|
|
65
|
+
restartGroupContextKeysFromEnvironment(process.env[RESTART_GROUP_CONTEXTS_ENV])
|
|
66
|
+
.map((contextKey) => ({ contextKey })),
|
|
57
67
|
);
|
|
58
68
|
const consoleOnly = process.env.AGENT_BOT_CONSOLE_ONLY === "1";
|
|
59
69
|
const config = loadConfig();
|
|
@@ -80,7 +90,7 @@ let serverReady = false;
|
|
|
80
90
|
|
|
81
91
|
const feishuOutbound = transport === "sdk" ? new FeishuMessageClient(config, logger) : undefined;
|
|
82
92
|
if (feishuOutbound) {
|
|
83
|
-
const renderer = new CardRenderer();
|
|
93
|
+
const renderer = new CardRenderer({ thinkingCardLayout: config.feishu.thinkingCardLayout });
|
|
84
94
|
const presenter = new FeishuTurnPresenter(feishuOutbound, store, renderer, {
|
|
85
95
|
normalIntervalMs: 2_000,
|
|
86
96
|
criticalGapMs: 500,
|
|
@@ -115,16 +125,26 @@ if (routes.length === 0) throw new Error("Neither Feishu nor console input is en
|
|
|
115
125
|
const outbound = new OutboundRouter(routes);
|
|
116
126
|
let shuttingDown = false;
|
|
117
127
|
let restartRequested = false;
|
|
128
|
+
let pendingSelfUpdatePlanPath: string | undefined;
|
|
118
129
|
const safeRestart = new SafeRestartScheduler({
|
|
119
130
|
readActivity: () => store.getServerActivityState(),
|
|
120
|
-
onReady: (reason, notificationTargets) =>
|
|
131
|
+
onReady: (reason, notificationTargets) => pendingSelfUpdatePlanPath
|
|
132
|
+
? initiateSelfUpdate(reason, notificationTargets)
|
|
133
|
+
: initiateRestart(reason, notificationTargets),
|
|
121
134
|
onStatus: (status) => safeRestartNotifier?.update(status),
|
|
122
135
|
onStatusError: (error) => logger.warn({ error }, "Failed to publish safe restart status."),
|
|
123
136
|
});
|
|
124
137
|
const controller = new ProxySessionController(config, store, runtimes, outbound, logger, {
|
|
125
138
|
supervised,
|
|
126
139
|
restart: requestRestart,
|
|
127
|
-
cancelSafeRestart: (scheduleId) =>
|
|
140
|
+
cancelSafeRestart: async (scheduleId) => {
|
|
141
|
+
const cancelled = await safeRestart.cancelScheduled(scheduleId);
|
|
142
|
+
if (cancelled && pendingSelfUpdatePlanPath) {
|
|
143
|
+
releaseSelfUpdatePlan(pendingSelfUpdatePlanPath);
|
|
144
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
145
|
+
}
|
|
146
|
+
return cancelled;
|
|
147
|
+
},
|
|
128
148
|
rememberFeishuUserOpenId: (userOpenId) => {
|
|
129
149
|
if (config.feishu.userOpenId?.startsWith("ou_")) return;
|
|
130
150
|
const persisted = persistFeishuUserOpenIdIfMissing(defaultDotEnvPath(), userOpenId);
|
|
@@ -170,14 +190,38 @@ if (feishuConnector && startupNotifier) {
|
|
|
170
190
|
startupReason,
|
|
171
191
|
startControlServer,
|
|
172
192
|
() => { serverReady = true; },
|
|
173
|
-
|
|
193
|
+
restartNotificationTargets,
|
|
174
194
|
);
|
|
175
195
|
} else {
|
|
176
196
|
await startControlServer();
|
|
177
197
|
await feishuConnector?.start();
|
|
178
198
|
serverReady = true;
|
|
179
199
|
}
|
|
200
|
+
await controller.recoverInterruptedTasks().catch((error: unknown) => {
|
|
201
|
+
logger.error({ error }, "Startup task recovery failed; the server will remain available.");
|
|
202
|
+
});
|
|
180
203
|
consoleConnector?.start();
|
|
204
|
+
recoverPendingSelfUpdate();
|
|
205
|
+
|
|
206
|
+
function recoverPendingSelfUpdate(): void {
|
|
207
|
+
const pending = readPendingSelfUpdate(agentBotHome());
|
|
208
|
+
if (!pending || pendingSelfUpdatePlanPath || restartRequested || safeRestart.scheduled) return;
|
|
209
|
+
let notificationTarget: RestartNotificationTarget | undefined;
|
|
210
|
+
try {
|
|
211
|
+
notificationTarget = inferControlRestartTarget(pending.plan.notificationSessionId);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
logger.warn({ error }, "Could not restore the self-update notification target; update recovery will continue.");
|
|
214
|
+
}
|
|
215
|
+
pendingSelfUpdatePlanPath = pending.planPath;
|
|
216
|
+
safeRestart.schedule(
|
|
217
|
+
pending.plan.reason ?? `Resume Agent Bot update to ${pending.plan.toVersion}`,
|
|
218
|
+
notificationTarget,
|
|
219
|
+
);
|
|
220
|
+
logger.warn(
|
|
221
|
+
{ planPath: pending.planPath, toVersion: pending.plan.toVersion },
|
|
222
|
+
"Recovered a prepared Agent Bot self-update after Worker restart.",
|
|
223
|
+
);
|
|
224
|
+
}
|
|
181
225
|
|
|
182
226
|
async function requestRestart(
|
|
183
227
|
contextKey: string,
|
|
@@ -192,6 +236,10 @@ async function requestRestart(
|
|
|
192
236
|
await outbound.sendText(contextKey, "Agent Bot 已在重启中,请稍候。").catch(() => undefined);
|
|
193
237
|
return;
|
|
194
238
|
}
|
|
239
|
+
if (pendingSelfUpdatePlanPath) {
|
|
240
|
+
await outbound.sendText(contextKey, "Agent Bot 更新已在等待中;更新完成后服务会自动恢复。").catch(() => undefined);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
195
243
|
if (force) {
|
|
196
244
|
await initiateRestart("用户执行 /restart --force 命令", [notificationTarget]);
|
|
197
245
|
return;
|
|
@@ -216,13 +264,13 @@ async function initiateRestart(
|
|
|
216
264
|
return;
|
|
217
265
|
}
|
|
218
266
|
restartRequested = true;
|
|
267
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
219
268
|
await safeRestart.cancelCurrent();
|
|
220
269
|
await safeRestartNotifier?.flush();
|
|
221
270
|
const allNotificationTargets = mergeRestartNotificationTargets(
|
|
222
271
|
notificationTargets,
|
|
223
272
|
safeRestartNotifier?.getNotificationTargets() ?? [],
|
|
224
273
|
);
|
|
225
|
-
const restartGroupContextKeys = groupContextKeysForRestartTargets(allNotificationTargets);
|
|
226
274
|
await sendRestartTexts(
|
|
227
275
|
allNotificationTargets,
|
|
228
276
|
"Agent Bot 正在重启,恢复在线后会发送启动状态通知。",
|
|
@@ -230,10 +278,59 @@ async function initiateRestart(
|
|
|
230
278
|
setTimeout(() => void shutdown(
|
|
231
279
|
supervised ? STOP_EXIT_CODE : 0,
|
|
232
280
|
reason,
|
|
233
|
-
|
|
281
|
+
allNotificationTargets,
|
|
234
282
|
), 100);
|
|
235
283
|
}
|
|
236
284
|
|
|
285
|
+
async function initiateSelfUpdate(
|
|
286
|
+
reason: string,
|
|
287
|
+
notificationTargets: RestartNotificationTarget[] = [],
|
|
288
|
+
): Promise<void> {
|
|
289
|
+
if (restartRequested) {
|
|
290
|
+
await sendRestartTexts(notificationTargets, "Agent Bot 已在重启或更新中,请稍候。");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const planPath = pendingSelfUpdatePlanPath;
|
|
294
|
+
if (!planPath) return;
|
|
295
|
+
restartRequested = true;
|
|
296
|
+
await safeRestartNotifier?.flush();
|
|
297
|
+
const allNotificationTargets = mergeRestartNotificationTargets(
|
|
298
|
+
notificationTargets,
|
|
299
|
+
safeRestartNotifier?.getNotificationTargets() ?? [],
|
|
300
|
+
);
|
|
301
|
+
await sendRestartTexts(
|
|
302
|
+
allNotificationTargets,
|
|
303
|
+
"Agent Bot 正在更新,完成或回滚后会自动恢复服务并发送启动状态通知。",
|
|
304
|
+
);
|
|
305
|
+
const environmentRefresh = refreshedSystemEnvironment();
|
|
306
|
+
if (environmentRefresh.error) {
|
|
307
|
+
logger.warn({ error: environmentRefresh.error }, "Failed to refresh the environment for Agent Bot update.");
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
assertSelfUpdatePlanPath(planPath, agentBotHome());
|
|
311
|
+
launchSelfUpdateRunner(planPath, {
|
|
312
|
+
waitPids: [process.pid, ...(supervised ? [process.ppid] : [])],
|
|
313
|
+
environment: replacementSupervisorEnvironment(
|
|
314
|
+
reason,
|
|
315
|
+
environmentRefresh.environment,
|
|
316
|
+
allNotificationTargets,
|
|
317
|
+
),
|
|
318
|
+
});
|
|
319
|
+
} catch (error) {
|
|
320
|
+
restartRequested = false;
|
|
321
|
+
releaseSelfUpdatePlan(planPath);
|
|
322
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
323
|
+
logger.error({ error, planPath }, "Failed to launch Agent Bot self-update; keeping the current service online.");
|
|
324
|
+
await sendRestartTexts(
|
|
325
|
+
allNotificationTargets,
|
|
326
|
+
`Agent Bot 更新未启动,当前服务保持在线:${error instanceof Error ? error.message : String(error)}`,
|
|
327
|
+
);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
331
|
+
setTimeout(() => void shutdown(STOP_EXIT_CODE), 100);
|
|
332
|
+
}
|
|
333
|
+
|
|
237
334
|
async function handleControlRequest(request: ControlRequest): Promise<ControlResponse> {
|
|
238
335
|
switch (request.action) {
|
|
239
336
|
case "health":
|
|
@@ -262,6 +359,9 @@ async function handleControlRequest(request: ControlRequest): Promise<ControlRes
|
|
|
262
359
|
},
|
|
263
360
|
};
|
|
264
361
|
case "server_restart": {
|
|
362
|
+
if (pendingSelfUpdatePlanPath) {
|
|
363
|
+
return { ok: false, message: "An Agent Bot update is already pending." };
|
|
364
|
+
}
|
|
265
365
|
const notificationTarget = inferControlRestartTarget(request.notificationSessionId);
|
|
266
366
|
if (request.mode === "safe") {
|
|
267
367
|
const newlyScheduled = safeRestart.schedule(request.reason, notificationTarget);
|
|
@@ -278,18 +378,90 @@ async function handleControlRequest(request: ControlRequest): Promise<ControlRes
|
|
|
278
378
|
), 25);
|
|
279
379
|
return { ok: true, message: "Immediate restart requested." };
|
|
280
380
|
}
|
|
381
|
+
case "server_update": {
|
|
382
|
+
if (restartRequested || pendingSelfUpdatePlanPath || safeRestart.scheduled) {
|
|
383
|
+
return { ok: false, message: "A restart or update is already pending." };
|
|
384
|
+
}
|
|
385
|
+
assertSelfUpdatePlanPath(request.planPath, agentBotHome());
|
|
386
|
+
const notificationTarget = inferControlRestartTarget(request.notificationSessionId);
|
|
387
|
+
pendingSelfUpdatePlanPath = request.planPath;
|
|
388
|
+
safeRestart.schedule(request.reason, notificationTarget);
|
|
389
|
+
return {
|
|
390
|
+
ok: true,
|
|
391
|
+
message: "Agent Bot update scheduled after active tasks finish and the server becomes idle.",
|
|
392
|
+
};
|
|
393
|
+
}
|
|
281
394
|
case "server_stop":
|
|
282
395
|
safeRestart.cancel();
|
|
396
|
+
if (pendingSelfUpdatePlanPath) releaseSelfUpdatePlan(pendingSelfUpdatePlanPath);
|
|
397
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
283
398
|
setTimeout(() => void shutdown(STOP_EXIT_CODE), 25);
|
|
284
399
|
return { ok: true, message: "Agent Bot server stop requested." };
|
|
285
400
|
case "task_status":
|
|
286
401
|
return { ok: true, data: await controller.controlGetTaskStatus(request.localSessionId) };
|
|
287
402
|
case "task_stop":
|
|
288
403
|
return { ok: true, message: await controller.controlStopTask(request.localSessionId) };
|
|
404
|
+
case "task_archive":
|
|
405
|
+
return { ok: true, data: await controller.controlArchiveTask(request.localSessionId) };
|
|
289
406
|
case "task_title":
|
|
290
407
|
return { ok: true, message: await controller.controlSetTaskTitle(request.localSessionId, request.title) };
|
|
291
408
|
case "task_prompt":
|
|
292
409
|
return { ok: true, message: await controller.controlSendTaskPrompt(request.localSessionId, request.text) };
|
|
410
|
+
case "task_new":
|
|
411
|
+
return {
|
|
412
|
+
ok: true,
|
|
413
|
+
data: await controller.controlCreateTask(
|
|
414
|
+
request.localSessionId,
|
|
415
|
+
request.title,
|
|
416
|
+
request.cwd,
|
|
417
|
+
request.projectless === true,
|
|
418
|
+
request.agentName,
|
|
419
|
+
),
|
|
420
|
+
};
|
|
421
|
+
case "task_fork":
|
|
422
|
+
return { ok: true, data: await controller.controlForkTask(request.localSessionId) };
|
|
423
|
+
case "task_switch":
|
|
424
|
+
return {
|
|
425
|
+
ok: true,
|
|
426
|
+
data: await controller.controlSwitchTask(
|
|
427
|
+
request.localSessionId,
|
|
428
|
+
request.targetLocalSessionId,
|
|
429
|
+
request.previous === true,
|
|
430
|
+
),
|
|
431
|
+
};
|
|
432
|
+
case "task_queue":
|
|
433
|
+
return { ok: true, data: await controller.controlQueueTaskPrompt(request.localSessionId, request.text) };
|
|
434
|
+
case "task_agent":
|
|
435
|
+
return { ok: true, data: controller.controlTaskAgent(request.localSessionId, request.agentName) };
|
|
436
|
+
case "task_settings":
|
|
437
|
+
return {
|
|
438
|
+
ok: true,
|
|
439
|
+
data: await controller.controlTaskSettings(request.localSessionId, request.setting, request.value),
|
|
440
|
+
};
|
|
441
|
+
case "task_goal":
|
|
442
|
+
return {
|
|
443
|
+
ok: true,
|
|
444
|
+
data: await controller.controlTaskGoal(
|
|
445
|
+
request.localSessionId,
|
|
446
|
+
request.goalAction,
|
|
447
|
+
request.objective,
|
|
448
|
+
),
|
|
449
|
+
};
|
|
450
|
+
case "task_turns":
|
|
451
|
+
return { ok: true, data: controller.controlListTaskTurns(request.localSessionId) };
|
|
452
|
+
case "task_reset":
|
|
453
|
+
return { ok: true, data: await controller.controlResetTaskToTurn(request.localSessionId, request.turnId) };
|
|
454
|
+
case "task_mute":
|
|
455
|
+
return { ok: true, data: controller.controlTaskMute(request.localSessionId, request.enabled) };
|
|
456
|
+
case "task_shell":
|
|
457
|
+
return { ok: true, data: await controller.controlRunTaskShell(request.localSessionId, request.command) };
|
|
458
|
+
case "task_directory":
|
|
459
|
+
return {
|
|
460
|
+
ok: true,
|
|
461
|
+
data: await controller.controlListTaskDirectory(request.localSessionId, request.directory, request.page),
|
|
462
|
+
};
|
|
463
|
+
case "task_send_file":
|
|
464
|
+
return { ok: true, data: { messageId: await controller.controlSendTaskFile(request.localSessionId, request.filePath) } };
|
|
293
465
|
case "task_new_group":
|
|
294
466
|
return {
|
|
295
467
|
ok: true,
|
|
@@ -317,10 +489,14 @@ async function handleControlRequest(request: ControlRequest): Promise<ControlRes
|
|
|
317
489
|
async function shutdown(
|
|
318
490
|
exitCode: number,
|
|
319
491
|
restartReason?: string,
|
|
320
|
-
|
|
492
|
+
restartNotificationTargets: RestartNotificationTarget[] = [],
|
|
321
493
|
): Promise<void> {
|
|
322
494
|
if (shuttingDown) return;
|
|
323
495
|
shuttingDown = true;
|
|
496
|
+
if (pendingSelfUpdatePlanPath) {
|
|
497
|
+
releaseSelfUpdatePlan(pendingSelfUpdatePlanPath);
|
|
498
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
499
|
+
}
|
|
324
500
|
logger.info({ exitCode }, "Shutting down Agent Bot.");
|
|
325
501
|
safeRestart.cancel();
|
|
326
502
|
feishuConnector?.stop();
|
|
@@ -333,11 +509,14 @@ async function shutdown(
|
|
|
333
509
|
runtimes.close();
|
|
334
510
|
await controlServer?.close().catch((error: unknown) => logger.warn({ error }, "Failed to close local control endpoint."));
|
|
335
511
|
store.close();
|
|
336
|
-
if (restartReason) startReplacementSupervisor(restartReason,
|
|
512
|
+
if (restartReason) startReplacementSupervisor(restartReason, restartNotificationTargets);
|
|
337
513
|
process.exit(exitCode);
|
|
338
514
|
}
|
|
339
515
|
|
|
340
|
-
function startReplacementSupervisor(
|
|
516
|
+
function startReplacementSupervisor(
|
|
517
|
+
restartReason: string,
|
|
518
|
+
restartNotificationTargets: RestartNotificationTarget[],
|
|
519
|
+
): void {
|
|
341
520
|
const supervisorEntry = fileURLToPath(new URL("./supervisor.js", import.meta.url));
|
|
342
521
|
const reportDirectory = resolveSupervisorDiagnosticsPaths(config).crashReportDirectory;
|
|
343
522
|
prepareCrashReportDirectory(reportDirectory);
|
|
@@ -361,7 +540,7 @@ function startReplacementSupervisor(restartReason: string, restartGroupContextKe
|
|
|
361
540
|
env: replacementSupervisorEnvironment(
|
|
362
541
|
restartReason,
|
|
363
542
|
environmentRefresh.environment,
|
|
364
|
-
|
|
543
|
+
restartNotificationTargets,
|
|
365
544
|
),
|
|
366
545
|
});
|
|
367
546
|
supervisor.unref();
|
|
@@ -430,15 +609,6 @@ async function sendRestartTexts(targets: RestartNotificationTarget[], text: stri
|
|
|
430
609
|
}));
|
|
431
610
|
}
|
|
432
611
|
|
|
433
|
-
function groupContextKeysForRestartTargets(targets: RestartNotificationTarget[]): string[] {
|
|
434
|
-
const knownGroupContextKeys = new Set(
|
|
435
|
-
store.listChatContexts("group").map((chat) => chat.contextKey),
|
|
436
|
-
);
|
|
437
|
-
return [...new Set(targets
|
|
438
|
-
.map((target) => baseChatContextKey(target.contextKey))
|
|
439
|
-
.filter((contextKey) => knownGroupContextKeys.has(contextKey)))];
|
|
440
|
-
}
|
|
441
|
-
|
|
442
612
|
function mergeRestartNotificationTargets(
|
|
443
613
|
...targetGroups: RestartNotificationTarget[][]
|
|
444
614
|
): RestartNotificationTarget[] {
|
|
@@ -23,8 +23,9 @@ export interface TurnPresenter {
|
|
|
23
23
|
taskTitle?: string,
|
|
24
24
|
replyTarget?: MessageReplyTarget,
|
|
25
25
|
prompt?: string,
|
|
26
|
-
): Promise<
|
|
26
|
+
): Promise<string | undefined>;
|
|
27
27
|
failPendingTurn(sessionId: string, message: string): Promise<void>;
|
|
28
|
+
interruptTurnForRecovery(sessionId: string, contextKey: string, turnId: string, message: string): Promise<void>;
|
|
28
29
|
appendSteerMessage(sessionId: string, turnId: string, text: string, messageId?: string): Promise<void>;
|
|
29
30
|
onEvent(event: AgentEvent): Promise<void>;
|
|
30
31
|
showDetails(contextKey: string, turnId: string): Promise<void>;
|
|
@@ -75,6 +76,10 @@ export class OutboundRouter {
|
|
|
75
76
|
return this.sessionReplyTargets.get(sessionId);
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
canRoute(contextKey: string): boolean {
|
|
80
|
+
return this.routes.some((route) => route.matches(contextKey));
|
|
81
|
+
}
|
|
82
|
+
|
|
78
83
|
updateSessionTitle(sessionId: string, taskTitle: string): void {
|
|
79
84
|
this.sessionRoutes.get(sessionId)?.presenter.updateSessionTitle(sessionId, taskTitle);
|
|
80
85
|
}
|
|
@@ -93,19 +98,28 @@ export class OutboundRouter {
|
|
|
93
98
|
taskTitle?: string,
|
|
94
99
|
replyTarget?: MessageReplyTarget,
|
|
95
100
|
prompt?: string,
|
|
96
|
-
): Promise<
|
|
101
|
+
): Promise<string | undefined> {
|
|
97
102
|
const route = this.route(contextKey);
|
|
98
103
|
this.sessionRoutes.set(sessionId, route);
|
|
99
104
|
this.sessionContextKeys.set(sessionId, contextKey);
|
|
100
105
|
if (replyTarget) this.sessionReplyTargets.set(sessionId, replyTarget);
|
|
101
106
|
else this.sessionReplyTargets.delete(sessionId);
|
|
102
|
-
|
|
107
|
+
return route.presenter.startPendingTurn(sessionId, contextKey, taskTitle, replyTarget, prompt);
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
async failPendingTurn(sessionId: string, message: string): Promise<void> {
|
|
106
111
|
await this.sessionRoutes.get(sessionId)?.presenter.failPendingTurn(sessionId, message);
|
|
107
112
|
}
|
|
108
113
|
|
|
114
|
+
async interruptTurnForRecovery(
|
|
115
|
+
sessionId: string,
|
|
116
|
+
contextKey: string,
|
|
117
|
+
turnId: string,
|
|
118
|
+
message: string,
|
|
119
|
+
): Promise<void> {
|
|
120
|
+
await this.route(contextKey).presenter.interruptTurnForRecovery(sessionId, contextKey, turnId, message);
|
|
121
|
+
}
|
|
122
|
+
|
|
109
123
|
async appendSteerMessage(
|
|
110
124
|
sessionId: string,
|
|
111
125
|
turnId: string,
|
|
@@ -175,6 +189,17 @@ export class OutboundRouter {
|
|
|
175
189
|
: outbound.sendText(contextKey, text);
|
|
176
190
|
}
|
|
177
191
|
|
|
192
|
+
sendFile(contextKey: string, filePath: string): Promise<string | undefined> {
|
|
193
|
+
const outbound = this.route(contextKey).outbound;
|
|
194
|
+
const target = this.currentReplyTarget(contextKey);
|
|
195
|
+
if (target) {
|
|
196
|
+
if (!outbound.replyFile) throw new Error("当前消息通道不支持在话题中发送文件。");
|
|
197
|
+
return outbound.replyFile(contextKey, target, filePath);
|
|
198
|
+
}
|
|
199
|
+
if (!outbound.sendFile) throw new Error("当前消息通道不支持发送文件。");
|
|
200
|
+
return outbound.sendFile(contextKey, filePath);
|
|
201
|
+
}
|
|
202
|
+
|
|
178
203
|
sendMarkdown(contextKey: string, markdown: string): Promise<string | undefined> {
|
|
179
204
|
const outbound = this.route(contextKey).outbound;
|
|
180
205
|
const target = this.currentReplyTarget(contextKey);
|