@keyou007/agent-bot 0.1.14-alpha.0 → 0.1.14-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.
Files changed (74) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +41 -44
  3. package/README.zh.md +41 -44
  4. package/config.example.yaml +4 -0
  5. package/dist/codex/CodexRuntime.js +8 -3
  6. package/dist/codex/CodexRuntime.js.map +1 -1
  7. package/dist/commands/CommandRouter.js +13 -0
  8. package/dist/commands/CommandRouter.js.map +1 -1
  9. package/dist/config/loadConfig.js +2 -0
  10. package/dist/config/loadConfig.js.map +1 -1
  11. package/dist/config/schema.js +2 -0
  12. package/dist/config/schema.js.map +1 -1
  13. package/dist/console/ConsoleTurnPresenter.js +2 -0
  14. package/dist/console/ConsoleTurnPresenter.js.map +1 -1
  15. package/dist/feishu/CardRenderer.js +428 -9
  16. package/dist/feishu/CardRenderer.js.map +1 -1
  17. package/dist/feishu/FeishuConnector.js +29 -8
  18. package/dist/feishu/FeishuConnector.js.map +1 -1
  19. package/dist/feishu/FeishuMessageClient.js +43 -0
  20. package/dist/feishu/FeishuMessageClient.js.map +1 -1
  21. package/dist/feishu/FeishuTurnPresenter.js +29 -1
  22. package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
  23. package/dist/feishu/ownerAccess.js +7 -0
  24. package/dist/feishu/ownerAccess.js.map +1 -0
  25. package/dist/index.js +14 -17
  26. package/dist/index.js.map +1 -1
  27. package/dist/presentation/OutboundRouter.js +19 -1
  28. package/dist/presentation/OutboundRouter.js.map +1 -1
  29. package/dist/proxy/ProxySessionController.js +1012 -58
  30. package/dist/proxy/ProxySessionController.js.map +1 -1
  31. package/dist/startup/StartupNotifier.js +65 -16
  32. package/dist/startup/StartupNotifier.js.map +1 -1
  33. package/dist/startup/startFeishu.js +2 -2
  34. package/dist/startup/startFeishu.js.map +1 -1
  35. package/dist/state/StateStore.js +250 -67
  36. package/dist/state/StateStore.js.map +1 -1
  37. package/dist/state/migrations.js +26 -0
  38. package/dist/state/migrations.js.map +1 -1
  39. package/dist/supervision/SafeRestartNotifier.js +17 -16
  40. package/dist/supervision/SafeRestartNotifier.js.map +1 -1
  41. package/dist/supervision/SafeRestartScheduler.js +12 -7
  42. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  43. package/dist/supervision/replacementSupervisor.js +45 -4
  44. package/dist/supervision/replacementSupervisor.js.map +1 -1
  45. package/dist/supervisor.js +30 -18
  46. package/dist/supervisor.js.map +1 -1
  47. package/docs/technical-reference.md +18 -5
  48. package/docs/technical-reference.zh.md +18 -5
  49. package/npm-shrinkwrap.json +2 -2
  50. package/package.json +1 -1
  51. package/skills/agent-bot/SKILL.md +8 -2
  52. package/src/codex/CodexRuntime.ts +9 -3
  53. package/src/commands/CommandRouter.ts +13 -0
  54. package/src/commands/commandTypes.ts +2 -0
  55. package/src/config/loadConfig.ts +2 -0
  56. package/src/config/schema.ts +2 -0
  57. package/src/console/ConsoleTurnPresenter.ts +4 -1
  58. package/src/feishu/CardRenderer.ts +533 -9
  59. package/src/feishu/FeishuConnector.ts +44 -10
  60. package/src/feishu/FeishuMessageClient.ts +58 -0
  61. package/src/feishu/FeishuTurnPresenter.ts +35 -2
  62. package/src/feishu/ownerAccess.ts +7 -0
  63. package/src/feishu/types.ts +7 -0
  64. package/src/index.ts +20 -20
  65. package/src/presentation/OutboundRouter.ts +28 -3
  66. package/src/proxy/ProxySessionController.ts +1177 -57
  67. package/src/startup/StartupNotifier.ts +67 -17
  68. package/src/startup/startFeishu.ts +5 -3
  69. package/src/state/StateStore.ts +394 -65
  70. package/src/state/migrations.ts +26 -0
  71. package/src/supervision/SafeRestartNotifier.ts +17 -17
  72. package/src/supervision/SafeRestartScheduler.ts +13 -6
  73. package/src/supervision/replacementSupervisor.ts +50 -4
  74. package/src/supervisor.ts +35 -16
@@ -15,27 +15,68 @@ export class StartupNotifier {
15
15
  this.options = options;
16
16
  this.metadataHydrator = metadataHydrator;
17
17
  }
18
- async notify(startedAt, restartReason, restartGroupContextKeys = []) {
18
+ async notify(startedAt, restartReason, restartTargets = []) {
19
19
  let targets;
20
20
  try {
21
21
  const activeGroupSince = new Date(startedAt.getTime() - 60_000);
22
22
  const knownGroups = new Map(this.store.listChatContexts("group")
23
23
  .filter((chat) => !isThreadContextKey(chat.contextKey))
24
24
  .map((chat) => [chat.contextKey, chat]));
25
- const targetGroups = new Map(this.store.listRecentlyActiveChatContexts(activeGroupSince)
26
- .filter((chat) => chat.chatType === "group" && !isThreadContextKey(chat.contextKey))
27
- .map((chat) => [chat.contextKey, chat]));
28
- for (const contextKey of restartGroupContextKeys) {
29
- const baseContextKey = baseChatContextKey(contextKey.trim());
30
- const group = knownGroups.get(baseContextKey);
31
- if (group)
32
- targetGroups.set(baseContextKey, group);
25
+ const targetsByContext = new Map();
26
+ for (const chat of this.store.listChatContexts("p2p")) {
27
+ targetsByContext.set(chat.contextKey, {
28
+ contextKey: chat.contextKey,
29
+ context: this.store.getUserContext(chat.contextKey),
30
+ });
31
+ }
32
+ const recentGroups = this.store.listRecentlyActiveChatContexts(activeGroupSince)
33
+ .filter((chat) => chat.chatType === "group" && !isThreadContextKey(chat.contextKey));
34
+ for (const chat of recentGroups) {
35
+ targetsByContext.set(chat.contextKey, {
36
+ contextKey: chat.contextKey,
37
+ context: this.store.getUserContext(chat.contextKey),
38
+ });
39
+ }
40
+ const explicitBaseContextKeys = new Set(restartTargets
41
+ .map((target) => target.contextKey.trim())
42
+ .filter((contextKey) => contextKey && !isThreadContextKey(contextKey)));
43
+ for (const target of restartTargets) {
44
+ const contextKey = target.contextKey.trim();
45
+ if (!contextKey)
46
+ continue;
47
+ if (isThreadContextKey(contextKey)) {
48
+ const baseContextKey = baseChatContextKey(contextKey);
49
+ if (!knownGroups.has(baseContextKey))
50
+ continue;
51
+ const replyMessageId = target.replyMessageId?.trim();
52
+ if (replyMessageId) {
53
+ if (!explicitBaseContextKeys.has(baseContextKey)) {
54
+ targetsByContext.delete(baseContextKey);
55
+ }
56
+ targetsByContext.set(contextKey, {
57
+ contextKey,
58
+ replyMessageId,
59
+ context: this.store.getUserContext(contextKey),
60
+ });
61
+ }
62
+ else {
63
+ const group = knownGroups.get(baseContextKey);
64
+ targetsByContext.set(baseContextKey, {
65
+ contextKey: baseContextKey,
66
+ context: this.store.getUserContext(baseContextKey),
67
+ });
68
+ }
69
+ continue;
70
+ }
71
+ const group = knownGroups.get(contextKey);
72
+ if (group) {
73
+ targetsByContext.set(contextKey, {
74
+ contextKey,
75
+ context: this.store.getUserContext(contextKey),
76
+ });
77
+ }
33
78
  }
34
- const chats = [...this.store.listChatContexts("p2p"), ...targetGroups.values()];
35
- targets = chats.map((chat) => ({
36
- contextKey: chat.contextKey,
37
- context: this.store.getUserContext(chat.contextKey),
38
- }));
79
+ targets = [...targetsByContext.values()];
39
80
  }
40
81
  catch (error) {
41
82
  this.logger.warn({ error }, "Failed to load startup notification targets.");
@@ -47,7 +88,7 @@ export class StartupNotifier {
47
88
  return;
48
89
  targets = [{ contextKey: `open_id:${defaultUserOpenId}` }];
49
90
  }
50
- const deliveries = await Promise.all(targets.map(async ({ contextKey, context }) => {
91
+ const deliveries = await Promise.all(targets.map(async ({ contextKey, replyMessageId, context }) => {
51
92
  let session = context?.currentSessionId ? this.store.getSession(context.currentSessionId) : undefined;
52
93
  if (session && this.metadataHydrator) {
53
94
  try {
@@ -84,7 +125,15 @@ export class StartupNotifier {
84
125
  : undefined,
85
126
  });
86
127
  try {
87
- await this.outbound.sendInteractiveCard(contextKey, card);
128
+ if (replyMessageId) {
129
+ if (!this.outbound.replyInteractiveCard) {
130
+ throw new Error("The outbound transport cannot preserve the safe-restart topic.");
131
+ }
132
+ await this.outbound.replyInteractiveCard(contextKey, { messageId: replyMessageId, replyInThread: true }, card);
133
+ }
134
+ else {
135
+ await this.outbound.sendInteractiveCard(contextKey, card);
136
+ }
88
137
  return true;
89
138
  }
90
139
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"StartupNotifier.js","sourceRoot":"","sources":["../../src/startup/StartupNotifier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StartupNotifier.js","sourceRoot":"","sources":["../../src/startup/StartupNotifier.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- export async function startFeishu(connector, notifier, startedAt, restartReason, prepare, onConnected, restartGroupContextKeys = []) {
1
+ export async function startFeishu(connector, notifier, startedAt, restartReason, prepare, onConnected, restartTargets = []) {
2
2
  await prepare?.();
3
3
  await connector.start();
4
- await notifier.notify(startedAt, restartReason, restartGroupContextKeys);
4
+ await notifier.notify(startedAt, restartReason, restartTargets);
5
5
  onConnected?.();
6
6
  }
7
7
  //# sourceMappingURL=startFeishu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"startFeishu.js","sourceRoot":"","sources":["../../src/startup/startFeishu.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAmC,EACnC,QAAmC,EACnC,SAAe,EACf,aAAqB,EACrB,OAA6B,EAC7B,WAAwB,EACxB,uBAAuB,GAAa,EAAE;IAEtC,MAAM,OAAO,EAAE,EAAE,CAAC;IAClB,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;IACxB,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,uBAAuB,CAAC,CAAC;IACzE,WAAW,EAAE,EAAE,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"startFeishu.js","sourceRoot":"","sources":["../../src/startup/startFeishu.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAmC,EACnC,QAAmC,EACnC,SAAe,EACf,aAAqB,EACrB,OAA6B,EAC7B,WAAwB,EACxB,cAAc,GAAgC,EAAE;IAEhD,MAAM,OAAO,EAAE,EAAE,CAAC;IAClB,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;IACxB,MAAM,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC;IAChE,WAAW,EAAE,EAAE,CAAC;AAClB,CAAC"}
@@ -16,6 +16,7 @@ export class StateStore {
16
16
  this.ensureSessionColumns();
17
17
  this.ensureTurnSnapshotColumns();
18
18
  this.ensureChatContextColumns();
19
+ this.ensureTurnAttemptColumns();
19
20
  this.initializeContextSessionMappings();
20
21
  this.db.exec("DROP INDEX IF EXISTS idx_sessions_remote_session_id_unique");
21
22
  this.reconcileDuplicateRemoteSessions();
@@ -75,6 +76,27 @@ export class StateStore {
75
76
  updated_at = excluded.updated_at
76
77
  `).run(contextKey, chatType, now, now);
77
78
  }
79
+ getChatContext(contextKey) {
80
+ const row = this.db.prepare("SELECT * FROM chat_contexts WHERE context_key = ?")
81
+ .get(contextKey);
82
+ return row ? mapChatContext(row) : undefined;
83
+ }
84
+ setChatRequiresMention(contextKey, requiresMention) {
85
+ const now = new Date().toISOString();
86
+ this.db.prepare(`
87
+ UPDATE chat_contexts
88
+ SET require_mention = ?, updated_at = ?
89
+ WHERE context_key = ? AND chat_type = 'group'
90
+ `).run(requiresMention ? 1 : 0, now, contextKey);
91
+ }
92
+ chatRequiresMention(contextKey) {
93
+ const row = this.db.prepare(`
94
+ SELECT require_mention
95
+ FROM chat_contexts
96
+ WHERE context_key = ? AND chat_type = 'group'
97
+ `).get(contextKey);
98
+ return row?.require_mention === 1;
99
+ }
78
100
  listChatContexts(chatType) {
79
101
  const rows = chatType
80
102
  ? this.db.prepare(`
@@ -83,13 +105,7 @@ export class StateStore {
83
105
  ORDER BY updated_at DESC
84
106
  `).all(chatType)
85
107
  : this.db.prepare("SELECT * FROM chat_contexts ORDER BY updated_at DESC").all();
86
- return rows.map((row) => ({
87
- contextKey: row.context_key,
88
- chatType: row.chat_type,
89
- lastActivityAt: row.last_activity_at ?? undefined,
90
- createdAt: row.created_at,
91
- updatedAt: row.updated_at,
92
- }));
108
+ return rows.map(mapChatContext);
93
109
  }
94
110
  markChatActive(contextKey, activeAt = new Date()) {
95
111
  const timestamp = activeAt.toISOString();
@@ -105,13 +121,7 @@ export class StateStore {
105
121
  WHERE last_activity_at >= ?
106
122
  ORDER BY last_activity_at DESC
107
123
  `).all(since.toISOString());
108
- return rows.map((row) => ({
109
- contextKey: row.context_key,
110
- chatType: row.chat_type,
111
- lastActivityAt: row.last_activity_at ?? undefined,
112
- createdAt: row.created_at,
113
- updatedAt: row.updated_at,
114
- }));
124
+ return rows.map(mapChatContext);
115
125
  }
116
126
  enqueuePrompt(input) {
117
127
  const createdAt = new Date().toISOString();
@@ -177,6 +187,131 @@ export class StateStore {
177
187
  });
178
188
  return cancel();
179
189
  }
190
+ createTurnAttempt(input) {
191
+ const createdAt = input.createdAt ?? new Date().toISOString();
192
+ const updatedAt = input.updatedAt ?? createdAt;
193
+ const status = input.status ?? (input.turnId ? "running" : "accepted");
194
+ this.db.prepare(`
195
+ INSERT INTO turn_attempts (
196
+ attempt_id, local_session_id, context_key, prompt_text, local_image_paths_json,
197
+ message_id, reply_message_id, pending_turn_id, turn_id, recovered_from_turn_id,
198
+ recovery_count, retry_count, status, created_at, updated_at
199
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
200
+ `).run(input.attemptId, input.localSessionId, input.contextKey, input.promptText, JSON.stringify(input.localImagePaths ?? []), input.messageId ?? null, input.replyMessageId ?? null, input.pendingTurnId ?? null, input.turnId ?? null, input.recoveredFromTurnId ?? null, input.recoveryCount ?? 0, input.retryCount ?? 0, status, createdAt, updatedAt);
201
+ return this.getTurnAttempt(input.attemptId);
202
+ }
203
+ getTurnAttempt(attemptId) {
204
+ const row = this.db.prepare("SELECT * FROM turn_attempts WHERE attempt_id = ?")
205
+ .get(attemptId);
206
+ return row ? mapTurnAttempt(row) : undefined;
207
+ }
208
+ findIncompleteTurnAttemptForSession(localSessionId) {
209
+ const row = this.db.prepare(`
210
+ SELECT * FROM turn_attempts
211
+ WHERE local_session_id = ?
212
+ AND status IN ('accepted', 'running', 'recovering')
213
+ ORDER BY updated_at DESC, rowid DESC
214
+ LIMIT 1
215
+ `).get(localSessionId);
216
+ return row ? mapTurnAttempt(row) : undefined;
217
+ }
218
+ listIncompleteTurnAttempts() {
219
+ const rows = this.db.prepare(`
220
+ SELECT * FROM turn_attempts
221
+ WHERE status IN ('accepted', 'running', 'recovering')
222
+ ORDER BY created_at ASC, rowid ASC
223
+ `).all();
224
+ return rows.map(mapTurnAttempt);
225
+ }
226
+ updateTurnAttempt(attemptId, patch) {
227
+ const existing = this.getTurnAttempt(attemptId);
228
+ if (!existing)
229
+ return;
230
+ const value = (candidate, fallback) => candidate === undefined ? fallback ?? null : candidate;
231
+ this.db.prepare(`
232
+ UPDATE turn_attempts
233
+ SET message_id = ?, reply_message_id = ?, pending_turn_id = ?, turn_id = ?,
234
+ recovered_from_turn_id = ?, recovery_count = ?, retry_count = ?, status = ?, updated_at = ?
235
+ WHERE attempt_id = ?
236
+ `).run(value(patch.messageId, existing.messageId), value(patch.replyMessageId, existing.replyMessageId), value(patch.pendingTurnId, existing.pendingTurnId), value(patch.turnId, existing.turnId), value(patch.recoveredFromTurnId, existing.recoveredFromTurnId), patch.recoveryCount ?? existing.recoveryCount, patch.retryCount ?? existing.retryCount, patch.status ?? existing.status, new Date().toISOString(), attemptId);
237
+ }
238
+ bindTurnAttempt(localSessionId, turnId) {
239
+ const attempt = this.findIncompleteTurnAttemptForSession(localSessionId);
240
+ if (!attempt || (attempt.turnId && attempt.turnId !== turnId))
241
+ return undefined;
242
+ this.updateTurnAttempt(attempt.attemptId, { turnId, status: "running" });
243
+ return this.getTurnAttempt(attempt.attemptId);
244
+ }
245
+ markTurnAttemptTerminal(turnId, status) {
246
+ this.db.prepare(`
247
+ UPDATE turn_attempts
248
+ SET status = ?, updated_at = ?
249
+ WHERE turn_id = ?
250
+ `).run(status, new Date().toISOString(), turnId);
251
+ }
252
+ findIncompleteTurnAttemptByTurnId(turnId) {
253
+ const row = this.db.prepare(`
254
+ SELECT * FROM turn_attempts
255
+ WHERE turn_id = ? AND status IN ('accepted', 'running', 'recovering')
256
+ ORDER BY updated_at DESC, rowid DESC
257
+ LIMIT 1
258
+ `).get(turnId);
259
+ return row ? mapTurnAttempt(row) : undefined;
260
+ }
261
+ touchTurnAttempt(turnId, activityAt = new Date()) {
262
+ const timestamp = activityAt.toISOString();
263
+ const throttleCutoff = new Date(activityAt.getTime() - 15_000).toISOString();
264
+ this.db.prepare(`
265
+ UPDATE turn_attempts
266
+ SET updated_at = ?
267
+ WHERE turn_id = ?
268
+ AND status IN ('accepted', 'running', 'recovering')
269
+ AND updated_at < ?
270
+ `).run(timestamp, turnId, throttleCutoff);
271
+ }
272
+ prepareTurnAttemptRecovery(attemptId, recoveredFromTurnId) {
273
+ const existing = this.getTurnAttempt(attemptId);
274
+ if (!existing)
275
+ return undefined;
276
+ this.updateTurnAttempt(attemptId, {
277
+ pendingTurnId: null,
278
+ turnId: null,
279
+ recoveredFromTurnId: recoveredFromTurnId ?? existing.turnId ?? existing.recoveredFromTurnId ?? null,
280
+ recoveryCount: existing.recoveryCount + 1,
281
+ status: "recovering",
282
+ });
283
+ return this.getTurnAttempt(attemptId);
284
+ }
285
+ prepareTurnAttemptRetry(attemptId, failedTurnId) {
286
+ const existing = this.getTurnAttempt(attemptId);
287
+ if (!existing || existing.turnId !== failedTurnId || !isIncompleteTurnAttemptStatus(existing.status)) {
288
+ return undefined;
289
+ }
290
+ this.updateTurnAttempt(attemptId, {
291
+ pendingTurnId: null,
292
+ turnId: null,
293
+ recoveredFromTurnId: failedTurnId,
294
+ retryCount: existing.retryCount + 1,
295
+ status: "recovering",
296
+ });
297
+ return this.getTurnAttempt(attemptId);
298
+ }
299
+ rebindPendingTurnMessages(localSessionId, sourceTurnId, targetTurnId) {
300
+ const now = new Date().toISOString();
301
+ const rebind = this.db.transaction(() => {
302
+ this.db.prepare(`
303
+ UPDATE message_reactions
304
+ SET turn_id = ?, updated_at = ?
305
+ WHERE local_session_id = ? AND turn_id = ? AND status = 'pending'
306
+ `).run(targetTurnId, now, localSessionId, sourceTurnId);
307
+ this.db.prepare(`
308
+ UPDATE message_turn_bindings
309
+ SET turn_id = ?, updated_at = ?
310
+ WHERE local_session_id = ? AND turn_id = ?
311
+ `).run(targetTurnId, now, localSessionId, sourceTurnId);
312
+ });
313
+ rebind();
314
+ }
180
315
  nextForkTitle(sourceTitle) {
181
316
  const baseTitle = forkBaseTitle(sourceTitle);
182
317
  const allocate = this.db.transaction(() => {
@@ -193,59 +328,6 @@ export class StateStore {
193
328
  });
194
329
  return formatForkTitle(baseTitle, allocate());
195
330
  }
196
- reconcileInterruptedAcpSessions(acpAgentNames) {
197
- const agentClause = acpAgentNames.length
198
- ? ` OR (runtime_kind IS NULL AND agent_name IN (${acpAgentNames.map(() => "?").join(", ")}))`
199
- : "";
200
- const rows = this.db
201
- .prepare(`SELECT * FROM sessions WHERE status = 'running' AND (runtime_kind = 'acp'${agentClause})`)
202
- .all(...acpAgentNames);
203
- if (rows.length === 0)
204
- return [];
205
- const now = new Date().toISOString();
206
- const completedAt = Date.now();
207
- const reconcile = this.db.transaction(() => {
208
- for (const row of rows) {
209
- this.db.prepare(`
210
- UPDATE sessions
211
- SET status = 'failed',
212
- last_turn_status = CASE WHEN last_turn_id IS NULL THEN last_turn_status ELSE 'failed' END,
213
- updated_at = ?
214
- WHERE local_session_id = ?
215
- `).run(now, row.local_session_id);
216
- if (!row.last_turn_id)
217
- continue;
218
- const snapshotRow = this.db
219
- .prepare("SELECT snapshot_json FROM turn_snapshots WHERE turn_id = ?")
220
- .get(row.last_turn_id);
221
- if (!snapshotRow)
222
- continue;
223
- try {
224
- const snapshot = JSON.parse(snapshotRow.snapshot_json);
225
- if (!["starting", "running", "tool_running", "waiting_for_approval"].includes(String(snapshot.status)))
226
- continue;
227
- const startedAt = typeof snapshot.startedAt === "number" ? snapshot.startedAt : undefined;
228
- const failedSnapshot = {
229
- ...snapshot,
230
- status: "failed",
231
- completedAt,
232
- ...(startedAt === undefined ? {} : { durationMs: Math.max(0, completedAt - startedAt) }),
233
- error: "Agent Bot 已重启,原 ACP 进程中的执行无法继续。",
234
- };
235
- delete failedSnapshot.activeTool;
236
- delete failedSnapshot.approval;
237
- this.db.prepare(`
238
- UPDATE turn_snapshots SET snapshot_json = ?, updated_at = ? WHERE turn_id = ?
239
- `).run(JSON.stringify(failedSnapshot), now, row.last_turn_id);
240
- }
241
- catch {
242
- // Keep an unreadable historical snapshot intact while still correcting the session state.
243
- }
244
- }
245
- });
246
- reconcile();
247
- return rows.map(mapSession);
248
- }
249
331
  setDefaultAgent(contextKey, agentName) {
250
332
  const now = new Date().toISOString();
251
333
  this.db
@@ -369,6 +451,27 @@ export class StateStore {
369
451
  .get(turnId);
370
452
  return row ? JSON.parse(row.snapshot_json) : undefined;
371
453
  }
454
+ findLatestTurnSnapshotForSession(localSessionId) {
455
+ const row = this.db.prepare(`
456
+ SELECT turn_id, local_session_id, context_key, snapshot_json, updated_at
457
+ FROM turn_snapshots
458
+ WHERE local_session_id = ?
459
+ ORDER BY
460
+ coalesce(json_extract(snapshot_json, '$.startedAt'), 0) DESC,
461
+ updated_at DESC,
462
+ rowid DESC
463
+ LIMIT 1
464
+ `).get(localSessionId);
465
+ return row
466
+ ? {
467
+ turnId: row.turn_id,
468
+ localSessionId: row.local_session_id,
469
+ contextKey: row.context_key ?? undefined,
470
+ snapshot: JSON.parse(row.snapshot_json),
471
+ updatedAt: row.updated_at,
472
+ }
473
+ : undefined;
474
+ }
372
475
  saveTurnRuntimeOrigin(turnId, localSessionId, agentName, remoteSessionId) {
373
476
  this.db.prepare(`
374
477
  INSERT OR IGNORE INTO turn_runtime_origins (
@@ -642,6 +745,41 @@ export class StateStore {
642
745
  .all();
643
746
  return rows.map(mapSession);
644
747
  }
748
+ listUndeliveredCompletedTurns() {
749
+ const rows = this.db.prepare(`
750
+ SELECT snapshots.turn_id, snapshots.local_session_id, snapshots.context_key
751
+ FROM turn_snapshots AS snapshots
752
+ LEFT JOIN turn_deliveries AS deliveries ON deliveries.turn_id = snapshots.turn_id
753
+ WHERE json_extract(snapshots.snapshot_json, '$.status') = 'completed'
754
+ AND length(coalesce(json_extract(snapshots.snapshot_json, '$.finalResponse'), '')) > 0
755
+ AND deliveries.final_delivered_at IS NULL
756
+ ORDER BY snapshots.updated_at ASC
757
+ `).all();
758
+ return rows.map((row) => ({
759
+ turnId: row.turn_id,
760
+ localSessionId: row.local_session_id,
761
+ contextKey: row.context_key ?? undefined,
762
+ }));
763
+ }
764
+ findMessageIdForTurn(turnId) {
765
+ const binding = this.db.prepare(`
766
+ SELECT message_id
767
+ FROM message_turn_bindings
768
+ WHERE turn_id = ?
769
+ ORDER BY updated_at DESC, rowid DESC
770
+ LIMIT 1
771
+ `).get(turnId);
772
+ if (binding)
773
+ return binding.message_id;
774
+ const reaction = this.db.prepare(`
775
+ SELECT message_id
776
+ FROM message_reactions
777
+ WHERE turn_id = ?
778
+ ORDER BY updated_at DESC, rowid DESC
779
+ LIMIT 1
780
+ `).get(turnId);
781
+ return reaction?.message_id;
782
+ }
645
783
  findLatestMessageIdForSession(localSessionId, contextKey) {
646
784
  const row = this.db.prepare(`
647
785
  SELECT message_id
@@ -991,6 +1129,15 @@ export class StateStore {
991
1129
  if (!existing.has("last_activity_at")) {
992
1130
  this.db.exec("ALTER TABLE chat_contexts ADD COLUMN last_activity_at TEXT");
993
1131
  }
1132
+ if (!existing.has("require_mention")) {
1133
+ this.db.exec("ALTER TABLE chat_contexts ADD COLUMN require_mention INTEGER NOT NULL DEFAULT 0");
1134
+ }
1135
+ }
1136
+ ensureTurnAttemptColumns() {
1137
+ const existing = new Set(this.db.pragma("table_info(turn_attempts)").map((column) => column.name));
1138
+ if (!existing.has("retry_count")) {
1139
+ this.db.exec("ALTER TABLE turn_attempts ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0");
1140
+ }
994
1141
  }
995
1142
  }
996
1143
  const MAX_TASK_TITLE_LENGTH = 120;
@@ -1018,6 +1165,16 @@ function mapUserContext(row) {
1018
1165
  updatedAt: row.updated_at,
1019
1166
  };
1020
1167
  }
1168
+ function mapChatContext(row) {
1169
+ return {
1170
+ contextKey: row.context_key,
1171
+ chatType: row.chat_type,
1172
+ requiresMention: row.require_mention === 1,
1173
+ lastActivityAt: row.last_activity_at ?? undefined,
1174
+ createdAt: row.created_at,
1175
+ updatedAt: row.updated_at,
1176
+ };
1177
+ }
1021
1178
  function mapSession(row) {
1022
1179
  return {
1023
1180
  localSessionId: row.local_session_id,
@@ -1068,4 +1225,30 @@ function mapQueuedPrompt(row) {
1068
1225
  createdAt: row.created_at,
1069
1226
  };
1070
1227
  }
1228
+ function mapTurnAttempt(row) {
1229
+ const parsedPaths = JSON.parse(row.local_image_paths_json);
1230
+ const localImagePaths = Array.isArray(parsedPaths)
1231
+ ? parsedPaths.filter((value) => typeof value === "string")
1232
+ : [];
1233
+ return {
1234
+ attemptId: row.attempt_id,
1235
+ localSessionId: row.local_session_id,
1236
+ contextKey: row.context_key,
1237
+ promptText: row.prompt_text,
1238
+ localImagePaths: localImagePaths.length > 0 ? localImagePaths : undefined,
1239
+ messageId: row.message_id ?? undefined,
1240
+ replyMessageId: row.reply_message_id ?? undefined,
1241
+ pendingTurnId: row.pending_turn_id ?? undefined,
1242
+ turnId: row.turn_id ?? undefined,
1243
+ recoveredFromTurnId: row.recovered_from_turn_id ?? undefined,
1244
+ recoveryCount: row.recovery_count,
1245
+ retryCount: row.retry_count,
1246
+ status: row.status,
1247
+ createdAt: row.created_at,
1248
+ updatedAt: row.updated_at,
1249
+ };
1250
+ }
1251
+ function isIncompleteTurnAttemptStatus(status) {
1252
+ return status === "accepted" || status === "running" || status === "recovering";
1253
+ }
1071
1254
  //# sourceMappingURL=StateStore.js.map