@keyou007/agent-bot 0.1.13-alpha.0 → 0.1.13-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 (97) hide show
  1. package/.env.example +2 -2
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +107 -98
  4. package/README.zh.md +81 -72
  5. package/dist/acp/AcpProcessManager.js +8 -3
  6. package/dist/acp/AcpProcessManager.js.map +1 -1
  7. package/dist/acp/AcpSessionManager.js +15 -0
  8. package/dist/acp/AcpSessionManager.js.map +1 -1
  9. package/dist/cli/ServerStarter.js +17 -2
  10. package/dist/cli/ServerStarter.js.map +1 -1
  11. package/dist/cli/controlProtocol.js.map +1 -1
  12. package/dist/cli/help.js +16 -4
  13. package/dist/cli/help.js.map +1 -1
  14. package/dist/cli/serverStatus.js +27 -0
  15. package/dist/cli/serverStatus.js.map +1 -1
  16. package/dist/cli/taskListOutput.js +72 -0
  17. package/dist/cli/taskListOutput.js.map +1 -0
  18. package/dist/cli.js +88 -54
  19. package/dist/cli.js.map +1 -1
  20. package/dist/codex/CodexProcessManager.js +34 -4
  21. package/dist/codex/CodexProcessManager.js.map +1 -1
  22. package/dist/codex/CodexRuntime.js +3 -0
  23. package/dist/codex/CodexRuntime.js.map +1 -1
  24. package/dist/commands/CommandRouter.js +5 -0
  25. package/dist/commands/CommandRouter.js.map +1 -1
  26. package/dist/config/FeishuUserOpenIdStore.js +76 -0
  27. package/dist/config/FeishuUserOpenIdStore.js.map +1 -0
  28. package/dist/config/loadConfig.js +3 -0
  29. package/dist/config/loadConfig.js.map +1 -1
  30. package/dist/feishu/CardRenderer.js +123 -22
  31. package/dist/feishu/CardRenderer.js.map +1 -1
  32. package/dist/feishu/FeishuConnector.js +16 -5
  33. package/dist/feishu/FeishuConnector.js.map +1 -1
  34. package/dist/index.js +125 -26
  35. package/dist/index.js.map +1 -1
  36. package/dist/presentation/turnGraph.js +36 -0
  37. package/dist/presentation/turnGraph.js.map +1 -0
  38. package/dist/proxy/ProxySessionController.js +425 -104
  39. package/dist/proxy/ProxySessionController.js.map +1 -1
  40. package/dist/runtime/AcpRuntimeAdapter.js +3 -0
  41. package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
  42. package/dist/runtime/agentEnvironment.js +24 -4
  43. package/dist/runtime/agentEnvironment.js.map +1 -1
  44. package/dist/runtime/createAgentRuntimeRegistry.js +7 -3
  45. package/dist/runtime/createAgentRuntimeRegistry.js.map +1 -1
  46. package/dist/startup/StartupNotifier.js +16 -7
  47. package/dist/startup/StartupNotifier.js.map +1 -1
  48. package/dist/startup/startFeishu.js +2 -2
  49. package/dist/startup/startFeishu.js.map +1 -1
  50. package/dist/state/StateStore.js +172 -0
  51. package/dist/state/StateStore.js.map +1 -1
  52. package/dist/state/migrations.js +21 -0
  53. package/dist/state/migrations.js.map +1 -1
  54. package/dist/supervision/SafeRestartNotifier.js +46 -3
  55. package/dist/supervision/SafeRestartNotifier.js.map +1 -1
  56. package/dist/supervision/SafeRestartScheduler.js +65 -6
  57. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  58. package/dist/supervision/replacementSupervisor.js +27 -2
  59. package/dist/supervision/replacementSupervisor.js.map +1 -1
  60. package/dist/supervisor.js +27 -2
  61. package/dist/supervisor.js.map +1 -1
  62. package/docs/technical-reference.md +20 -7
  63. package/docs/technical-reference.zh.md +20 -7
  64. package/npm-shrinkwrap.json +2 -2
  65. package/package.json +1 -1
  66. package/skills/agent-bot/SKILL.md +63 -26
  67. package/src/acp/AcpProcessManager.ts +16 -3
  68. package/src/acp/AcpSessionManager.ts +14 -0
  69. package/src/cli/ServerStarter.ts +25 -2
  70. package/src/cli/controlProtocol.ts +6 -1
  71. package/src/cli/help.ts +16 -4
  72. package/src/cli/serverStatus.ts +28 -0
  73. package/src/cli/taskListOutput.ts +91 -0
  74. package/src/cli.ts +123 -51
  75. package/src/codex/CodexProcessManager.ts +38 -3
  76. package/src/codex/CodexRuntime.ts +6 -0
  77. package/src/commands/CommandRouter.ts +4 -0
  78. package/src/commands/commandTypes.ts +1 -0
  79. package/src/config/FeishuUserOpenIdStore.ts +81 -0
  80. package/src/config/loadConfig.ts +3 -0
  81. package/src/feishu/CardRenderer.ts +169 -22
  82. package/src/feishu/FeishuConnector.ts +13 -4
  83. package/src/index.ts +195 -23
  84. package/src/presentation/turnGraph.ts +48 -0
  85. package/src/proxy/ProxySessionController.ts +507 -112
  86. package/src/runtime/AcpRuntimeAdapter.ts +5 -0
  87. package/src/runtime/agentEnvironment.ts +41 -3
  88. package/src/runtime/createAgentRuntimeRegistry.ts +18 -3
  89. package/src/runtime/types.ts +5 -0
  90. package/src/startup/StartupNotifier.ts +23 -7
  91. package/src/startup/startFeishu.ts +3 -2
  92. package/src/state/StateStore.ts +224 -0
  93. package/src/state/migrations.ts +21 -0
  94. package/src/supervision/SafeRestartNotifier.ts +53 -4
  95. package/src/supervision/SafeRestartScheduler.ts +78 -7
  96. package/src/supervision/replacementSupervisor.ts +27 -1
  97. package/src/supervisor.ts +33 -2
@@ -11,9 +11,15 @@ export type SafeRestartPhase =
11
11
  | "restarting"
12
12
  | "cancelled";
13
13
 
14
+ export interface RestartNotificationTarget {
15
+ contextKey: string;
16
+ replyMessageId?: string;
17
+ }
18
+
14
19
  export interface SafeRestartStatus {
15
20
  scheduleId: number;
16
21
  reason: string;
22
+ notificationTargets?: RestartNotificationTarget[];
17
23
  phase: SafeRestartPhase;
18
24
  activity: ServerActivityState;
19
25
  remainingMs?: number;
@@ -21,7 +27,7 @@ export interface SafeRestartStatus {
21
27
 
22
28
  export interface SafeRestartSchedulerOptions {
23
29
  readActivity(): ServerActivityState;
24
- onReady(reason: string): void | Promise<void>;
30
+ onReady(reason: string, notificationTargets: RestartNotificationTarget[]): void | Promise<void>;
25
31
  onStatus?(status: SafeRestartStatus): void | Promise<void>;
26
32
  onStatusError?(error: unknown): void;
27
33
  quietPeriodMs?: number;
@@ -33,6 +39,7 @@ export class SafeRestartScheduler {
33
39
  private polling?: Promise<void>;
34
40
  private pollAgain = false;
35
41
  private reason?: string;
42
+ private notificationTargets: RestartNotificationTarget[] = [];
36
43
  private idleSince?: number;
37
44
  private idleInboundAt?: string;
38
45
  private scheduleId = 0;
@@ -47,9 +54,13 @@ export class SafeRestartScheduler {
47
54
  return this.reason;
48
55
  }
49
56
 
50
- schedule(reason: string): boolean {
57
+ schedule(reason: string, notificationTarget?: RestartNotificationTarget): boolean {
51
58
  const newlyScheduled = !this.reason;
52
- if (newlyScheduled) this.scheduleId += 1;
59
+ if (newlyScheduled) {
60
+ this.scheduleId += 1;
61
+ this.notificationTargets = [];
62
+ }
63
+ this.addNotificationTarget(notificationTarget);
53
64
  this.reason = reason;
54
65
  this.idleSince = undefined;
55
66
  this.idleInboundAt = undefined;
@@ -73,10 +84,12 @@ export class SafeRestartScheduler {
73
84
  const reason = this.reason;
74
85
  if (!reason || this.scheduleId !== expectedScheduleId) return false;
75
86
  const activity = this.options.readActivity();
87
+ const notificationTargets = this.notificationTargetsSnapshot();
76
88
  this.clear();
77
89
  await this.emitStatus({
78
90
  scheduleId: expectedScheduleId,
79
91
  reason,
92
+ notificationTargets,
80
93
  phase: "cancelled",
81
94
  activity,
82
95
  });
@@ -87,6 +100,7 @@ export class SafeRestartScheduler {
87
100
  if (this.timer) clearInterval(this.timer);
88
101
  this.timer = undefined;
89
102
  this.reason = undefined;
103
+ this.notificationTargets = [];
90
104
  this.idleSince = undefined;
91
105
  this.idleInboundAt = undefined;
92
106
  }
@@ -101,6 +115,7 @@ export class SafeRestartScheduler {
101
115
  await this.emitStatus({
102
116
  scheduleId: this.scheduleId,
103
117
  reason,
118
+ notificationTargets: this.notificationTargetsSnapshot(),
104
119
  phase: state.runningSessions > 0 ? "waiting_tasks" : "waiting_delivery",
105
120
  activity: state,
106
121
  });
@@ -111,12 +126,26 @@ export class SafeRestartScheduler {
111
126
  if (this.idleSince === undefined || this.idleInboundAt !== latestInboundAt) {
112
127
  this.idleSince = Date.now();
113
128
  this.idleInboundAt = latestInboundAt;
114
- await this.emitStatus({ scheduleId: this.scheduleId, reason, phase: "countdown", activity: state, remainingMs: quietPeriodMs });
129
+ await this.emitStatus({
130
+ scheduleId: this.scheduleId,
131
+ reason,
132
+ notificationTargets: this.notificationTargetsSnapshot(),
133
+ phase: "countdown",
134
+ activity: state,
135
+ remainingMs: quietPeriodMs,
136
+ });
115
137
  return;
116
138
  }
117
139
  const remainingMs = Math.max(0, quietPeriodMs - (Date.now() - this.idleSince));
118
140
  if (remainingMs > 0) {
119
- await this.emitStatus({ scheduleId: this.scheduleId, reason, phase: "countdown", activity: state, remainingMs });
141
+ await this.emitStatus({
142
+ scheduleId: this.scheduleId,
143
+ reason,
144
+ notificationTargets: this.notificationTargetsSnapshot(),
145
+ phase: "countdown",
146
+ activity: state,
147
+ remainingMs,
148
+ });
120
149
  return;
121
150
  }
122
151
  const confirmed = this.options.readActivity();
@@ -131,6 +160,7 @@ export class SafeRestartScheduler {
131
160
  await this.emitStatus({
132
161
  scheduleId: this.scheduleId,
133
162
  reason,
163
+ notificationTargets: this.notificationTargetsSnapshot(),
134
164
  phase: confirmed.runningSessions > 0 ? "waiting_tasks" : "waiting_delivery",
135
165
  activity: confirmed,
136
166
  });
@@ -138,6 +168,7 @@ export class SafeRestartScheduler {
138
168
  await this.emitStatus({
139
169
  scheduleId: this.scheduleId,
140
170
  reason,
171
+ notificationTargets: this.notificationTargetsSnapshot(),
141
172
  phase: "countdown",
142
173
  activity: confirmed,
143
174
  remainingMs: quietPeriodMs,
@@ -146,9 +177,17 @@ export class SafeRestartScheduler {
146
177
  return;
147
178
  }
148
179
  const scheduleId = this.scheduleId;
180
+ const notificationTargets = this.notificationTargetsSnapshot();
149
181
  this.clear();
150
- await this.emitStatus({ scheduleId, reason, phase: "restarting", activity: confirmed, remainingMs: 0 });
151
- await this.options.onReady(reason);
182
+ await this.emitStatus({
183
+ scheduleId,
184
+ reason,
185
+ notificationTargets,
186
+ phase: "restarting",
187
+ activity: confirmed,
188
+ remainingMs: 0,
189
+ });
190
+ await this.options.onReady(reason, notificationTargets);
152
191
  }
153
192
 
154
193
  private requestPoll(): void {
@@ -175,4 +214,36 @@ export class SafeRestartScheduler {
175
214
  this.options.onStatusError?.(error);
176
215
  });
177
216
  }
217
+
218
+ private addNotificationTarget(value: RestartNotificationTarget | undefined): void {
219
+ const target = normalizedNotificationTarget(value);
220
+ if (!target) return;
221
+ const existingIndex = this.notificationTargets.findIndex(
222
+ (candidate) => candidate.contextKey === target.contextKey,
223
+ );
224
+ if (existingIndex < 0) {
225
+ this.notificationTargets.push(target);
226
+ return;
227
+ }
228
+ const existing = this.notificationTargets[existingIndex]!;
229
+ if (!existing.replyMessageId && target.replyMessageId) {
230
+ this.notificationTargets[existingIndex] = target;
231
+ }
232
+ }
233
+
234
+ private notificationTargetsSnapshot(): RestartNotificationTarget[] {
235
+ return this.notificationTargets.map((target) => ({ ...target }));
236
+ }
237
+ }
238
+
239
+ function normalizedNotificationTarget(
240
+ value: RestartNotificationTarget | undefined,
241
+ ): RestartNotificationTarget | undefined {
242
+ const contextKey = value?.contextKey.trim();
243
+ if (!contextKey) return undefined;
244
+ const replyMessageId = value?.replyMessageId?.trim();
245
+ return {
246
+ contextKey,
247
+ ...(replyMessageId ? { replyMessageId } : {}),
248
+ };
178
249
  }
@@ -1,10 +1,36 @@
1
+ export const RESTART_GROUP_CONTEXTS_ENV = "AGENT_BOT_RESTART_GROUP_CONTEXTS";
2
+
1
3
  export function replacementSupervisorEnvironment(
2
4
  restartReason: string,
3
5
  environment: NodeJS.ProcessEnv = process.env,
6
+ restartGroupContextKeys: readonly string[] = [],
4
7
  ): NodeJS.ProcessEnv {
5
- return {
8
+ const result: NodeJS.ProcessEnv = {
6
9
  ...environment,
7
10
  AGENT_BOT_START_DELAY_MS: "250",
8
11
  AGENT_BOT_RESTART_REASON: restartReason,
9
12
  };
13
+ const normalized = normalizedContextKeys(restartGroupContextKeys);
14
+ if (normalized.length > 0) {
15
+ result[RESTART_GROUP_CONTEXTS_ENV] = JSON.stringify(normalized);
16
+ } else {
17
+ delete result[RESTART_GROUP_CONTEXTS_ENV];
18
+ }
19
+ return result;
20
+ }
21
+
22
+ export function restartGroupContextKeysFromEnvironment(value: string | undefined): string[] {
23
+ if (!value?.trim()) return [];
24
+ try {
25
+ const parsed = JSON.parse(value) as unknown;
26
+ return Array.isArray(parsed)
27
+ ? normalizedContextKeys(parsed.filter((entry): entry is string => typeof entry === "string"))
28
+ : [];
29
+ } catch {
30
+ return [];
31
+ }
32
+ }
33
+
34
+ function normalizedContextKeys(values: readonly string[]): string[] {
35
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
10
36
  }
package/src/supervisor.ts CHANGED
@@ -14,6 +14,10 @@ import {
14
14
  } from "./supervision/restartPolicy.js";
15
15
  import { SupervisorDiagnostics, nodeDiagnosticReportArguments } from "./supervision/SupervisorDiagnostics.js";
16
16
  import { refreshedSystemEnvironment } from "./supervision/systemEnvironment.js";
17
+ import {
18
+ RESTART_GROUP_CONTEXTS_ENV,
19
+ restartGroupContextKeysFromEnvironment,
20
+ } from "./supervision/replacementSupervisor.js";
17
21
 
18
22
  const childEntry = fileURLToPath(new URL("./index.js", import.meta.url));
19
23
  const config = loadConfig();
@@ -24,9 +28,13 @@ const diagnostics = new SupervisorDiagnostics(config);
24
28
  diagnostics.initialize();
25
29
  let child: ChildProcess | undefined;
26
30
  let restartTimer: NodeJS.Timeout | undefined;
31
+ let restartGroupContextExpiryTimer: NodeJS.Timeout | undefined;
27
32
  let stopping = false;
28
33
  let consecutiveFailures = 0;
29
34
  let nextStartReason = process.env.AGENT_BOT_RESTART_REASON?.trim() || "Supervisor 启动";
35
+ let nextRestartGroupContextKeys = restartGroupContextKeysFromEnvironment(
36
+ process.env[RESTART_GROUP_CONTEXTS_ENV],
37
+ );
30
38
 
31
39
  async function startChild(): Promise<void> {
32
40
  if (stopping || child) return;
@@ -37,6 +45,7 @@ async function startChild(): Promise<void> {
37
45
  }
38
46
  const startedAt = Date.now();
39
47
  const restartReason = nextStartReason;
48
+ const restartGroupContextKeys = nextRestartGroupContextKeys;
40
49
  nextStartReason = "Supervisor 重新拉起进程";
41
50
  const workerStderr = diagnostics.openWorkerStderr();
42
51
  const environmentRefresh = refreshedSystemEnvironment();
@@ -51,11 +60,11 @@ async function startChild(): Promise<void> {
51
60
  childEntry,
52
61
  ], {
53
62
  cwd: process.cwd(),
54
- env: {
63
+ env: workerEnvironment({
55
64
  ...environmentRefresh.environment,
56
65
  AGENT_BOT_SUPERVISED: "1",
57
66
  AGENT_BOT_RESTART_REASON: restartReason,
58
- },
67
+ }, restartGroupContextKeys),
59
68
  stdio: ["ignore", "ignore", workerStderr],
60
69
  windowsHide: true,
61
70
  });
@@ -66,6 +75,7 @@ async function startChild(): Promise<void> {
66
75
  writeSupervisorLog("started", {
67
76
  pid: child.pid,
68
77
  restartReason,
78
+ restartGroupContextKeys,
69
79
  workerStderrPath: diagnostics.paths.workerStderrPath,
70
80
  crashReportDirectory: diagnostics.paths.crashReportDirectory,
71
81
  });
@@ -76,6 +86,26 @@ async function startChild(): Promise<void> {
76
86
  child.once("exit", (code, signal) => {
77
87
  void handleChildExit(code, signal, startedAt, workerPid);
78
88
  });
89
+ if (restartGroupContextKeys.length > 0) {
90
+ if (restartGroupContextExpiryTimer) clearTimeout(restartGroupContextExpiryTimer);
91
+ restartGroupContextExpiryTimer = setTimeout(() => {
92
+ nextRestartGroupContextKeys = [];
93
+ restartGroupContextExpiryTimer = undefined;
94
+ }, STABLE_UPTIME_MS);
95
+ }
96
+ }
97
+
98
+ function workerEnvironment(
99
+ environment: NodeJS.ProcessEnv,
100
+ restartGroupContextKeys: readonly string[],
101
+ ): NodeJS.ProcessEnv {
102
+ const result = { ...environment };
103
+ if (restartGroupContextKeys.length > 0) {
104
+ result[RESTART_GROUP_CONTEXTS_ENV] = JSON.stringify(restartGroupContextKeys);
105
+ } else {
106
+ delete result[RESTART_GROUP_CONTEXTS_ENV];
107
+ }
108
+ return result;
79
109
  }
80
110
 
81
111
  async function handleChildExit(
@@ -134,6 +164,7 @@ function stop(signal: NodeJS.Signals): void {
134
164
  if (stopping) return;
135
165
  stopping = true;
136
166
  if (restartTimer) clearTimeout(restartTimer);
167
+ if (restartGroupContextExpiryTimer) clearTimeout(restartGroupContextExpiryTimer);
137
168
  if (child && !child.killed) {
138
169
  child.kill(signal);
139
170
  return;