@keyou007/agent-bot 0.1.16-alpha.1 → 0.1.16-alpha.3
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/dist/feishu/CardRenderer.js +32 -14
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +57 -14
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +8 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/supervision/SafeRestartNotifier.js +43 -3
- package/dist/supervision/SafeRestartNotifier.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +8 -8
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/feishu/CardRenderer.ts +32 -10
- package/src/proxy/ProxySessionController.ts +70 -14
- package/src/state/StateStore.ts +9 -0
- package/src/supervision/SafeRestartNotifier.ts +52 -3
- package/src/supervision/SafeRestartScheduler.ts +8 -8
|
@@ -2503,17 +2503,28 @@ export class ProxySessionController {
|
|
|
2503
2503
|
|
|
2504
2504
|
private async presentPromptQueueCard(localSessionId: string, contextKey: string): Promise<void> {
|
|
2505
2505
|
await this.serializePromptQueueCardWrite(localSessionId, async () => {
|
|
2506
|
-
const
|
|
2507
|
-
const existing =
|
|
2506
|
+
const cards = this.queuedPromptCards.get(localSessionId);
|
|
2507
|
+
const existing = cards?.get(contextKey);
|
|
2508
2508
|
if (existing) {
|
|
2509
2509
|
try {
|
|
2510
|
-
await this.outbound.updateInteractiveCard(
|
|
2511
|
-
|
|
2510
|
+
await this.outbound.updateInteractiveCard(
|
|
2511
|
+
contextKey,
|
|
2512
|
+
existing,
|
|
2513
|
+
this.renderPromptQueueCard(localSessionId, contextKey, "superseded"),
|
|
2514
|
+
);
|
|
2512
2515
|
} catch (error) {
|
|
2513
|
-
this.logger.warn(
|
|
2516
|
+
this.logger.warn(
|
|
2517
|
+
{ error, localSessionId, contextKey, messageId: existing },
|
|
2518
|
+
"Failed to stop the previous prompt queue card; sending a replacement.",
|
|
2519
|
+
);
|
|
2514
2520
|
}
|
|
2521
|
+
cards?.delete(contextKey);
|
|
2522
|
+
if (cards?.size === 0) this.queuedPromptCards.delete(localSessionId);
|
|
2515
2523
|
}
|
|
2516
|
-
const messageId = await this.outbound.sendInteractiveCard(
|
|
2524
|
+
const messageId = await this.outbound.sendInteractiveCard(
|
|
2525
|
+
contextKey,
|
|
2526
|
+
this.renderPromptQueueCard(localSessionId, contextKey),
|
|
2527
|
+
);
|
|
2517
2528
|
if (messageId) this.rememberPromptQueueCard(localSessionId, contextKey, messageId);
|
|
2518
2529
|
});
|
|
2519
2530
|
}
|
|
@@ -2536,10 +2547,15 @@ export class ProxySessionController {
|
|
|
2536
2547
|
});
|
|
2537
2548
|
}
|
|
2538
2549
|
|
|
2539
|
-
private renderPromptQueueCard(
|
|
2550
|
+
private renderPromptQueueCard(
|
|
2551
|
+
localSessionId: string,
|
|
2552
|
+
contextKey: string,
|
|
2553
|
+
phase: "active" | "superseded" = "active",
|
|
2554
|
+
): Record<string, unknown> {
|
|
2540
2555
|
return this.cardRenderer.renderPromptQueue({
|
|
2541
2556
|
sessionId: localSessionId,
|
|
2542
2557
|
contextKey,
|
|
2558
|
+
phase,
|
|
2543
2559
|
prompts: this.store.listQueuedPrompts(localSessionId).map((prompt) => ({
|
|
2544
2560
|
id: prompt.promptId,
|
|
2545
2561
|
text: prompt.displayPrompt ?? prompt.text,
|
|
@@ -4835,7 +4851,43 @@ export class ProxySessionController {
|
|
|
4835
4851
|
if (!current.remoteSessionId || !this.isCodexSession(current)) {
|
|
4836
4852
|
throw new Error("当前任务不是可 Reset 的 App Server 任务。");
|
|
4837
4853
|
}
|
|
4838
|
-
const
|
|
4854
|
+
const completedTurns = this.store.listTaskTurnGraph(current.localSessionId);
|
|
4855
|
+
const completedTurnIds = new Set(completedTurns.map((turn) => turn.turnId));
|
|
4856
|
+
const runningTurnId = current.lastTurnStatus === "running" && current.lastTurnId
|
|
4857
|
+
&& !completedTurnIds.has(current.lastTurnId)
|
|
4858
|
+
? current.lastTurnId
|
|
4859
|
+
: undefined;
|
|
4860
|
+
const persistedRunningSnapshot = runningTurnId
|
|
4861
|
+
? turnViewSnapshot(this.store.getTurnSnapshot(runningTurnId))
|
|
4862
|
+
: undefined;
|
|
4863
|
+
const runningAttempt = runningTurnId
|
|
4864
|
+
? this.store.findIncompleteTurnAttemptByTurnId(runningTurnId)
|
|
4865
|
+
: undefined;
|
|
4866
|
+
const attemptStartedAt = Date.parse(runningAttempt?.createdAt ?? "");
|
|
4867
|
+
const runningSnapshot: TurnViewState | undefined = runningTurnId
|
|
4868
|
+
? persistedRunningSnapshot ?? {
|
|
4869
|
+
sessionId: current.localSessionId,
|
|
4870
|
+
turnId: runningTurnId,
|
|
4871
|
+
prompt: runningAttempt?.promptText ?? current.title,
|
|
4872
|
+
status: "running",
|
|
4873
|
+
startedAt: Number.isFinite(attemptStartedAt) ? attemptStartedAt : Date.now(),
|
|
4874
|
+
assistantText: "",
|
|
4875
|
+
plan: [],
|
|
4876
|
+
activities: [],
|
|
4877
|
+
completedTools: [],
|
|
4878
|
+
failedTools: [],
|
|
4879
|
+
fileSummary: [],
|
|
4880
|
+
}
|
|
4881
|
+
: undefined;
|
|
4882
|
+
const runningTurn = runningTurnId && runningSnapshot
|
|
4883
|
+
? {
|
|
4884
|
+
turnId: runningTurnId,
|
|
4885
|
+
parentTurnId: this.store.getTurnParent(runningTurnId, current.localSessionId),
|
|
4886
|
+
snapshot: runningSnapshot,
|
|
4887
|
+
updatedAt: runningAttempt?.updatedAt ?? new Date(runningSnapshot.startedAt).toISOString(),
|
|
4888
|
+
}
|
|
4889
|
+
: undefined;
|
|
4890
|
+
const allTurns = runningTurn ? [runningTurn, ...completedTurns] : completedTurns;
|
|
4839
4891
|
const graphRows = buildTurnGraphRows(allTurns.map((turn) => ({
|
|
4840
4892
|
turnId: turn.turnId,
|
|
4841
4893
|
parentTurnId: turn.parentTurnId,
|
|
@@ -4854,16 +4906,18 @@ export class ProxySessionController {
|
|
|
4854
4906
|
100,
|
|
4855
4907
|
);
|
|
4856
4908
|
const isCurrent = current.lastTurnId === turn.turnId && current.lastTurnStatus === "completed";
|
|
4909
|
+
const isRunning = runningTurnId === turn.turnId;
|
|
4857
4910
|
return [{
|
|
4858
4911
|
sequence: graph.sequence,
|
|
4859
4912
|
graphNodeLine: graph.nodeLine,
|
|
4860
4913
|
graphConnectorLine: graph.connectorLine,
|
|
4861
4914
|
lines: [
|
|
4862
|
-
|
|
4863
|
-
`${formatResetTurnTime(snapshot.completedAt ?? snapshot.startedAt)} · ${
|
|
4915
|
+
cardText(summary || "未记录对话内容"),
|
|
4916
|
+
`${formatResetTurnTime(snapshot.completedAt ?? snapshot.startedAt)} · ${cardText(turn.turnId)}`,
|
|
4864
4917
|
],
|
|
4865
4918
|
current: isCurrent,
|
|
4866
|
-
|
|
4919
|
+
running: isRunning,
|
|
4920
|
+
actions: isCurrent || isRunning ? undefined : [{
|
|
4867
4921
|
text: "Reset",
|
|
4868
4922
|
value: {
|
|
4869
4923
|
action: "turn_reset",
|
|
@@ -4899,7 +4953,9 @@ export class ProxySessionController {
|
|
|
4899
4953
|
const card = this.cardRenderer.renderResetHistoryCard({
|
|
4900
4954
|
entries,
|
|
4901
4955
|
footerLines: [
|
|
4902
|
-
|
|
4956
|
+
runningTurn
|
|
4957
|
+
? `第 ${page + 1}/${totalPages} 页 · 共 ${total} 个 turn(${completedTurns.length} 个已完成,1 个运行中)`
|
|
4958
|
+
: `第 ${page + 1}/${totalPages} 页 · 共 ${total} 个已完成 turn`,
|
|
4903
4959
|
],
|
|
4904
4960
|
pageActions,
|
|
4905
4961
|
});
|
|
@@ -5266,8 +5322,8 @@ export class ProxySessionController {
|
|
|
5266
5322
|
reference: entry.reference,
|
|
5267
5323
|
summary: `${offset + index + 1}. ${marker}${title} · ${entry.agentName}`,
|
|
5268
5324
|
detailLines: [
|
|
5269
|
-
cardText(lastUserPrompt)
|
|
5270
|
-
|
|
5325
|
+
`**最新 Prompt**:${cardText(lastUserPrompt)}`,
|
|
5326
|
+
`<font color='grey'>最近更新:${cardText(entry.updatedLabel)}</font>`,
|
|
5271
5327
|
],
|
|
5272
5328
|
actions,
|
|
5273
5329
|
current: entry.current,
|
package/src/state/StateStore.ts
CHANGED
|
@@ -1057,6 +1057,15 @@ export class StateStore {
|
|
|
1057
1057
|
`).run(turnId, localSessionId, parentTurnId ?? null, new Date().toISOString());
|
|
1058
1058
|
}
|
|
1059
1059
|
|
|
1060
|
+
getTurnParent(turnId: string, localSessionId: string): string | undefined {
|
|
1061
|
+
const row = this.db.prepare(`
|
|
1062
|
+
SELECT parent_turn_id
|
|
1063
|
+
FROM turn_parent_links
|
|
1064
|
+
WHERE turn_id = ? AND local_session_id = ?
|
|
1065
|
+
`).get(turnId, localSessionId) as { parent_turn_id: string | null } | undefined;
|
|
1066
|
+
return row?.parent_turn_id ?? undefined;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1060
1069
|
findLatestCompletedTurnId(localSessionId: string, contextKey: string): string | undefined {
|
|
1061
1070
|
const row = this.db
|
|
1062
1071
|
.prepare(`
|
|
@@ -17,6 +17,7 @@ export class SafeRestartNotifier {
|
|
|
17
17
|
private readonly notificationTargets = new Map<string, RestartNotificationTarget>();
|
|
18
18
|
private activeScheduleId?: number;
|
|
19
19
|
private terminalScheduleId?: number;
|
|
20
|
+
private lastStatus?: SafeRestartStatus;
|
|
20
21
|
private pendingInitialStatus?: SafeRestartStatus;
|
|
21
22
|
private initialCardTimer?: NodeJS.Timeout;
|
|
22
23
|
private queue = Promise.resolve();
|
|
@@ -40,12 +41,24 @@ export class SafeRestartNotifier {
|
|
|
40
41
|
this.terminalScheduleId = Math.max(this.terminalScheduleId ?? 0, status.scheduleId);
|
|
41
42
|
}
|
|
42
43
|
if (this.activeScheduleId !== status.scheduleId) {
|
|
44
|
+
const previousStatus = this.lastStatus ?? this.pendingInitialStatus;
|
|
45
|
+
const previousMessageIds = [...this.messageIds];
|
|
46
|
+
const previousTargets = new Map(this.notificationTargets);
|
|
47
|
+
if (
|
|
48
|
+
previousStatus
|
|
49
|
+
&& previousStatus.phase !== "cancelled"
|
|
50
|
+
&& previousStatus.phase !== "restarting"
|
|
51
|
+
&& previousMessageIds.length > 0
|
|
52
|
+
) {
|
|
53
|
+
this.enqueueSupersededCards(previousStatus, previousMessageIds, previousTargets);
|
|
54
|
+
}
|
|
43
55
|
this.activeScheduleId = status.scheduleId;
|
|
44
56
|
this.messageIds.clear();
|
|
45
57
|
this.cardHashes.clear();
|
|
46
58
|
this.notificationTargets.clear();
|
|
47
59
|
this.clearInitialCardTimer();
|
|
48
60
|
this.pendingInitialStatus = status;
|
|
61
|
+
this.lastStatus = status;
|
|
49
62
|
this.collectNotificationTargets(status);
|
|
50
63
|
const delayMs = status.phase === "cancelled"
|
|
51
64
|
? 0
|
|
@@ -59,6 +72,7 @@ export class SafeRestartNotifier {
|
|
|
59
72
|
}, delayMs);
|
|
60
73
|
return Promise.resolve();
|
|
61
74
|
}
|
|
75
|
+
this.lastStatus = status;
|
|
62
76
|
this.collectNotificationTargets(status);
|
|
63
77
|
if (this.pendingInitialStatus) {
|
|
64
78
|
this.pendingInitialStatus = status;
|
|
@@ -149,9 +163,13 @@ export class SafeRestartNotifier {
|
|
|
149
163
|
} else {
|
|
150
164
|
created = await this.outbound.sendInteractiveCard(contextKey, card);
|
|
151
165
|
}
|
|
152
|
-
if (created
|
|
153
|
-
this.
|
|
154
|
-
|
|
166
|
+
if (created) {
|
|
167
|
+
if (this.activeScheduleId === status.scheduleId) {
|
|
168
|
+
this.messageIds.set(contextKey, created);
|
|
169
|
+
this.cardHashes.set(contextKey, cardHash);
|
|
170
|
+
} else {
|
|
171
|
+
await this.stopCard(created, status, reason ?? status.reason);
|
|
172
|
+
}
|
|
155
173
|
}
|
|
156
174
|
} catch (error) {
|
|
157
175
|
this.logger.warn({ error, contextKey }, "Failed to send safe restart status card.");
|
|
@@ -159,6 +177,37 @@ export class SafeRestartNotifier {
|
|
|
159
177
|
}));
|
|
160
178
|
}
|
|
161
179
|
|
|
180
|
+
private enqueueSupersededCards(
|
|
181
|
+
status: SafeRestartStatus,
|
|
182
|
+
messageIds: Array<[string, string]>,
|
|
183
|
+
targets: Map<string, RestartNotificationTarget>,
|
|
184
|
+
): void {
|
|
185
|
+
const queued = this.queue.catch(() => undefined).then(async () => {
|
|
186
|
+
await Promise.all(messageIds.map(async ([contextKey, messageId]) => {
|
|
187
|
+
const reason = targets.get(contextKey)?.reason ?? status.reason;
|
|
188
|
+
await this.stopCard(messageId, status, reason);
|
|
189
|
+
}));
|
|
190
|
+
});
|
|
191
|
+
this.queue = queued;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private async stopCard(messageId: string, status: SafeRestartStatus, reason: string): Promise<void> {
|
|
195
|
+
try {
|
|
196
|
+
await this.outbound.updateInteractiveCard(messageId, this.renderer.renderSafeRestartStatus({
|
|
197
|
+
scheduleId: status.scheduleId,
|
|
198
|
+
reason,
|
|
199
|
+
phase: "superseded",
|
|
200
|
+
pendingFinalDeliveries: status.activity.pendingFinalDeliveries,
|
|
201
|
+
waitingTasks: [],
|
|
202
|
+
}));
|
|
203
|
+
} catch (error) {
|
|
204
|
+
this.logger.warn(
|
|
205
|
+
{ error, messageId, scheduleId: status.scheduleId },
|
|
206
|
+
"Failed to stop a superseded safe restart status card.",
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
162
211
|
private collectNotificationTargets(status: SafeRestartStatus): void {
|
|
163
212
|
for (const target of status.notificationTargets ?? []) {
|
|
164
213
|
const contextKey = target.contextKey.trim();
|
|
@@ -58,9 +58,9 @@ export class SafeRestartScheduler {
|
|
|
58
58
|
schedule(reason: string, notificationTarget?: RestartNotificationTarget): boolean {
|
|
59
59
|
const newlyScheduled = !this.reason;
|
|
60
60
|
if (newlyScheduled) {
|
|
61
|
-
this.scheduleId += 1;
|
|
62
61
|
this.notificationTargets = [];
|
|
63
62
|
}
|
|
63
|
+
this.scheduleId += 1;
|
|
64
64
|
this.addNotificationTarget(notificationTarget, reason);
|
|
65
65
|
this.reason = reason;
|
|
66
66
|
this.idleSince = undefined;
|
|
@@ -109,12 +109,13 @@ export class SafeRestartScheduler {
|
|
|
109
109
|
private async poll(): Promise<void> {
|
|
110
110
|
const reason = this.reason;
|
|
111
111
|
if (!reason) return;
|
|
112
|
+
const scheduleId = this.scheduleId;
|
|
112
113
|
const state = this.options.readActivity();
|
|
113
114
|
if (state.runningSessions > 0 || state.pendingFinalDeliveries > 0) {
|
|
114
115
|
this.idleSince = undefined;
|
|
115
116
|
this.idleInboundAt = undefined;
|
|
116
117
|
await this.emitStatus({
|
|
117
|
-
scheduleId
|
|
118
|
+
scheduleId,
|
|
118
119
|
reason,
|
|
119
120
|
notificationTargets: this.notificationTargetsSnapshot(),
|
|
120
121
|
phase: state.runningSessions > 0 ? "waiting_tasks" : "waiting_delivery",
|
|
@@ -128,7 +129,7 @@ export class SafeRestartScheduler {
|
|
|
128
129
|
this.idleSince = Date.now();
|
|
129
130
|
this.idleInboundAt = latestInboundAt;
|
|
130
131
|
await this.emitStatus({
|
|
131
|
-
scheduleId
|
|
132
|
+
scheduleId,
|
|
132
133
|
reason,
|
|
133
134
|
notificationTargets: this.notificationTargetsSnapshot(),
|
|
134
135
|
phase: "countdown",
|
|
@@ -140,7 +141,7 @@ export class SafeRestartScheduler {
|
|
|
140
141
|
const remainingMs = Math.max(0, quietPeriodMs - (Date.now() - this.idleSince));
|
|
141
142
|
if (remainingMs > 0) {
|
|
142
143
|
await this.emitStatus({
|
|
143
|
-
scheduleId
|
|
144
|
+
scheduleId,
|
|
144
145
|
reason,
|
|
145
146
|
notificationTargets: this.notificationTargetsSnapshot(),
|
|
146
147
|
phase: "countdown",
|
|
@@ -159,7 +160,7 @@ export class SafeRestartScheduler {
|
|
|
159
160
|
this.idleInboundAt = undefined;
|
|
160
161
|
if (confirmed.runningSessions > 0 || confirmed.pendingFinalDeliveries > 0) {
|
|
161
162
|
await this.emitStatus({
|
|
162
|
-
scheduleId
|
|
163
|
+
scheduleId,
|
|
163
164
|
reason,
|
|
164
165
|
notificationTargets: this.notificationTargetsSnapshot(),
|
|
165
166
|
phase: confirmed.runningSessions > 0 ? "waiting_tasks" : "waiting_delivery",
|
|
@@ -167,7 +168,7 @@ export class SafeRestartScheduler {
|
|
|
167
168
|
});
|
|
168
169
|
} else {
|
|
169
170
|
await this.emitStatus({
|
|
170
|
-
scheduleId
|
|
171
|
+
scheduleId,
|
|
171
172
|
reason,
|
|
172
173
|
notificationTargets: this.notificationTargetsSnapshot(),
|
|
173
174
|
phase: "countdown",
|
|
@@ -177,7 +178,6 @@ export class SafeRestartScheduler {
|
|
|
177
178
|
}
|
|
178
179
|
return;
|
|
179
180
|
}
|
|
180
|
-
const scheduleId = this.scheduleId;
|
|
181
181
|
const notificationTargets = this.notificationTargetsSnapshot();
|
|
182
182
|
this.clear();
|
|
183
183
|
await this.emitStatus({
|
|
@@ -230,7 +230,7 @@ export class SafeRestartScheduler {
|
|
|
230
230
|
this.notificationTargets[existingIndex] = {
|
|
231
231
|
...existing,
|
|
232
232
|
reason: target.reason,
|
|
233
|
-
...(
|
|
233
|
+
...(target.replyMessageId
|
|
234
234
|
? { replyMessageId: target.replyMessageId }
|
|
235
235
|
: {}),
|
|
236
236
|
};
|