@keyou007/agent-bot 0.1.17-alpha.2 → 0.1.18-alpha.0

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.
@@ -125,6 +125,7 @@ const SESSION_PROMPT_PREVIEW_LENGTH = 50;
125
125
  const DIRECTORY_PAGE_SIZE = 15;
126
126
  const RESET_HISTORY_PAGE_SIZE = 10;
127
127
  const MAX_LLM_TURN_RETRIES = 3;
128
+ const QUEUED_PROMPT_RETRY_DELAY_MS = 5_000;
128
129
  const RECOVERY_ACTIVITY_WINDOW_MS = 10 * 60 * 1_000;
129
130
  const RECOVERY_HEARTBEAT_INTERVAL_MS = 60 * 1_000;
130
131
  const SHELL_COMMAND_JOB_POLL_INTERVAL_MS = 1_000;
@@ -132,7 +133,7 @@ const SHELL_COMMAND_JOB_RELAUNCH_DELAY_MS = 5_000;
132
133
  const FORWARD_ATTACHMENT_WINDOW_MS = 800;
133
134
  const DEFAULT_MERGED_FORWARD_INSTRUCTION = "请参考以下内容回复用户";
134
135
  const DEFAULT_REFERENCED_MESSAGE_INSTRUCTION = "请参考引用消息回复用户";
135
- const TURN_STOP_REQUESTED_MESSAGE = "正在停止当前任务,请稍候。状态卡片将在任务停止后自动更新。";
136
+ const TURN_STOP_REQUESTED_MESSAGE = "已发送停止信号,任务会自动停止。";
136
137
  const REMOTE_SESSION_REFERENCE_PREFIX = "agent-runtime:";
137
138
  const WINDOWS_DRIVES_DIRECTORY = "agentbot://windows-drives";
138
139
  const IMAGE_FILE_EXTENSIONS = new Set([
@@ -428,6 +429,7 @@ export class ProxySessionController {
428
429
  private readonly threadInitializations = new Map<string, Promise<void>>();
429
430
  private readonly llmRetryingSessions = new Set<string>();
430
431
  private readonly retriedFailureTurnIds = new Set<string>();
432
+ private readonly queuedPromptRetryTimers = new Map<string, NodeJS.Timeout>();
431
433
  private readonly recoveryRetryTimers = new Map<string, NodeJS.Timeout>();
432
434
  private readonly pendingMergedForwards = new Map<string, PendingForwardAttachment>();
433
435
  private readonly pendingResourceForwards = new Map<string, PendingForwardAttachment>();
@@ -1097,6 +1099,8 @@ export class ProxySessionController {
1097
1099
  for (const pending of this.pendingResourceForwards.values()) pending.resolveAttachment(undefined);
1098
1100
  this.pendingResourceForwards.clear();
1099
1101
  this.relatedReactionMessageIds.clear();
1102
+ for (const timer of this.queuedPromptRetryTimers.values()) clearTimeout(timer);
1103
+ this.queuedPromptRetryTimers.clear();
1100
1104
  for (const timer of this.recoveryRetryTimers.values()) clearTimeout(timer);
1101
1105
  this.recoveryRetryTimers.clear();
1102
1106
  for (const [jobId] of this.shellCommandJobMonitors) this.stopShellCommandJobMonitor(jobId);
@@ -1753,7 +1757,7 @@ export class ProxySessionController {
1753
1757
  }
1754
1758
  switch (command.type) {
1755
1759
  case "shell":
1756
- await this.runShellCommand(contextKey, command.command, messageId);
1760
+ await this.runShellCommand(contextKey, command.command, messageId, incomingMessage);
1757
1761
  return;
1758
1762
  case "dir":
1759
1763
  await this.openDirectoryBrowser(contextKey, command.directory);
@@ -3993,6 +3997,11 @@ export class ProxySessionController {
3993
3997
  }
3994
3998
 
3995
3999
  private scheduleNextQueuedPrompt(sessionId: string): Promise<void> {
4000
+ const retryTimer = this.queuedPromptRetryTimers.get(sessionId);
4001
+ if (retryTimer) {
4002
+ clearTimeout(retryTimer);
4003
+ this.queuedPromptRetryTimers.delete(sessionId);
4004
+ }
3996
4005
  const previous = this.queuedPromptStarts.get(sessionId) ?? Promise.resolve();
3997
4006
  const next = previous.catch(() => undefined).then(() => this.startNextQueuedPromptIfIdle(sessionId));
3998
4007
  this.queuedPromptStarts.set(sessionId, next);
@@ -4001,6 +4010,23 @@ export class ProxySessionController {
4001
4010
  });
4002
4011
  }
4003
4012
 
4013
+ private scheduleQueuedPromptRetry(sessionId: string): void {
4014
+ if (this.queuedPromptRetryTimers.has(sessionId)) return;
4015
+ if (this.store.countQueuedPrompts(sessionId) === 0) return;
4016
+ const timer = setTimeout(() => {
4017
+ this.queuedPromptRetryTimers.delete(sessionId);
4018
+ void this.scheduleNextQueuedPrompt(sessionId).catch((error: unknown) => {
4019
+ this.logger.warn(
4020
+ { error, errorMessage: runtimeErrorMessage(error), sessionId },
4021
+ "Failed to retry queued prompt start.",
4022
+ );
4023
+ this.scheduleQueuedPromptRetry(sessionId);
4024
+ });
4025
+ }, QUEUED_PROMPT_RETRY_DELAY_MS);
4026
+ timer.unref?.();
4027
+ this.queuedPromptRetryTimers.set(sessionId, timer);
4028
+ }
4029
+
4004
4030
  private async startNextQueuedPromptIfIdle(sessionId: string): Promise<void> {
4005
4031
  if (this.store.countQueuedPrompts(sessionId) === 0) return;
4006
4032
  const baseRecord = this.store.getSession(sessionId);
@@ -4028,10 +4054,22 @@ export class ProxySessionController {
4028
4054
  );
4029
4055
  if (prompt.messageId) await this.bindMessageReactionToTurn(prompt.messageId, sessionId, turnId);
4030
4056
  } catch (error) {
4031
- this.logger.warn({ error, sessionId }, "Failed to start queued prompt.");
4032
- if (prompt?.messageId) await this.finalizeStandaloneMessageReaction(prompt.messageId, "failed");
4033
- if (prompt) await this.sendError(prompt.contextKey, error);
4034
- if (prompt && this.store.countQueuedPrompts(sessionId) > 0) {
4057
+ this.logger.warn(
4058
+ {
4059
+ error,
4060
+ errorMessage: runtimeErrorMessage(error),
4061
+ phase: prompt ? "start" : "preflight",
4062
+ sessionId,
4063
+ },
4064
+ "Failed to start queued prompt.",
4065
+ );
4066
+ if (!prompt) {
4067
+ this.scheduleQueuedPromptRetry(sessionId);
4068
+ return;
4069
+ }
4070
+ if (prompt.messageId) await this.finalizeStandaloneMessageReaction(prompt.messageId, "failed");
4071
+ await this.sendError(prompt.contextKey, error);
4072
+ if (this.store.countQueuedPrompts(sessionId) > 0) {
4035
4073
  queueMicrotask(() => void this.scheduleNextQueuedPrompt(sessionId));
4036
4074
  }
4037
4075
  }
@@ -4925,8 +4963,9 @@ export class ProxySessionController {
4925
4963
  contextKey: string,
4926
4964
  command: string,
4927
4965
  sourceMessageId?: string,
4966
+ incomingMessage?: IncomingMessage,
4928
4967
  ): Promise<void> {
4929
- const cwd = this.currentSession(contextKey)?.cwd ?? this.config.defaults.cwd;
4968
+ const cwd = this.shellCommandCwd(contextKey, incomingMessage);
4930
4969
  const created = await this.shellCommandJobs.createJob({ contextKey, sourceMessageId, command, cwd });
4931
4970
  let cardMessageId: string | undefined;
4932
4971
  const initialState: ShellCommandCardView = {
@@ -4972,6 +5011,28 @@ export class ProxySessionController {
4972
5011
  }
4973
5012
  }
4974
5013
 
5014
+ private shellCommandCwd(contextKey: string, incomingMessage?: IncomingMessage): string {
5015
+ const current = this.currentSession(contextKey);
5016
+ if (current) return current.cwd;
5017
+
5018
+ if (incomingMessage?.threadContext) {
5019
+ const anchor = threadForkAnchorMessageIds(incomingMessage)
5020
+ .map((messageId) => this.store.findTurnAnchorByMessageId(messageId))
5021
+ .find((candidate) => candidate !== undefined);
5022
+ const source = anchor?.contextKey
5023
+ ? this.store.getSessionForContext(anchor.localSessionId, anchor.contextKey)
5024
+ : anchor
5025
+ ? this.store.getSession(anchor.localSessionId)
5026
+ : undefined;
5027
+ const sourceContextKey = anchor?.contextKey ?? source?.contextKey;
5028
+ if (source && sourceContextKey && baseChatContextKey(sourceContextKey) === baseChatContextKey(contextKey)) {
5029
+ return source.cwd;
5030
+ }
5031
+ }
5032
+
5033
+ return this.config.defaults.cwd;
5034
+ }
5035
+
4975
5036
  private monitorShellCommandJob(
4976
5037
  job: ShellCommandJobSnapshot,
4977
5038
  seededState?: ShellCommandCardView,
@@ -48,8 +48,13 @@ export class StartupNotifier {
48
48
  .filter((chat) => !isThreadContextKey(chat.contextKey))
49
49
  .map((chat) => [chat.contextKey, chat]),
50
50
  );
51
+ const knownPrivateChats = new Map(
52
+ this.store.listChatContexts("p2p")
53
+ .filter((chat) => !isThreadContextKey(chat.contextKey))
54
+ .map((chat) => [chat.contextKey, chat]),
55
+ );
51
56
  const targetsByContext = new Map<string, StartupNotificationTarget>();
52
- for (const chat of this.store.listChatContexts("p2p")) {
57
+ for (const chat of knownPrivateChats.values()) {
53
58
  targetsByContext.set(chat.contextKey, {
54
59
  contextKey: chat.contextKey,
55
60
  context: this.store.getUserContext(chat.contextKey),
@@ -71,10 +76,11 @@ export class StartupNotifier {
71
76
  if (!contextKey) continue;
72
77
  if (isThreadContextKey(contextKey)) {
73
78
  const baseContextKey = baseChatContextKey(contextKey);
74
- if (!knownGroups.has(baseContextKey)) continue;
79
+ const parentChat = knownGroups.get(baseContextKey) ?? knownPrivateChats.get(baseContextKey);
80
+ if (!parentChat) continue;
75
81
  const replyMessageId = target.replyMessageId?.trim();
76
82
  if (replyMessageId) {
77
- if (!explicitBaseContextKeys.has(baseContextKey)) {
83
+ if (parentChat.chatType === "group" && !explicitBaseContextKeys.has(baseContextKey)) {
78
84
  targetsByContext.delete(baseContextKey);
79
85
  }
80
86
  targetsByContext.set(contextKey, {
@@ -83,7 +89,6 @@ export class StartupNotifier {
83
89
  context: this.store.getUserContext(contextKey),
84
90
  });
85
91
  } else {
86
- const group = knownGroups.get(baseContextKey)!;
87
92
  targetsByContext.set(baseContextKey, {
88
93
  contextKey: baseContextKey,
89
94
  context: this.store.getUserContext(baseContextKey),