@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.
- package/.env.example +2 -2
- package/CHANGELOG.md +15 -0
- package/README.md +107 -98
- package/README.zh.md +81 -72
- package/dist/acp/AcpProcessManager.js +8 -3
- package/dist/acp/AcpProcessManager.js.map +1 -1
- package/dist/acp/AcpSessionManager.js +15 -0
- package/dist/acp/AcpSessionManager.js.map +1 -1
- package/dist/cli/ServerStarter.js +17 -2
- package/dist/cli/ServerStarter.js.map +1 -1
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/help.js +16 -4
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/serverStatus.js +27 -0
- package/dist/cli/serverStatus.js.map +1 -1
- package/dist/cli/taskListOutput.js +72 -0
- package/dist/cli/taskListOutput.js.map +1 -0
- package/dist/cli.js +88 -54
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexProcessManager.js +34 -4
- package/dist/codex/CodexProcessManager.js.map +1 -1
- package/dist/codex/CodexRuntime.js +3 -0
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +5 -0
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/config/FeishuUserOpenIdStore.js +76 -0
- package/dist/config/FeishuUserOpenIdStore.js.map +1 -0
- package/dist/config/loadConfig.js +3 -0
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/feishu/CardRenderer.js +123 -22
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +16 -5
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/index.js +125 -26
- package/dist/index.js.map +1 -1
- package/dist/presentation/turnGraph.js +36 -0
- package/dist/presentation/turnGraph.js.map +1 -0
- package/dist/proxy/ProxySessionController.js +425 -104
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/runtime/AcpRuntimeAdapter.js +3 -0
- package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
- package/dist/runtime/agentEnvironment.js +24 -4
- package/dist/runtime/agentEnvironment.js.map +1 -1
- package/dist/runtime/createAgentRuntimeRegistry.js +7 -3
- package/dist/runtime/createAgentRuntimeRegistry.js.map +1 -1
- package/dist/startup/StartupNotifier.js +16 -7
- package/dist/startup/StartupNotifier.js.map +1 -1
- package/dist/startup/startFeishu.js +2 -2
- package/dist/startup/startFeishu.js.map +1 -1
- package/dist/state/StateStore.js +172 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/migrations.js +21 -0
- package/dist/state/migrations.js.map +1 -1
- package/dist/supervision/SafeRestartNotifier.js +46 -3
- package/dist/supervision/SafeRestartNotifier.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +65 -6
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/dist/supervision/replacementSupervisor.js +27 -2
- package/dist/supervision/replacementSupervisor.js.map +1 -1
- package/dist/supervisor.js +27 -2
- package/dist/supervisor.js.map +1 -1
- package/docs/technical-reference.md +20 -7
- package/docs/technical-reference.zh.md +20 -7
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +63 -26
- package/src/acp/AcpProcessManager.ts +16 -3
- package/src/acp/AcpSessionManager.ts +14 -0
- package/src/cli/ServerStarter.ts +25 -2
- package/src/cli/controlProtocol.ts +6 -1
- package/src/cli/help.ts +16 -4
- package/src/cli/serverStatus.ts +28 -0
- package/src/cli/taskListOutput.ts +91 -0
- package/src/cli.ts +123 -51
- package/src/codex/CodexProcessManager.ts +38 -3
- package/src/codex/CodexRuntime.ts +6 -0
- package/src/commands/CommandRouter.ts +4 -0
- package/src/commands/commandTypes.ts +1 -0
- package/src/config/FeishuUserOpenIdStore.ts +81 -0
- package/src/config/loadConfig.ts +3 -0
- package/src/feishu/CardRenderer.ts +169 -22
- package/src/feishu/FeishuConnector.ts +13 -4
- package/src/index.ts +195 -23
- package/src/presentation/turnGraph.ts +48 -0
- package/src/proxy/ProxySessionController.ts +507 -112
- package/src/runtime/AcpRuntimeAdapter.ts +5 -0
- package/src/runtime/agentEnvironment.ts +41 -3
- package/src/runtime/createAgentRuntimeRegistry.ts +18 -3
- package/src/runtime/types.ts +5 -0
- package/src/startup/StartupNotifier.ts +23 -7
- package/src/startup/startFeishu.ts +3 -2
- package/src/state/StateStore.ts +224 -0
- package/src/state/migrations.ts +21 -0
- package/src/supervision/SafeRestartNotifier.ts +53 -4
- package/src/supervision/SafeRestartScheduler.ts +78 -7
- package/src/supervision/replacementSupervisor.ts +27 -1
- 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)
|
|
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({
|
|
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({
|
|
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({
|
|
151
|
-
|
|
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
|
-
|
|
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;
|