@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.
Files changed (113) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +82 -55
  3. package/README.zh.md +82 -55
  4. package/config.example.yaml +5 -1
  5. package/dist/cli/AutostartManager.js +431 -0
  6. package/dist/cli/AutostartManager.js.map +1 -0
  7. package/dist/cli/FeishuAppConfiguration.js +17 -13
  8. package/dist/cli/FeishuAppConfiguration.js.map +1 -1
  9. package/dist/cli/Initializer.js +35 -0
  10. package/dist/cli/Initializer.js.map +1 -1
  11. package/dist/cli/SelfUpdateRunner.js +552 -0
  12. package/dist/cli/SelfUpdateRunner.js.map +1 -0
  13. package/dist/cli/SelfUpdater.js +431 -0
  14. package/dist/cli/SelfUpdater.js.map +1 -0
  15. package/dist/cli/autostartOutput.js +63 -0
  16. package/dist/cli/autostartOutput.js.map +1 -0
  17. package/dist/cli/controlProtocol.js.map +1 -1
  18. package/dist/cli/help.js +92 -18
  19. package/dist/cli/help.js.map +1 -1
  20. package/dist/cli/taskGroupOptions.js +14 -11
  21. package/dist/cli/taskGroupOptions.js.map +1 -1
  22. package/dist/cli/taskListOutput.js +3 -0
  23. package/dist/cli/taskListOutput.js.map +1 -1
  24. package/dist/cli/taskTarget.js +99 -0
  25. package/dist/cli/taskTarget.js.map +1 -0
  26. package/dist/cli.js +643 -58
  27. package/dist/cli.js.map +1 -1
  28. package/dist/codex/CodexRuntime.js +16 -5
  29. package/dist/codex/CodexRuntime.js.map +1 -1
  30. package/dist/commands/CommandRouter.js +20 -1
  31. package/dist/commands/CommandRouter.js.map +1 -1
  32. package/dist/config/loadConfig.js +2 -0
  33. package/dist/config/loadConfig.js.map +1 -1
  34. package/dist/config/schema.js +2 -0
  35. package/dist/config/schema.js.map +1 -1
  36. package/dist/console/ConsoleTurnPresenter.js +2 -0
  37. package/dist/console/ConsoleTurnPresenter.js.map +1 -1
  38. package/dist/feishu/CardRenderer.js +584 -16
  39. package/dist/feishu/CardRenderer.js.map +1 -1
  40. package/dist/feishu/FeishuConnector.js +29 -8
  41. package/dist/feishu/FeishuConnector.js.map +1 -1
  42. package/dist/feishu/FeishuMarkdown.js +25 -1
  43. package/dist/feishu/FeishuMarkdown.js.map +1 -1
  44. package/dist/feishu/FeishuMessageClient.js +43 -0
  45. package/dist/feishu/FeishuMessageClient.js.map +1 -1
  46. package/dist/feishu/FeishuTurnPresenter.js +29 -1
  47. package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
  48. package/dist/feishu/ownerAccess.js +7 -0
  49. package/dist/feishu/ownerAccess.js.map +1 -0
  50. package/dist/index.js +148 -19
  51. package/dist/index.js.map +1 -1
  52. package/dist/presentation/OutboundRouter.js +19 -1
  53. package/dist/presentation/OutboundRouter.js.map +1 -1
  54. package/dist/proxy/ProxySessionController.js +1444 -66
  55. package/dist/proxy/ProxySessionController.js.map +1 -1
  56. package/dist/startup/StartupNotifier.js +65 -16
  57. package/dist/startup/StartupNotifier.js.map +1 -1
  58. package/dist/startup/startFeishu.js +2 -2
  59. package/dist/startup/startFeishu.js.map +1 -1
  60. package/dist/state/StateStore.js +268 -67
  61. package/dist/state/StateStore.js.map +1 -1
  62. package/dist/state/migrations.js +26 -0
  63. package/dist/state/migrations.js.map +1 -1
  64. package/dist/supervision/SafeRestartNotifier.js +17 -16
  65. package/dist/supervision/SafeRestartNotifier.js.map +1 -1
  66. package/dist/supervision/SafeRestartScheduler.js +12 -7
  67. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  68. package/dist/supervision/replacementSupervisor.js +45 -4
  69. package/dist/supervision/replacementSupervisor.js.map +1 -1
  70. package/dist/supervisor.js +30 -18
  71. package/dist/supervisor.js.map +1 -1
  72. package/docs/technical-reference.md +33 -10
  73. package/docs/technical-reference.zh.md +33 -10
  74. package/npm-shrinkwrap.json +2 -2
  75. package/package.json +2 -2
  76. package/skills/agent-bot/SKILL.md +107 -182
  77. package/src/cli/AutostartManager.ts +579 -0
  78. package/src/cli/FeishuAppConfiguration.ts +22 -7
  79. package/src/cli/Initializer.ts +57 -0
  80. package/src/cli/SelfUpdateRunner.ts +817 -0
  81. package/src/cli/SelfUpdater.ts +688 -0
  82. package/src/cli/autostartOutput.ts +75 -0
  83. package/src/cli/controlProtocol.ts +111 -1
  84. package/src/cli/help.ts +92 -18
  85. package/src/cli/taskGroupOptions.ts +27 -17
  86. package/src/cli/taskListOutput.ts +10 -0
  87. package/src/cli/taskTarget.ts +168 -0
  88. package/src/cli.ts +781 -65
  89. package/src/codex/CodexRuntime.ts +17 -5
  90. package/src/commands/CommandRouter.ts +19 -1
  91. package/src/commands/commandTypes.ts +3 -0
  92. package/src/config/loadConfig.ts +2 -0
  93. package/src/config/schema.ts +2 -0
  94. package/src/console/ConsoleTurnPresenter.ts +4 -1
  95. package/src/feishu/CardRenderer.ts +716 -16
  96. package/src/feishu/FeishuConnector.ts +44 -10
  97. package/src/feishu/FeishuMarkdown.ts +27 -1
  98. package/src/feishu/FeishuMessageClient.ts +58 -0
  99. package/src/feishu/FeishuTurnPresenter.ts +35 -2
  100. package/src/feishu/ownerAccess.ts +7 -0
  101. package/src/feishu/types.ts +7 -0
  102. package/src/index.ts +192 -22
  103. package/src/presentation/OutboundRouter.ts +28 -3
  104. package/src/proxy/ProxySessionController.ts +1707 -70
  105. package/src/runtime/types.ts +1 -0
  106. package/src/startup/StartupNotifier.ts +67 -17
  107. package/src/startup/startFeishu.ts +5 -3
  108. package/src/state/StateStore.ts +413 -65
  109. package/src/state/migrations.ts +26 -0
  110. package/src/supervision/SafeRestartNotifier.ts +17 -17
  111. package/src/supervision/SafeRestartScheduler.ts +13 -6
  112. package/src/supervision/replacementSupervisor.ts +50 -4
  113. 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
@@ -317,6 +399,24 @@ export class StateStore {
317
399
  `)
318
400
  .run(patch.acpSessionId ?? existing.acpSessionId ?? null, patch.status ?? existing.status, now, localSessionId);
319
401
  }
402
+ archiveSession(localSessionId) {
403
+ const now = new Date().toISOString();
404
+ const archive = this.db.transaction(() => {
405
+ this.db.prepare(`
406
+ UPDATE sessions
407
+ SET status = 'closed', updated_at = ?
408
+ WHERE local_session_id = ?
409
+ `).run(now, localSessionId);
410
+ this.db.prepare(`
411
+ UPDATE user_contexts
412
+ SET current_session_id = CASE WHEN current_session_id = ? THEN NULL ELSE current_session_id END,
413
+ previous_session_id = CASE WHEN previous_session_id = ? THEN NULL ELSE previous_session_id END,
414
+ updated_at = ?
415
+ WHERE current_session_id = ? OR previous_session_id = ?
416
+ `).run(localSessionId, localSessionId, now, localSessionId, localSessionId);
417
+ });
418
+ archive();
419
+ }
320
420
  updateRuntimeSession(localSessionId, patch) {
321
421
  const existing = this.getSession(localSessionId);
322
422
  if (!existing) {
@@ -369,6 +469,27 @@ export class StateStore {
369
469
  .get(turnId);
370
470
  return row ? JSON.parse(row.snapshot_json) : undefined;
371
471
  }
472
+ findLatestTurnSnapshotForSession(localSessionId) {
473
+ const row = this.db.prepare(`
474
+ SELECT turn_id, local_session_id, context_key, snapshot_json, updated_at
475
+ FROM turn_snapshots
476
+ WHERE local_session_id = ?
477
+ ORDER BY
478
+ coalesce(json_extract(snapshot_json, '$.startedAt'), 0) DESC,
479
+ updated_at DESC,
480
+ rowid DESC
481
+ LIMIT 1
482
+ `).get(localSessionId);
483
+ return row
484
+ ? {
485
+ turnId: row.turn_id,
486
+ localSessionId: row.local_session_id,
487
+ contextKey: row.context_key ?? undefined,
488
+ snapshot: JSON.parse(row.snapshot_json),
489
+ updatedAt: row.updated_at,
490
+ }
491
+ : undefined;
492
+ }
372
493
  saveTurnRuntimeOrigin(turnId, localSessionId, agentName, remoteSessionId) {
373
494
  this.db.prepare(`
374
495
  INSERT OR IGNORE INTO turn_runtime_origins (
@@ -642,6 +763,41 @@ export class StateStore {
642
763
  .all();
643
764
  return rows.map(mapSession);
644
765
  }
766
+ listUndeliveredCompletedTurns() {
767
+ const rows = this.db.prepare(`
768
+ SELECT snapshots.turn_id, snapshots.local_session_id, snapshots.context_key
769
+ FROM turn_snapshots AS snapshots
770
+ LEFT JOIN turn_deliveries AS deliveries ON deliveries.turn_id = snapshots.turn_id
771
+ WHERE json_extract(snapshots.snapshot_json, '$.status') = 'completed'
772
+ AND length(coalesce(json_extract(snapshots.snapshot_json, '$.finalResponse'), '')) > 0
773
+ AND deliveries.final_delivered_at IS NULL
774
+ ORDER BY snapshots.updated_at ASC
775
+ `).all();
776
+ return rows.map((row) => ({
777
+ turnId: row.turn_id,
778
+ localSessionId: row.local_session_id,
779
+ contextKey: row.context_key ?? undefined,
780
+ }));
781
+ }
782
+ findMessageIdForTurn(turnId) {
783
+ const binding = this.db.prepare(`
784
+ SELECT message_id
785
+ FROM message_turn_bindings
786
+ WHERE turn_id = ?
787
+ ORDER BY updated_at DESC, rowid DESC
788
+ LIMIT 1
789
+ `).get(turnId);
790
+ if (binding)
791
+ return binding.message_id;
792
+ const reaction = this.db.prepare(`
793
+ SELECT message_id
794
+ FROM message_reactions
795
+ WHERE turn_id = ?
796
+ ORDER BY updated_at DESC, rowid DESC
797
+ LIMIT 1
798
+ `).get(turnId);
799
+ return reaction?.message_id;
800
+ }
645
801
  findLatestMessageIdForSession(localSessionId, contextKey) {
646
802
  const row = this.db.prepare(`
647
803
  SELECT message_id
@@ -991,6 +1147,15 @@ export class StateStore {
991
1147
  if (!existing.has("last_activity_at")) {
992
1148
  this.db.exec("ALTER TABLE chat_contexts ADD COLUMN last_activity_at TEXT");
993
1149
  }
1150
+ if (!existing.has("require_mention")) {
1151
+ this.db.exec("ALTER TABLE chat_contexts ADD COLUMN require_mention INTEGER NOT NULL DEFAULT 0");
1152
+ }
1153
+ }
1154
+ ensureTurnAttemptColumns() {
1155
+ const existing = new Set(this.db.pragma("table_info(turn_attempts)").map((column) => column.name));
1156
+ if (!existing.has("retry_count")) {
1157
+ this.db.exec("ALTER TABLE turn_attempts ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0");
1158
+ }
994
1159
  }
995
1160
  }
996
1161
  const MAX_TASK_TITLE_LENGTH = 120;
@@ -1018,6 +1183,16 @@ function mapUserContext(row) {
1018
1183
  updatedAt: row.updated_at,
1019
1184
  };
1020
1185
  }
1186
+ function mapChatContext(row) {
1187
+ return {
1188
+ contextKey: row.context_key,
1189
+ chatType: row.chat_type,
1190
+ requiresMention: row.require_mention === 1,
1191
+ lastActivityAt: row.last_activity_at ?? undefined,
1192
+ createdAt: row.created_at,
1193
+ updatedAt: row.updated_at,
1194
+ };
1195
+ }
1021
1196
  function mapSession(row) {
1022
1197
  return {
1023
1198
  localSessionId: row.local_session_id,
@@ -1068,4 +1243,30 @@ function mapQueuedPrompt(row) {
1068
1243
  createdAt: row.created_at,
1069
1244
  };
1070
1245
  }
1246
+ function mapTurnAttempt(row) {
1247
+ const parsedPaths = JSON.parse(row.local_image_paths_json);
1248
+ const localImagePaths = Array.isArray(parsedPaths)
1249
+ ? parsedPaths.filter((value) => typeof value === "string")
1250
+ : [];
1251
+ return {
1252
+ attemptId: row.attempt_id,
1253
+ localSessionId: row.local_session_id,
1254
+ contextKey: row.context_key,
1255
+ promptText: row.prompt_text,
1256
+ localImagePaths: localImagePaths.length > 0 ? localImagePaths : undefined,
1257
+ messageId: row.message_id ?? undefined,
1258
+ replyMessageId: row.reply_message_id ?? undefined,
1259
+ pendingTurnId: row.pending_turn_id ?? undefined,
1260
+ turnId: row.turn_id ?? undefined,
1261
+ recoveredFromTurnId: row.recovered_from_turn_id ?? undefined,
1262
+ recoveryCount: row.recovery_count,
1263
+ retryCount: row.retry_count,
1264
+ status: row.status,
1265
+ createdAt: row.created_at,
1266
+ updatedAt: row.updated_at,
1267
+ };
1268
+ }
1269
+ function isIncompleteTurnAttemptStatus(status) {
1270
+ return status === "accepted" || status === "running" || status === "recovering";
1271
+ }
1071
1272
  //# sourceMappingURL=StateStore.js.map