@keyou007/agent-bot 0.1.13-alpha.0 → 0.1.13-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.
- package/.env.example +2 -2
- package/CHANGELOG.md +9 -0
- package/README.md +10 -4
- package/README.zh.md +10 -4
- 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 +12 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli.js +39 -0
- package/dist/cli.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 +82 -22
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/index.js +103 -23
- 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 +270 -47
- package/dist/proxy/ProxySessionController.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 +169 -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 +11 -4
- package/docs/technical-reference.zh.md +11 -4
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +10 -4
- package/src/cli/ServerStarter.ts +25 -2
- package/src/cli/controlProtocol.ts +6 -1
- package/src/cli/help.ts +12 -2
- package/src/cli.ts +57 -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 +109 -22
- package/src/index.ts +161 -21
- package/src/presentation/turnGraph.ts +48 -0
- package/src/proxy/ProxySessionController.ts +323 -55
- package/src/startup/StartupNotifier.ts +23 -7
- package/src/startup/startFeishu.ts +3 -2
- package/src/state/StateStore.ts +221 -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
|
@@ -4,6 +4,7 @@ export class SafeRestartScheduler {
|
|
|
4
4
|
polling;
|
|
5
5
|
pollAgain = false;
|
|
6
6
|
reason;
|
|
7
|
+
notificationTargets = [];
|
|
7
8
|
idleSince;
|
|
8
9
|
idleInboundAt;
|
|
9
10
|
scheduleId = 0;
|
|
@@ -16,10 +17,13 @@ export class SafeRestartScheduler {
|
|
|
16
17
|
get pendingReason() {
|
|
17
18
|
return this.reason;
|
|
18
19
|
}
|
|
19
|
-
schedule(reason) {
|
|
20
|
+
schedule(reason, notificationTarget) {
|
|
20
21
|
const newlyScheduled = !this.reason;
|
|
21
|
-
if (newlyScheduled)
|
|
22
|
+
if (newlyScheduled) {
|
|
22
23
|
this.scheduleId += 1;
|
|
24
|
+
this.notificationTargets = [];
|
|
25
|
+
}
|
|
26
|
+
this.addNotificationTarget(notificationTarget);
|
|
23
27
|
this.reason = reason;
|
|
24
28
|
this.idleSince = undefined;
|
|
25
29
|
this.idleInboundAt = undefined;
|
|
@@ -42,10 +46,12 @@ export class SafeRestartScheduler {
|
|
|
42
46
|
if (!reason || this.scheduleId !== expectedScheduleId)
|
|
43
47
|
return false;
|
|
44
48
|
const activity = this.options.readActivity();
|
|
49
|
+
const notificationTargets = this.notificationTargetsSnapshot();
|
|
45
50
|
this.clear();
|
|
46
51
|
await this.emitStatus({
|
|
47
52
|
scheduleId: expectedScheduleId,
|
|
48
53
|
reason,
|
|
54
|
+
notificationTargets,
|
|
49
55
|
phase: "cancelled",
|
|
50
56
|
activity,
|
|
51
57
|
});
|
|
@@ -56,6 +62,7 @@ export class SafeRestartScheduler {
|
|
|
56
62
|
clearInterval(this.timer);
|
|
57
63
|
this.timer = undefined;
|
|
58
64
|
this.reason = undefined;
|
|
65
|
+
this.notificationTargets = [];
|
|
59
66
|
this.idleSince = undefined;
|
|
60
67
|
this.idleInboundAt = undefined;
|
|
61
68
|
}
|
|
@@ -70,6 +77,7 @@ export class SafeRestartScheduler {
|
|
|
70
77
|
await this.emitStatus({
|
|
71
78
|
scheduleId: this.scheduleId,
|
|
72
79
|
reason,
|
|
80
|
+
notificationTargets: this.notificationTargetsSnapshot(),
|
|
73
81
|
phase: state.runningSessions > 0 ? "waiting_tasks" : "waiting_delivery",
|
|
74
82
|
activity: state,
|
|
75
83
|
});
|
|
@@ -80,12 +88,26 @@ export class SafeRestartScheduler {
|
|
|
80
88
|
if (this.idleSince === undefined || this.idleInboundAt !== latestInboundAt) {
|
|
81
89
|
this.idleSince = Date.now();
|
|
82
90
|
this.idleInboundAt = latestInboundAt;
|
|
83
|
-
await this.emitStatus({
|
|
91
|
+
await this.emitStatus({
|
|
92
|
+
scheduleId: this.scheduleId,
|
|
93
|
+
reason,
|
|
94
|
+
notificationTargets: this.notificationTargetsSnapshot(),
|
|
95
|
+
phase: "countdown",
|
|
96
|
+
activity: state,
|
|
97
|
+
remainingMs: quietPeriodMs,
|
|
98
|
+
});
|
|
84
99
|
return;
|
|
85
100
|
}
|
|
86
101
|
const remainingMs = Math.max(0, quietPeriodMs - (Date.now() - this.idleSince));
|
|
87
102
|
if (remainingMs > 0) {
|
|
88
|
-
await this.emitStatus({
|
|
103
|
+
await this.emitStatus({
|
|
104
|
+
scheduleId: this.scheduleId,
|
|
105
|
+
reason,
|
|
106
|
+
notificationTargets: this.notificationTargetsSnapshot(),
|
|
107
|
+
phase: "countdown",
|
|
108
|
+
activity: state,
|
|
109
|
+
remainingMs,
|
|
110
|
+
});
|
|
89
111
|
return;
|
|
90
112
|
}
|
|
91
113
|
const confirmed = this.options.readActivity();
|
|
@@ -98,6 +120,7 @@ export class SafeRestartScheduler {
|
|
|
98
120
|
await this.emitStatus({
|
|
99
121
|
scheduleId: this.scheduleId,
|
|
100
122
|
reason,
|
|
123
|
+
notificationTargets: this.notificationTargetsSnapshot(),
|
|
101
124
|
phase: confirmed.runningSessions > 0 ? "waiting_tasks" : "waiting_delivery",
|
|
102
125
|
activity: confirmed,
|
|
103
126
|
});
|
|
@@ -106,6 +129,7 @@ export class SafeRestartScheduler {
|
|
|
106
129
|
await this.emitStatus({
|
|
107
130
|
scheduleId: this.scheduleId,
|
|
108
131
|
reason,
|
|
132
|
+
notificationTargets: this.notificationTargetsSnapshot(),
|
|
109
133
|
phase: "countdown",
|
|
110
134
|
activity: confirmed,
|
|
111
135
|
remainingMs: quietPeriodMs,
|
|
@@ -114,9 +138,17 @@ export class SafeRestartScheduler {
|
|
|
114
138
|
return;
|
|
115
139
|
}
|
|
116
140
|
const scheduleId = this.scheduleId;
|
|
141
|
+
const notificationTargets = this.notificationTargetsSnapshot();
|
|
117
142
|
this.clear();
|
|
118
|
-
await this.emitStatus({
|
|
119
|
-
|
|
143
|
+
await this.emitStatus({
|
|
144
|
+
scheduleId,
|
|
145
|
+
reason,
|
|
146
|
+
notificationTargets,
|
|
147
|
+
phase: "restarting",
|
|
148
|
+
activity: confirmed,
|
|
149
|
+
remainingMs: 0,
|
|
150
|
+
});
|
|
151
|
+
await this.options.onReady(reason, notificationTargets);
|
|
120
152
|
}
|
|
121
153
|
requestPoll() {
|
|
122
154
|
if (this.polling) {
|
|
@@ -142,5 +174,32 @@ export class SafeRestartScheduler {
|
|
|
142
174
|
this.options.onStatusError?.(error);
|
|
143
175
|
});
|
|
144
176
|
}
|
|
177
|
+
addNotificationTarget(value) {
|
|
178
|
+
const target = normalizedNotificationTarget(value);
|
|
179
|
+
if (!target)
|
|
180
|
+
return;
|
|
181
|
+
const existingIndex = this.notificationTargets.findIndex((candidate) => candidate.contextKey === target.contextKey);
|
|
182
|
+
if (existingIndex < 0) {
|
|
183
|
+
this.notificationTargets.push(target);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const existing = this.notificationTargets[existingIndex];
|
|
187
|
+
if (!existing.replyMessageId && target.replyMessageId) {
|
|
188
|
+
this.notificationTargets[existingIndex] = target;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
notificationTargetsSnapshot() {
|
|
192
|
+
return this.notificationTargets.map((target) => ({ ...target }));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function normalizedNotificationTarget(value) {
|
|
196
|
+
const contextKey = value?.contextKey.trim();
|
|
197
|
+
if (!contextKey)
|
|
198
|
+
return undefined;
|
|
199
|
+
const replyMessageId = value?.replyMessageId?.trim();
|
|
200
|
+
return {
|
|
201
|
+
contextKey,
|
|
202
|
+
...(replyMessageId ? { replyMessageId } : {}),
|
|
203
|
+
};
|
|
145
204
|
}
|
|
146
205
|
//# sourceMappingURL=SafeRestartScheduler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SafeRestartScheduler.js","sourceRoot":"","sources":["../../src/supervision/SafeRestartScheduler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SafeRestartScheduler.js","sourceRoot":"","sources":["../../src/supervision/SafeRestartScheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,33 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
1
|
+
export const RESTART_GROUP_CONTEXTS_ENV = "AGENT_BOT_RESTART_GROUP_CONTEXTS";
|
|
2
|
+
export function replacementSupervisorEnvironment(restartReason, environment = process.env, restartGroupContextKeys = []) {
|
|
3
|
+
const result = {
|
|
3
4
|
...environment,
|
|
4
5
|
AGENT_BOT_START_DELAY_MS: "250",
|
|
5
6
|
AGENT_BOT_RESTART_REASON: restartReason,
|
|
6
7
|
};
|
|
8
|
+
const normalized = normalizedContextKeys(restartGroupContextKeys);
|
|
9
|
+
if (normalized.length > 0) {
|
|
10
|
+
result[RESTART_GROUP_CONTEXTS_ENV] = JSON.stringify(normalized);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
delete result[RESTART_GROUP_CONTEXTS_ENV];
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
export function restartGroupContextKeysFromEnvironment(value) {
|
|
18
|
+
if (!value?.trim())
|
|
19
|
+
return [];
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(value);
|
|
22
|
+
return Array.isArray(parsed)
|
|
23
|
+
? normalizedContextKeys(parsed.filter((entry) => typeof entry === "string"))
|
|
24
|
+
: [];
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function normalizedContextKeys(values) {
|
|
31
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
7
32
|
}
|
|
8
33
|
//# sourceMappingURL=replacementSupervisor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replacementSupervisor.js","sourceRoot":"","sources":["../../src/supervision/replacementSupervisor.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gCAAgC,CAC9C,aAAqB,EACrB,WAAW,GAAsB,OAAO,CAAC,GAAG;
|
|
1
|
+
{"version":3,"file":"replacementSupervisor.js","sourceRoot":"","sources":["../../src/supervision/replacementSupervisor.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,kCAAkC,CAAC;AAE7E,MAAM,UAAU,gCAAgC,CAC9C,aAAqB,EACrB,WAAW,GAAsB,OAAO,CAAC,GAAG,EAC5C,uBAAuB,GAAsB,EAAE;IAE/C,MAAM,MAAM,GAAsB;QAChC,GAAG,WAAW;QACd,wBAAwB,EAAE,KAAK;QAC/B,wBAAwB,EAAE,aAAa;KACxC,CAAC;IACF,MAAM,UAAU,GAAG,qBAAqB,CAAC,uBAAuB,CAAC,CAAC;IAClE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,OAAO,MAAM,CAAC,0BAA0B,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,sCAAsC,CAAC,KAAyB;IAC9E,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAY,CAAC;QAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAC1B,CAAC,CAAC,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC;YAC7F,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAyB;IACtD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC"}
|
package/dist/supervisor.js
CHANGED
|
@@ -7,6 +7,7 @@ import { requireServerFeishuTransport } from "./feishu/transport.js";
|
|
|
7
7
|
import { crashRestartDelayMs, describeRestartReason, INTENTIONAL_RESTART_DELAY_MS, RESTART_EXIT_CODE, STABLE_UPTIME_MS, STOP_EXIT_CODE, } from "./supervision/restartPolicy.js";
|
|
8
8
|
import { SupervisorDiagnostics, nodeDiagnosticReportArguments } from "./supervision/SupervisorDiagnostics.js";
|
|
9
9
|
import { refreshedSystemEnvironment } from "./supervision/systemEnvironment.js";
|
|
10
|
+
import { RESTART_GROUP_CONTEXTS_ENV, restartGroupContextKeysFromEnvironment, } from "./supervision/replacementSupervisor.js";
|
|
10
11
|
const childEntry = fileURLToPath(new URL("./index.js", import.meta.url));
|
|
11
12
|
const config = loadConfig();
|
|
12
13
|
requireServerFeishuTransport(config.feishu);
|
|
@@ -16,9 +17,11 @@ const diagnostics = new SupervisorDiagnostics(config);
|
|
|
16
17
|
diagnostics.initialize();
|
|
17
18
|
let child;
|
|
18
19
|
let restartTimer;
|
|
20
|
+
let restartGroupContextExpiryTimer;
|
|
19
21
|
let stopping = false;
|
|
20
22
|
let consecutiveFailures = 0;
|
|
21
23
|
let nextStartReason = process.env.AGENT_BOT_RESTART_REASON?.trim() || "Supervisor 启动";
|
|
24
|
+
let nextRestartGroupContextKeys = restartGroupContextKeysFromEnvironment(process.env[RESTART_GROUP_CONTEXTS_ENV]);
|
|
22
25
|
async function startChild() {
|
|
23
26
|
if (stopping || child)
|
|
24
27
|
return;
|
|
@@ -29,6 +32,7 @@ async function startChild() {
|
|
|
29
32
|
}
|
|
30
33
|
const startedAt = Date.now();
|
|
31
34
|
const restartReason = nextStartReason;
|
|
35
|
+
const restartGroupContextKeys = nextRestartGroupContextKeys;
|
|
32
36
|
nextStartReason = "Supervisor 重新拉起进程";
|
|
33
37
|
const workerStderr = diagnostics.openWorkerStderr();
|
|
34
38
|
const environmentRefresh = refreshedSystemEnvironment();
|
|
@@ -44,11 +48,11 @@ async function startChild() {
|
|
|
44
48
|
childEntry,
|
|
45
49
|
], {
|
|
46
50
|
cwd: process.cwd(),
|
|
47
|
-
env: {
|
|
51
|
+
env: workerEnvironment({
|
|
48
52
|
...environmentRefresh.environment,
|
|
49
53
|
AGENT_BOT_SUPERVISED: "1",
|
|
50
54
|
AGENT_BOT_RESTART_REASON: restartReason,
|
|
51
|
-
},
|
|
55
|
+
}, restartGroupContextKeys),
|
|
52
56
|
stdio: ["ignore", "ignore", workerStderr],
|
|
53
57
|
windowsHide: true,
|
|
54
58
|
});
|
|
@@ -60,6 +64,7 @@ async function startChild() {
|
|
|
60
64
|
writeSupervisorLog("started", {
|
|
61
65
|
pid: child.pid,
|
|
62
66
|
restartReason,
|
|
67
|
+
restartGroupContextKeys,
|
|
63
68
|
workerStderrPath: diagnostics.paths.workerStderrPath,
|
|
64
69
|
crashReportDirectory: diagnostics.paths.crashReportDirectory,
|
|
65
70
|
});
|
|
@@ -69,6 +74,24 @@ async function startChild() {
|
|
|
69
74
|
child.once("exit", (code, signal) => {
|
|
70
75
|
void handleChildExit(code, signal, startedAt, workerPid);
|
|
71
76
|
});
|
|
77
|
+
if (restartGroupContextKeys.length > 0) {
|
|
78
|
+
if (restartGroupContextExpiryTimer)
|
|
79
|
+
clearTimeout(restartGroupContextExpiryTimer);
|
|
80
|
+
restartGroupContextExpiryTimer = setTimeout(() => {
|
|
81
|
+
nextRestartGroupContextKeys = [];
|
|
82
|
+
restartGroupContextExpiryTimer = undefined;
|
|
83
|
+
}, STABLE_UPTIME_MS);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function workerEnvironment(environment, restartGroupContextKeys) {
|
|
87
|
+
const result = { ...environment };
|
|
88
|
+
if (restartGroupContextKeys.length > 0) {
|
|
89
|
+
result[RESTART_GROUP_CONTEXTS_ENV] = JSON.stringify(restartGroupContextKeys);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
delete result[RESTART_GROUP_CONTEXTS_ENV];
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
72
95
|
}
|
|
73
96
|
async function handleChildExit(code, signal, startedAt, workerPid) {
|
|
74
97
|
child = undefined;
|
|
@@ -123,6 +146,8 @@ function stop(signal) {
|
|
|
123
146
|
stopping = true;
|
|
124
147
|
if (restartTimer)
|
|
125
148
|
clearTimeout(restartTimer);
|
|
149
|
+
if (restartGroupContextExpiryTimer)
|
|
150
|
+
clearTimeout(restartGroupContextExpiryTimer);
|
|
126
151
|
if (child && !child.killed) {
|
|
127
152
|
child.kill(signal);
|
|
128
153
|
return;
|
package/dist/supervisor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,4BAA4B,EAC5B,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,GACf,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,qBAAqB,EAAE,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AAC9G,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -73,6 +73,7 @@ Credential behavior:
|
|
|
73
73
|
- Environment values take precedence over values in `~/.agent-bot/.env`.
|
|
74
74
|
- Both `FEISHU_APP_ID` and `FEISHU_APP_SECRET` are required.
|
|
75
75
|
- One-click registration also stores the authorizing user's `open_id` as `FEISHU_USER_OPEN_ID`.
|
|
76
|
+
- When existing app credentials have no user Open ID, the first private-chat message with a valid `ou_` sender persists that sender atomically to the Profile `.env`; group messages and later users never replace it.
|
|
76
77
|
- Complete credentials are preserved unless `--reconfigure-feishu` is used.
|
|
77
78
|
- Missing or incomplete credentials cause a new app registration.
|
|
78
79
|
- Newly registered credentials are fsynced, atomically replaced in `.env`, and read back before configuration begins.
|
|
@@ -100,7 +101,7 @@ The generated authorization links do not contain the app secret.
|
|
|
100
101
|
|
|
101
102
|
`agentbot --profile <directory> init --reset` fully resets an explicitly selected Profile. The Profile server must be stopped first. The command moves the active `config.yaml`, `.env`, `data/`, and `logs/` into a unique timestamped directory under `<profile>/.reset-backups/`, creates clean replacements from the packaged templates, and proceeds through normal initialization. Existing reset backups and unrelated files are retained. Because backups can contain old app secrets and conversation data, protect the Profile directory accordingly. The remote Feishu app is not deleted. `--reset --skip-feishu` creates a clean Console-only Profile, while `--reset` and `--reconfigure-feishu` cannot be combined.
|
|
102
103
|
|
|
103
|
-
After Feishu initialization succeeds, the CLI releases the initialization lock and starts the detached supervisor through the same readiness path as `agentbot server start`. It waits up to 45 seconds for the worker to connect to Feishu and become ready. If the selected profile is already running, no second supervisor is created. `--skip-feishu` skips automatic server startup, and `--json` includes the resulting `server.status` without adding non-JSON output.
|
|
104
|
+
After Feishu initialization succeeds, the CLI releases the initialization lock and starts the detached supervisor through the same readiness path as `agentbot server start`. It waits up to 45 seconds for the worker to connect to Feishu and become ready. If the selected profile is already running, no second supervisor is created; instead, initialization requests a safe restart so the current installed code and refreshed configuration are loaded after active work and final delivery finish. The JSON result reports this as `server.status: "restart-scheduled"`. `--skip-feishu` skips automatic server startup, and `--json` includes the resulting `server.status` without adding non-JSON output.
|
|
104
105
|
|
|
105
106
|
## Feishu App Requirements
|
|
106
107
|
|
|
@@ -171,7 +172,7 @@ logging:
|
|
|
171
172
|
|
|
172
173
|
`feishu.respondToAllGroupMessages` defaults to `true`. Set it to `false` to ignore group messages unless they mention the current bot. The worker resolves the bot's Open ID at startup so mentioning another member does not trigger it. Private messages are always accepted. Initialization requests all-user group-message delivery regardless of this runtime option, allowing it to be changed later without another authorization.
|
|
173
174
|
|
|
174
|
-
`agentbot server start` requires both Feishu credentials. The worker starts the SDK's persistent connection without inspecting SDK logs or private connection state, then sends startup status cards as an outbound readiness check. Each startup card includes the Agent Bot version read from the installed package metadata.
|
|
175
|
+
`agentbot server start` requires both Feishu credentials. The worker starts the SDK's persistent connection without inspecting SDK logs or private connection state, then sends startup status cards as an outbound readiness check. Each startup card includes the Agent Bot version read from the installed package metadata. Every startup sends a card to every known private chat, regardless of recent activity, plus each non-topic group active during the minute before this Worker started. After a safe restart, it also sends to every group enrolled in that restart's notification set regardless of activity age. A topic route is normalized to its parent group, so topic contexts are never startup-card targets. If the database has no eligible chat yet, it sends the card to `feishu.userOpenId` using an `open_id` private message. When notification targets exist, at least one card must be delivered before the server reports ready; individual target failures remain isolated. If neither a known chat nor `feishu.userOpenId` is available, startup continues without the outbound check. The first subsequent private message can persist the missing user Open ID for later startup notifications and CLI-created groups. Missing credentials still fail startup with an initialization hint. `agentbot console` is the explicit local-only path and does not require Feishu credentials.
|
|
175
176
|
|
|
176
177
|
At least one agent must be configured. `defaults.agent` must name a configured agent.
|
|
177
178
|
|
|
@@ -222,13 +223,15 @@ The new group's welcome message reports the persisted fork settings: Provider, m
|
|
|
222
223
|
|
|
223
224
|
Each turn owns one progress card. Normal updates are throttled to one every two seconds; critical updates have a 500 ms minimum gap. On completion, Agent Bot updates the progress card to a terminal state before sending a separate final Markdown message.
|
|
224
225
|
|
|
226
|
+
Successfully completed progress cards include a `Reset` action with its compact effect warning directly below the action. `/turns` places the same effect warning at the top, then presents the current task's completed-turn snapshots in reverse chronological order, 10 per page. Each `turn_started` event persists the previous completed turn as its parent. Existing tasks are backfilled from snapshot timestamps and `session_reset_to_turn` audits, so the first turn after a historical Reset points to the selected turn instead of the time-adjacent abandoned branch. The renderer computes lanes across the complete history before slicing a page, then displays true continuations and merges in the graph column, indented content in the second column, and state or action in the third. The current conversation point is marked instead of showing an action; every other turn has a `Reset` action. Pagination and successful Reset actions update the same card, remain bound to the task that opened it, and move the current marker to the selected turn. The success text identifies the target with its Prompt summary, completion time, and Turn ID. Agent Bot records the App Server thread that originally owned each turn, forks that thread through the selected turn, and replaces the current task's remote thread binding without changing its local task ID, title, Agent, project directory, execution settings, or chat routing. Existing snapshots after the selected turn are not deleted, so the card shows retained turns from the old path together with turns later completed on the new branch. Reset is rejected while the current task is active, and it never reverts local file changes.
|
|
227
|
+
|
|
225
228
|
After the durable message-deduplication claim, Agent Bot awaits the `OnIt` reaction before chat persistence, image downloads, queue waits, command execution, or runtime calls. It replaces that reaction with `DONE`, `ERROR`, or `CrossMark` when the turn succeeds, fails, or is canceled. Reaction failures are logged but do not block task execution.
|
|
226
229
|
|
|
227
230
|
Incoming rich-text images are downloaded into the inbound image cache and passed to the App Server as `localImage` inputs. An image-only message uses the default Prompt `请查看这张图片`. An ACP runtime that cannot accept image input returns an explicit error.
|
|
228
231
|
|
|
229
232
|
## Tasks, Projects, And External App Server Work
|
|
230
233
|
|
|
231
|
-
`/sessions` reads tasks through `thread/list` from every configured App Server Agent. For Codex, this includes tasks created by Codex Desktop, CLI, Agent Bot, or another App Server client under the same `CODEX_HOME`; other Agents expose their own task stores through the same protocol.
|
|
234
|
+
`/sessions` reads tasks through `thread/list` from every configured App Server Agent. For Codex, this includes tasks created by Codex Desktop, CLI, Agent Bot, or another App Server client under the same `CODEX_HOME`; other Agents expose their own task stores through the same protocol. The merged, globally sorted result is displayed five tasks per page. `Previous` and `Next` replace the current card contents instead of appending more tasks, while preserving search terms and stable global task numbers.
|
|
232
235
|
|
|
233
236
|
Each task entry exposes `NewGroup` and `ForkGroup` callbacks. The callback payload keeps the selected task ID and source context, while the Lark operator `open_id` is used to invite the user to the new group. `NewGroup` resolves the selected task's project and execution settings; `ForkGroup` resolves its latest available completed turn.
|
|
234
237
|
|
|
@@ -290,9 +293,13 @@ A safe restart waits for:
|
|
|
290
293
|
|
|
291
294
|
The Feishu `/restart` command uses this safe path by default; `/restart --force` restarts immediately and may interrupt active work. The command rejects every other argument. New messages reset the quiet timer. CLI `--immediate` and `--force` also skip these checks. Exit code `75` identifies an intentional worker restart.
|
|
292
295
|
|
|
296
|
+
A pending safe-restart schedule accumulates the exact notification route of every conversation that triggers it. Every inbound user message, including slash commands, marks its base private or group conversation active. On every status update the notifier also enrolls every persisted conversation active during the preceding minute. Each enrolled route receives its own status card and final restarting acknowledgement and remains enrolled until the schedule ends, even after its activity becomes older than one minute. Repeated routes are deduplicated; when a topic route was first discovered without a message anchor, a later anchored trigger upgrades it. Topic requests retain the originating message ID and use a thread reply so restart updates cannot fall back to the group body. Recent topic activity is tracked on the parent group, while an explicit topic trigger remains in that topic. Later requests also update the restart reason. Before replacement, Agent Bot extracts and deduplicates the parent group of every enrolled route, passes that list through the replacement Supervisor, and adds those groups to the service-wide startup-card targets. The Supervisor retains the list until the Worker has remained stable for 60 seconds, so an early crash-driven relaunch does not lose required recipients.
|
|
297
|
+
|
|
298
|
+
Use `agentbot server restart --task <task>` when a CLI request belongs to a specific task. Task numbers, full IDs, and unambiguous ID prefixes are resolved before the control request is sent. Without `--task`, the Server infers the safe-restart status route only when all running tasks belong to one conversation; it rejects an ambiguous request spanning multiple running conversations. A CLI restart with no running task has no conversation owner for its waiting status.
|
|
299
|
+
|
|
293
300
|
The first safe-restart status card is delayed by three seconds so a task's final response can usually arrive first. Status changes during that window are coalesced into the initial card. The delay does not block scheduler polling, and shutdown flushes any pending card immediately.
|
|
294
301
|
|
|
295
|
-
Pending safe-restart cards include a bottom `Cancel` action. The action carries the scheduler's monotonic schedule ID, so an old card cannot cancel a newer restart. A successful cancellation updates every existing status card in place and removes the action; the action is also absent once the scheduler enters the irreversible restarting phase.
|
|
302
|
+
Pending safe-restart cards include a bottom `Cancel` action. The action carries the scheduler's monotonic schedule ID, so an old card cannot cancel a newer restart. A successful cancellation updates every enrolled conversation's existing status card in place and removes the action; the action is also absent once the scheduler enters the irreversible restarting phase.
|
|
296
303
|
|
|
297
304
|
The local control server implements task mutations, task-targeted NewGroup and ForkGroup operations, and service restart requests. Read-only task queries access SQLite directly.
|
|
298
305
|
|
|
@@ -73,6 +73,7 @@ CLI 通过 Node.js 国际化能力读取系统 Locale。以 `zh` 开头的 Local
|
|
|
73
73
|
- 进程环境变量优先于 `~/.agent-bot/.env`
|
|
74
74
|
- `FEISHU_APP_ID` 和 `FEISHU_APP_SECRET` 必须同时存在
|
|
75
75
|
- 一键注册还会把授权用户的 `open_id` 保存为 `FEISHU_USER_OPEN_ID`
|
|
76
|
+
- 已有应用凭据但缺少用户 Open ID 时,第一条发送者为有效 `ou_` 标识的私聊消息会把该用户原子写入 Profile 的 `.env`;群消息和后续用户都不会覆盖它
|
|
76
77
|
- 除非使用 `--reconfigure-feishu`,完整凭据不会被替换
|
|
77
78
|
- 缺少凭据或只有一项时会重新创建应用
|
|
78
79
|
- 新创建的凭据经过 fsync、原子替换写入 `.env`,并在配置权限前读回校验
|
|
@@ -100,7 +101,7 @@ https://open.feishu.cn/app/<appId>/auth?q=im%3Amessage.group_msg&op_from=openapi
|
|
|
100
101
|
|
|
101
102
|
`agentbot --profile <目录> init --reset` 会完整重置显式指定的 Profile。执行前必须先停止该 Profile 的 Server。命令会把当前 `config.yaml`、`.env`、`data/` 和 `logs/` 移入 `<profile>/.reset-backups/` 下唯一的时间戳目录,从随包模板创建干净的新文件和目录,再继续正常初始化。已有重置备份及其他无关文件会保留。备份中可能含有旧 App Secret 和会话数据,应妥善保护 Profile 目录。远端旧飞书应用不会被删除。`--reset --skip-feishu` 可创建干净的 Console-only Profile,`--reset` 不能与 `--reconfigure-feishu` 同时使用。
|
|
102
103
|
|
|
103
|
-
飞书初始化成功后,CLI 会先释放初始化锁,再通过与 `agentbot server start` 相同的就绪检查流程启动后台 Supervisor,并等待最多 45 秒,直到 Worker 连接飞书并进入就绪状态。如果当前 Profile 的服务已经运行,不会创建第二个 Supervisor
|
|
104
|
+
飞书初始化成功后,CLI 会先释放初始化锁,再通过与 `agentbot server start` 相同的就绪检查流程启动后台 Supervisor,并等待最多 45 秒,直到 Worker 连接飞书并进入就绪状态。如果当前 Profile 的服务已经运行,不会创建第二个 Supervisor,而是安排安全重启,在活动任务和最终结果投递完成后加载当前安装的代码及更新后的配置;JSON 结果将其表示为 `server.status: "restart-scheduled"`。`--skip-feishu` 会跳过自动启动;`--json` 会把结果写入 `server.status`,不会混入非 JSON 文本。
|
|
104
105
|
|
|
105
106
|
## 飞书应用要求
|
|
106
107
|
|
|
@@ -171,7 +172,7 @@ logging:
|
|
|
171
172
|
|
|
172
173
|
`feishu.respondToAllGroupMessages` 默认为 `true`。设为 `false` 后,未 @ 当前机器人的群消息会被忽略;Worker 启动时会解析机器人的 Open ID,因此 @ 其他成员不会误触发。私聊始终正常处理。无论该运行时配置如何,初始化都会申请接收全部群消息的权限,之后切换配置无需再次授权。
|
|
173
174
|
|
|
174
|
-
`agentbot server start` 要求同时配置飞书 `appId` 和 `appSecret`。Worker 启动 SDK 长连接时不读取 SDK 日志或私有连接状态,随后通过发送启动状态卡片检查出站能力。每张启动卡片都会显示从已安装包元数据读取的 Agent Bot
|
|
175
|
+
`agentbot server start` 要求同时配置飞书 `appId` 和 `appSecret`。Worker 启动 SDK 长连接时不读取 SDK 日志或私有连接状态,随后通过发送启动状态卡片检查出站能力。每张启动卡片都会显示从已安装包元数据读取的 Agent Bot 版本。每次启动都会向所有已知私聊发送卡片,不受最近活跃时间影响;同时向本次 Worker 启动前 1 分钟内活跃的非话题群聊发送卡片。安全重启后,还会向所有已加入本次重启通知范围的群发送,不受活跃时间限制;话题路由会折算为所属普通群,因此话题会话永远不会成为启动卡目标。数据库里尚无符合条件的会话时,改用 `feishu.userOpenId`,按 `open_id` 私聊发送。存在通知目标时,至少一张卡片发送成功后 Server 才报告就绪,单个目标发送失败仍相互隔离。如果既没有已知会话,也没有 `feishu.userOpenId`,启动会跳过出站检查并继续;之后收到的第一条私聊消息可以补全用户 Open ID,供后续启动通知和 CLI 建群使用。缺少凭据时仍会启动失败并提示先初始化。`agentbot console` 是明确的纯本地入口,不需要飞书凭据。
|
|
175
176
|
|
|
176
177
|
必须至少配置一个 Agent,`defaults.agent` 必须指向已配置的 Agent。
|
|
177
178
|
|
|
@@ -222,13 +223,15 @@ Agent Bot 会在每个 `thread/fork` 请求中默认发送实验性的 `excludeT
|
|
|
222
223
|
|
|
223
224
|
每个 turn 只有一张进度卡。普通更新最多每两秒一次,关键更新最短间隔 500 毫秒。完成时先把进度卡更新为终态,再单独发送 Markdown 最终回答。
|
|
224
225
|
|
|
226
|
+
成功完成的思考卡片包含 `Reset` 操作,其作用提示紧跟在按钮下方。`/turns` 卡片把相同的作用提示放在最上方,然后按时间倒序显示当前任务的已完成 turn,每页 10 条。每次 `turn_started` 都会把前一个已完成 turn 保存为父节点;现有任务会结合快照时间和 `session_reset_to_turn` 审计记录回填,因此历史 Reset 后的第一轮会指向所选 turn,而不是时间上相邻的废弃路径。渲染器先基于完整历史计算 lane,再切分页面,从而跨分页正确显示延续和合并;内容缩进到第二列,状态或操作位于第三列。当前对话位置显示“当前”标记,其余 turn 提供 `Reset` 按钮。翻页和成功的 Reset 都会更新同一张卡片、保持与打开卡片时的任务绑定,并把“当前”标记移动到所选 turn;成功提示会用 Prompt 摘要、完成时间和 Turn ID 标明目标轮次。Agent Bot 会持久化每个 turn 原本所属的 App Server thread,从所选 turn fork,并替换当前任务的远端 thread 绑定;本地任务 ID、标题、Agent、项目目录、运行设置和聊天路由保持不变。所选 turn 之后已经完成的快照不会被删除,因此卡片会同时显示旧路径保留的轮次和 Reset 后新分支产生的轮次。当前任务仍在执行时会拒绝 Reset,本地文件修改也不会被回退。
|
|
227
|
+
|
|
225
228
|
完成持久化消息去重占位后,Agent Bot 会等待 `OnIt` 表情添加成功,再进行聊天信息持久化、图片下载、队列等待、命令执行或 Runtime 调用。Turn 成功、失败或取消时分别替换为 `DONE`、`ERROR` 或 `CrossMark`。表情操作失败会记录日志,但不会阻塞任务。
|
|
226
229
|
|
|
227
230
|
富文本图片会下载到输入图片缓存,并以 `localImage` 传给 App Server。纯图片消息使用默认 Prompt `请查看这张图片`。ACP Runtime 不支持图片输入时会明确报错。
|
|
228
231
|
|
|
229
232
|
## 任务、项目与外部 App Server 工作
|
|
230
233
|
|
|
231
|
-
`/sessions` 通过 `thread/list` 读取每个已配置 App Server Agent 的任务。对于 Codex,可发现同一 `CODEX_HOME` 下由 Codex Desktop、CLI、Agent Bot 或其他 App Server 客户端创建的任务;其他 Agent
|
|
234
|
+
`/sessions` 通过 `thread/list` 读取每个已配置 App Server Agent 的任务。对于 Codex,可发现同一 `CODEX_HOME` 下由 Codex Desktop、CLI、Agent Bot 或其他 App Server 客户端创建的任务;其他 Agent 通过同一协议暴露各自的任务存储。多 Agent 任务合并并全局排序后,每页显示 5 个;`Previous` 和 `Next` 会替换当前卡片内容而不是继续追加任务,同时保留搜索条件和全局任务序号。
|
|
232
235
|
|
|
233
236
|
每个任务条目都提供 `NewGroup` 和 `ForkGroup` 回调。回调数据保留所选任务 ID 与来源上下文,并使用飞书操作者的 `open_id` 邀请用户进入新群。`NewGroup` 解析所选任务的项目和执行设置;`ForkGroup` 解析该任务最新可用的已完成 turn。
|
|
234
237
|
|
|
@@ -290,9 +293,13 @@ Supervisor、Worker、替换 Supervisor 和 Console Worker 默认启用 Node fat
|
|
|
290
293
|
|
|
291
294
|
飞书 `/restart` 命令默认使用这条安全重启路径;`/restart --force` 会立即重启,并可能中断正在执行的任务。该命令拒绝其他所有参数。新消息会重置静默计时,CLI 的 `--immediate` 和 `--force` 也会跳过这些检查。退出码 `75` 表示主动 worker 重启。
|
|
292
295
|
|
|
296
|
+
待执行的安全重启计划会收集每一个触发它的精确会话路由。收到任何用户消息时,包括斜杠命令,都会把所属基础私聊或群聊标为活跃;每次状态更新还会加入此前 1 分钟内活跃的所有已知会话。每个已加入的会话都会收到各自的状态卡片和真正开始重启前的提示,而且即使之后超过 1 分钟未活跃,也会一直保留到本次计划结束。同一路由会去重;如果话题路由最初缺少消息锚点,后续带锚点的触发会补全它。话题请求会保留原消息 ID,并通过话题回复发送,避免重启状态落到群主会话;话题中的最近活动记在所属普通群,只有显式从话题触发时才向该话题本身发送。后续请求也会更新重启原因。替换进程前,Agent Bot 会提取并去重所有已加入路由所属的普通群,经替换 Supervisor 传给 Worker,再把这些群加入服务级启动卡目标。Supervisor 会保留该列表,直到 Worker 连续稳定运行 60 秒,避免启动阶段异常退出并重新拉起时丢失必发群。
|
|
297
|
+
|
|
298
|
+
当 CLI 重启属于某个具体任务时,使用 `agentbot server restart --task <任务>`。任务序号、完整 ID 和不冲突的 ID 前缀会在发送控制请求前解析。未传 `--task` 时,只有所有运行中任务都属于同一个会话,Server 才会自动推断安全重启状态的发送目标;如果同时有多个运行中的会话,请求会被拒绝。没有运行中任务的本地 CLI 重启没有等待状态所属会话。
|
|
299
|
+
|
|
293
300
|
首次安全重启状态卡会延迟 3 秒发送,让任务最终回答尽可能先到达;延迟期间的状态变化会合并到首张卡片。该延迟不阻塞调度器轮询,真正关闭前会立即 flush 尚未发送的卡片。
|
|
294
301
|
|
|
295
|
-
等待中的安全重启状态卡底部带有 `Cancel` 操作。回调会携带调度器单调递增的计划 ID
|
|
302
|
+
等待中的安全重启状态卡底部带有 `Cancel` 操作。回调会携带调度器单调递增的计划 ID,因此旧卡片不会取消较新的重启。取消成功后,所有已加入会话中的状态卡都会原地更新并移除按钮;调度器进入不可逆的正在重启阶段后也不再显示该按钮。
|
|
296
303
|
|
|
297
304
|
本地控制服务负责修改任务、执行指定任务的 NewGroup 和 ForkGroup,以及请求服务重启;只读任务查询直接访问 SQLite。
|
|
298
305
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyou007/agent-bot",
|
|
3
|
-
"version": "0.1.13-alpha.
|
|
3
|
+
"version": "0.1.13-alpha.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@keyou007/agent-bot",
|
|
9
|
-
"version": "0.1.13-alpha.
|
|
9
|
+
"version": "0.1.13-alpha.1",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@agentclientprotocol/sdk": "1.2.1",
|
package/package.json
CHANGED
|
@@ -52,7 +52,9 @@ After the maintenance choice, interactive initialization requires the user to se
|
|
|
52
52
|
|
|
53
53
|
Initialization creates the Agent Bot home, `config.yaml`, `.env`, `data/`, and `logs/` from the bundled examples. If Feishu credentials are absent, it starts Feishu one-click app registration, prints a QR code followed by its authorization URL, waits for the user to approve the request, and stores the returned app ID and secret in `.env`. It then audits the app's currently published tenant permissions, event subscriptions, and callbacks. Core and optional missing items produce separate configuration challenges. Core configuration is polled until ready, except that the manually configured all-group-message permission may be explicitly skipped with `Y`. For optional configuration, the CLI prints the QR code followed by the authorization URL and immediately waits up to five minutes for activation. The only terminal choice is also `Y`, which skips optional authorization and continues initialization.
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
An existing app may have complete credentials but no `FEISHU_USER_OPEN_ID`. In that state, ask the user to send the bot a direct message. Agent Bot atomically persists the first valid private-chat sender to the Profile `.env`; group messages and later users cannot replace it. This identity is needed for startup-notification fallback and CLI-created group invitations.
|
|
56
|
+
|
|
57
|
+
Rerun `agentbot init` after upgrading Agent Bot. It non-destructively upgrades an existing `config.yaml` by adding settings missing from the current bundled template while preserving user values, comments, custom Agents, and intentionally omitted template Agents. It also appends active variables missing from the bundled `.env.example` without changing existing values, comments, line endings, or enabling commented optional variables. It repeats the Agent version checks and Feishu configuration audit. If the selected Profile server is already running, initialization schedules a safe restart so the current installed code and refreshed configuration take effect after active work finishes. Invalid YAML is left untouched and reported as an error. Use `init --reset` only for an explicitly requested full Profile reset.
|
|
56
58
|
|
|
57
59
|
Core messaging configuration includes `im:message.group_msg`, not merely the narrower group-mention scope. Accept `im:message.group_msg:readonly` when an existing app version reports that alias. Feishu cannot add `im:message.group_msg` through the one-click launcher, so Agent Bot excludes it from `addons` and prints a QR code plus a direct filtered Developer Console permission-page URL. The user should add the permission manually, publish the app version, and complete tenant approval if required. Entering `Y` skips only this permission wait and continues with a partial configuration; the bot then cannot respond to ordinary group messages that do not mention it.
|
|
58
60
|
|
|
@@ -99,12 +101,14 @@ Use these commands:
|
|
|
99
101
|
```powershell
|
|
100
102
|
agentbot server start
|
|
101
103
|
agentbot server status
|
|
102
|
-
agentbot server restart --reason "reason for restart"
|
|
104
|
+
agentbot server restart --task <current-task-id> --reason "reason for restart"
|
|
103
105
|
agentbot server stop
|
|
104
106
|
```
|
|
105
107
|
|
|
106
108
|
Treat `server restart` as the normal restart path. It schedules a safe restart and waits for active tasks, pending final-message delivery, and a quiet inbound-message window. The first safe-restart status card is delayed by three seconds so the active task's final response can usually be delivered first; this presentation delay does not postpone scheduler polling. While the restart is pending, its status card has a bottom `Cancel` button that conditionally cancels that exact schedule and updates the card in place.
|
|
107
109
|
|
|
110
|
+
When scheduling from an Agent Bot-hosted task, pass `--task <current-task-id>`. This adds the task's exact Lark conversation, including its topic, to the pending restart's notification set. Every conversation that triggers the same pending restart plus every conversation active during the previous minute receives its own status card and restarting acknowledgement. Once enrolled, a route remains in the set until that restart ends; repeated routes are deduplicated. Resolve the current ID with `agentbot task list --status running` when necessary. If `--task` is omitted, the Server infers a target only when all running tasks belong to one conversation and rejects the request when multiple running conversations make ownership ambiguous. Startup cards are service-wide notices: every known private chat receives one on every startup, groups receive one when active in the previous minute or enrolled for the current safe restart, and topic routes receive the card in their parent group rather than inside the topic.
|
|
111
|
+
|
|
108
112
|
On Windows, initial start, safe or immediate replacement, and every crash-driven Worker launch reload the latest Machine and User environment variables. After changing `PATH` or installing a CLI shim, finish the current work and use the normal restart path. Agent Bot keeps `AGENT_BOT_*` and `FEISHU_*` process-local so Profile selection and bot credentials do not leak between isolated instances.
|
|
109
113
|
|
|
110
114
|
In Feishu, `/restart` schedules the same safe restart. Use `/restart --force` only when the user explicitly accepts interruption; the command accepts no other arguments.
|
|
@@ -188,10 +192,12 @@ Use `/forkgroup [title]` in Feishu to fork the current position into a newly cre
|
|
|
188
192
|
|
|
189
193
|
Use `/new [title] --nodir` to force a new App Server Projectless task even when the current task belongs to a project. `--nodir` and `--dir <cwd>` are mutually exclusive; omitting both preserves the normal project-shape inheritance behavior.
|
|
190
194
|
|
|
191
|
-
|
|
195
|
+
The Feishu `/sessions` card shows five tasks per page and updates the same card through `Previous` and `Next`; it never expands earlier pages into one long card. Every task has `New`, `NewGroup`, `Fork`, and `ForkGroup` actions. `New` inherits the source task's project directory, model, reasoning effort, and permission mode, creates a new task, and switches the current chat to it without stopping or forking the source task. `NewGroup` applies the same inheritance in a newly created private group. `Fork` switches the current chat to a branch, while `ForkGroup` binds the branch to a newly created private group; both fork actions use the selected task's latest available completed turn without interrupting an active turn.
|
|
192
196
|
|
|
193
197
|
Use `/fork [number-or-task-id]` in Feishu to branch from the current or specified App Server task and switch to the new branch. If the source task is running, Agent Bot forks from its latest completed turn and leaves the active turn running; it rejects only when the task has no completed turn yet. Every fork request sends `excludeTurns: true` to the App Server so the response omits populated turns without removing turns from the forked history. Older App Servers that explicitly reject this experimental field are retried once without it; ambiguous failures such as timeouts or disconnects are not retried.
|
|
194
198
|
|
|
199
|
+
Successfully completed Feishu thinking cards include a bottom `Reset` action followed by its compact warning. `/turns` opens the current task's completed-turn history with the Reset-effect warning at the top and 10 turns per page. It renders each entry as an indented numbered node in a commit graph whose lanes follow persisted parent turns, including Reset branches and merges across page boundaries. A successful Reset refreshes that card in place, moves the current marker to the selected turn, and sends a confirmation containing the target Prompt summary, completion time, and Turn ID. Reset forks the selected turn's original App Server thread through that turn and replaces the current task's remote thread binding in place, preserving the local task ID, title, Agent, project directory, execution settings, and chat route. Completed snapshots after the selected point remain visible together with later turns from the new branch. Reset is available only when the card still belongs to the current task and the task is idle. It restores conversation context only and never reverts local files.
|
|
200
|
+
|
|
195
201
|
Status cards include a `Refresh` action that reads the latest task state and updates the same card in place without switching tasks or sending another card.
|
|
196
202
|
|
|
197
203
|
Use `/agent`, `/provider`, `/model`, `/thinking`, or `/permissions` in Feishu to open the same interactive Card 2.0 execution-settings card with the corresponding tab active. The card exposes Provider, Model, Thinking, and Permission tabs whenever a current task exists, plus an Agent tab when multiple agents are configured. Each non-current option has an English `Switch` action. Selecting an option refreshes the same card in place. Agent changes affect future tasks; the other settings apply from the next request. Model changes automatically choose a compatible reasoning effort when necessary.
|
|
@@ -213,7 +219,7 @@ Use `--context <key>` or `--status <status>` to narrow `task list`. Use `--profi
|
|
|
213
219
|
After changing Agent Bot code, run the relevant tests, typecheck, and build. If a running service must pick up the result, schedule:
|
|
214
220
|
|
|
215
221
|
```powershell
|
|
216
|
-
agentbot server restart --reason "brief description of the completed change"
|
|
222
|
+
agentbot server restart --task <current-task-id> --reason "brief description of the completed change"
|
|
217
223
|
```
|
|
218
224
|
|
|
219
225
|
Do not replace the worker immediately while a task is active. A later incoming task must not cancel the pending safe restart; allow the scheduler to wait until the service becomes safely idle again.
|
package/src/cli/ServerStarter.ts
CHANGED
|
@@ -11,7 +11,11 @@ import {
|
|
|
11
11
|
import { refreshedSystemEnvironment } from "../supervision/systemEnvironment.js";
|
|
12
12
|
import { controlEndpoint } from "./controlProtocol.js";
|
|
13
13
|
import { cliText } from "./i18n.js";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
isServerReachable,
|
|
16
|
+
isServerRunning,
|
|
17
|
+
sendControlRequest,
|
|
18
|
+
} from "./LocalControlClient.js";
|
|
15
19
|
|
|
16
20
|
export interface ServerStartResult {
|
|
17
21
|
status: "started" | "already-running";
|
|
@@ -19,6 +23,7 @@ export interface ServerStartResult {
|
|
|
19
23
|
|
|
20
24
|
export type InitializationServerResult =
|
|
21
25
|
| ServerStartResult
|
|
26
|
+
| { status: "restart-scheduled" }
|
|
22
27
|
| { status: "skipped"; reason: "feishu-skipped" };
|
|
23
28
|
|
|
24
29
|
export interface ServerStarterDependencies {
|
|
@@ -33,6 +38,7 @@ const SERVER_START_TIMEOUT_MS = 45_000;
|
|
|
33
38
|
interface InitializationServerDependencies {
|
|
34
39
|
loadConfig(configPath?: string): AppConfig;
|
|
35
40
|
startServer(config: AppConfig): Promise<ServerStartResult>;
|
|
41
|
+
restartServer(config: AppConfig): Promise<void>;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
export async function startInitializedServer(
|
|
@@ -42,7 +48,11 @@ export async function startInitializedServer(
|
|
|
42
48
|
if (options.skipFeishu) {
|
|
43
49
|
return { status: "skipped", reason: "feishu-skipped" };
|
|
44
50
|
}
|
|
45
|
-
|
|
51
|
+
const config = dependencies.loadConfig(options.configPath);
|
|
52
|
+
const result = await dependencies.startServer(config);
|
|
53
|
+
if (result.status !== "already-running") return result;
|
|
54
|
+
await dependencies.restartServer(config);
|
|
55
|
+
return { status: "restart-scheduled" };
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
export async function startServer(
|
|
@@ -116,6 +126,19 @@ const defaultDependencies: ServerStarterDependencies = {
|
|
|
116
126
|
const defaultInitializationDependencies: InitializationServerDependencies = {
|
|
117
127
|
loadConfig,
|
|
118
128
|
startServer: (config) => startServer(config),
|
|
129
|
+
restartServer: async (config) => {
|
|
130
|
+
const response = await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
131
|
+
action: "server_restart",
|
|
132
|
+
mode: "safe",
|
|
133
|
+
reason: "Agent Bot 初始化完成,需要加载当前版本",
|
|
134
|
+
});
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
throw new Error(response.message?.trim() || cliText(
|
|
137
|
+
"Could not schedule the Agent Bot restart after initialization.",
|
|
138
|
+
"初始化完成后无法安排 Agent Bot 重启。",
|
|
139
|
+
));
|
|
140
|
+
}
|
|
141
|
+
},
|
|
119
142
|
};
|
|
120
143
|
|
|
121
144
|
async function waitForServer(endpoint: string, timeoutMs: number): Promise<boolean> {
|