@keyou007/agent-bot 0.1.22 → 0.1.24-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.
Files changed (77) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +26 -2
  3. package/README.zh.md +26 -2
  4. package/config.example.yaml +5 -0
  5. package/dist/cli/controlProtocol.js.map +1 -1
  6. package/dist/cli/help.js +4 -0
  7. package/dist/cli/help.js.map +1 -1
  8. package/dist/cli/taskGroupOptions.js +27 -0
  9. package/dist/cli/taskGroupOptions.js.map +1 -1
  10. package/dist/cli.js +21 -1
  11. package/dist/cli.js.map +1 -1
  12. package/dist/codex/CodexProcessManager.js +19 -3
  13. package/dist/codex/CodexProcessManager.js.map +1 -1
  14. package/dist/codex/CodexRuntime.js +92 -9
  15. package/dist/codex/CodexRuntime.js.map +1 -1
  16. package/dist/commands/CommandRouter.js +27 -0
  17. package/dist/commands/CommandRouter.js.map +1 -1
  18. package/dist/config/loadConfig.js +4 -0
  19. package/dist/config/loadConfig.js.map +1 -1
  20. package/dist/config/schema.js +1 -0
  21. package/dist/config/schema.js.map +1 -1
  22. package/dist/feishu/CardRenderer.js +31 -2
  23. package/dist/feishu/CardRenderer.js.map +1 -1
  24. package/dist/feishu/FeishuMessageClient.js +6 -3
  25. package/dist/feishu/FeishuMessageClient.js.map +1 -1
  26. package/dist/feishu/MergedForwardMessage.js +17 -7
  27. package/dist/feishu/MergedForwardMessage.js.map +1 -1
  28. package/dist/index.js +50 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/local-files/TurnPreviewPage.js +106 -87
  31. package/dist/local-files/TurnPreviewPage.js.map +1 -1
  32. package/dist/proxy/ConversationExport.js +41 -0
  33. package/dist/proxy/ConversationExport.js.map +1 -0
  34. package/dist/proxy/ProxySessionController.js +118 -7
  35. package/dist/proxy/ProxySessionController.js.map +1 -1
  36. package/dist/state/StateStore.js +65 -0
  37. package/dist/state/StateStore.js.map +1 -1
  38. package/dist/state/migrations.js +12 -0
  39. package/dist/state/migrations.js.map +1 -1
  40. package/dist/updates/AutomaticUpdatePreparer.js +66 -0
  41. package/dist/updates/AutomaticUpdatePreparer.js.map +1 -0
  42. package/dist/updates/DailyUpdateMonitor.js +261 -0
  43. package/dist/updates/DailyUpdateMonitor.js.map +1 -0
  44. package/dist/updates/PublishedRelease.js +43 -0
  45. package/dist/updates/PublishedRelease.js.map +1 -0
  46. package/dist/updates/types.js +2 -0
  47. package/dist/updates/types.js.map +1 -0
  48. package/docs/technical-reference.md +4 -0
  49. package/docs/technical-reference.zh.md +4 -0
  50. package/npm-shrinkwrap.json +2 -2
  51. package/package.json +1 -1
  52. package/skills/agent-bot/SKILL.md +11 -0
  53. package/src/cli/controlProtocol.ts +9 -0
  54. package/src/cli/help.ts +4 -0
  55. package/src/cli/taskGroupOptions.ts +33 -3
  56. package/src/cli.ts +21 -0
  57. package/src/codex/CodexProcessManager.ts +21 -4
  58. package/src/codex/CodexRuntime.ts +104 -12
  59. package/src/commands/CommandRouter.ts +24 -0
  60. package/src/commands/commandTypes.ts +1 -0
  61. package/src/config/loadConfig.ts +4 -0
  62. package/src/config/schema.ts +4 -1
  63. package/src/feishu/CardRenderer.ts +34 -2
  64. package/src/feishu/FeishuMessageClient.ts +6 -2
  65. package/src/feishu/MergedForwardMessage.ts +29 -7
  66. package/src/feishu/types.ts +1 -1
  67. package/src/index.ts +49 -0
  68. package/src/local-files/TurnPreviewPage.ts +105 -88
  69. package/src/proxy/ConversationExport.ts +43 -0
  70. package/src/proxy/ProxySessionController.ts +166 -8
  71. package/src/runtime/types.ts +6 -0
  72. package/src/state/StateStore.ts +69 -0
  73. package/src/state/migrations.ts +12 -0
  74. package/src/updates/AutomaticUpdatePreparer.ts +76 -0
  75. package/src/updates/DailyUpdateMonitor.ts +252 -0
  76. package/src/updates/PublishedRelease.ts +44 -0
  77. package/src/updates/types.ts +19 -0
@@ -0,0 +1,261 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { compareSemver } from "../cli/SelfUpdater.js";
3
+ import { CardRenderer } from "../feishu/CardRenderer.js";
4
+ import { stableVersionSchema } from "./PublishedRelease.js";
5
+ export function scheduleDailyUpdate(now, random = Math.random) {
6
+ const start = new Date(now);
7
+ if (start.getHours() >= 17)
8
+ start.setDate(start.getDate() + 1);
9
+ start.setHours(10, 0, 0, 0);
10
+ const end = new Date(start);
11
+ end.setHours(17, 0, 0, 0);
12
+ const lower = Math.max(now, start.getTime());
13
+ const dueAt = lower + Math.floor(Math.min(0.999999999, Math.max(0, random())) * (end.getTime() - lower));
14
+ return { day: localDay(start.getTime()), dueAt, checked: false };
15
+ }
16
+ function localDay(now) {
17
+ const date = new Date(now);
18
+ return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
19
+ }
20
+ export class DailyUpdateMonitor {
21
+ options;
22
+ renderer = new CardRenderer();
23
+ now;
24
+ timer;
25
+ stopped = true;
26
+ work = Promise.resolve();
27
+ busy = false;
28
+ cardWrites = Promise.resolve();
29
+ lastCardState = new Map();
30
+ constructor(options) {
31
+ this.options = options;
32
+ this.now = options.now ?? Date.now;
33
+ }
34
+ async start() {
35
+ if (!this.stopped)
36
+ return;
37
+ this.stopped = false;
38
+ this.work = this.recover().catch((error) => this.logError(error));
39
+ await this.work;
40
+ if (this.stopped)
41
+ return;
42
+ this.timer = setInterval(() => this.poll(), 1_000);
43
+ this.timer.unref();
44
+ this.poll();
45
+ }
46
+ async stop() {
47
+ this.stopped = true;
48
+ clearInterval(this.timer);
49
+ await this.work;
50
+ await this.cardWrites;
51
+ }
52
+ async cancel(action) {
53
+ if (this.stopped)
54
+ throw new Error("自动更新已停止。");
55
+ const notice = this.options.store.getUpdateNotice(String(action.value.version ?? ""));
56
+ if (!notice || !action.messageId || action.userId !== notice.userOpenId
57
+ || action.userId !== this.options.userOpenId() || action.value.token !== notice.token
58
+ || (notice.messageId && notice.messageId !== action.messageId)) {
59
+ throw new Error("更新卡片已失效或无权操作。");
60
+ }
61
+ if (notice.status === "cancelled")
62
+ return;
63
+ if ((notice.status !== "countdown" && notice.status !== "announcing")
64
+ || (notice.deadline !== undefined && this.now() >= notice.deadline)) {
65
+ throw new Error("取消倒计时已结束,请查看安全重启卡片中的更新状态。");
66
+ }
67
+ this.options.store.saveUpdateNotice({ ...notice, messageId: action.messageId, status: "cancelled" });
68
+ this.options.logger.info({ version: notice.version }, "Cancelled automatic Agent Bot update.");
69
+ await this.refreshCard(notice.version);
70
+ }
71
+ async cancelPreparedUpdate(version) {
72
+ const notice = this.options.store.getUpdateNotice(version);
73
+ if (!notice || (notice.status !== "preparing" && notice.status !== "scheduled"))
74
+ return;
75
+ this.options.store.saveUpdateNotice({ ...notice, status: "cancelled" });
76
+ await this.refreshCard(version).catch((error) => this.logError(error));
77
+ }
78
+ poll() {
79
+ if (this.stopped || this.busy)
80
+ return;
81
+ this.busy = true;
82
+ this.work = this.tick().catch((error) => this.logError(error)).finally(() => { this.busy = false; });
83
+ }
84
+ async tick() {
85
+ for (const notice of this.options.store.listPendingUpdateNotices()) {
86
+ if (this.stopped)
87
+ return;
88
+ if (notice.status !== "countdown")
89
+ continue;
90
+ if (notice.userOpenId !== this.options.userOpenId()) {
91
+ this.options.store.saveUpdateNotice({ ...notice, status: "cancelled" });
92
+ await this.refreshCard(notice.version);
93
+ }
94
+ else if (this.now() < notice.deadline) {
95
+ try {
96
+ await this.refreshCard(notice.version);
97
+ }
98
+ catch (error) {
99
+ await this.fail(notice.version, error);
100
+ }
101
+ }
102
+ else {
103
+ await this.apply(notice);
104
+ }
105
+ }
106
+ if (this.stopped)
107
+ return;
108
+ const now = this.now();
109
+ let schedule = this.options.store.getUpdateCheckSchedule();
110
+ if (!schedule || schedule.day !== localDay(now)) {
111
+ // Keep tomorrow's persisted random slot when starting after the daily window.
112
+ if (!schedule || schedule.dueAt <= now) {
113
+ schedule = scheduleDailyUpdate(now, this.options.random);
114
+ this.options.store.saveUpdateCheckSchedule(schedule);
115
+ this.options.logger.info({ dueAt: new Date(schedule.dueAt).toISOString() }, "Scheduled daily Agent Bot update check.");
116
+ }
117
+ }
118
+ if (schedule.checked || now < schedule.dueAt)
119
+ return;
120
+ const hour = new Date(now).getHours();
121
+ if (hour < 10 || hour >= 17)
122
+ return;
123
+ const owner = this.options.userOpenId();
124
+ if (!owner || this.options.hasPendingUpdate())
125
+ return;
126
+ this.options.store.saveUpdateCheckSchedule({ ...schedule, checked: true });
127
+ const version = stableVersionSchema.parse(await this.options.readLatest());
128
+ if (this.stopped || compareSemver(version, this.options.currentVersion) <= 0
129
+ || this.options.store.getUpdateNotice(version))
130
+ return;
131
+ const notes = await this.options.readNotes(version);
132
+ if (this.stopped || owner !== this.options.userOpenId())
133
+ return;
134
+ if (!notes.trim())
135
+ throw new Error(`Release notes are unavailable for Agent Bot ${version}.`);
136
+ let supportError;
137
+ try {
138
+ await this.options.checkSupport();
139
+ }
140
+ catch (error) {
141
+ supportError = error instanceof Error ? error.message : String(error);
142
+ }
143
+ if (this.stopped || owner !== this.options.userOpenId())
144
+ return;
145
+ await this.announce({
146
+ version, currentVersion: this.options.currentVersion, userOpenId: owner,
147
+ token: randomUUID(), notes, status: supportError ? "failed" : "announcing", error: supportError,
148
+ });
149
+ }
150
+ async announce(notice) {
151
+ this.options.store.saveUpdateNotice(notice);
152
+ try {
153
+ const messageId = await this.options.outbound.sendInteractiveCard(`open_id:${notice.userOpenId}`, this.renderer.renderUpdateNotice(notice, this.now()), notice.token);
154
+ if (!messageId)
155
+ throw new Error("Update notification delivery was not confirmed; automatic update stopped.");
156
+ const current = this.options.store.getUpdateNotice(notice.version);
157
+ this.options.store.saveUpdateNotice({
158
+ ...current, messageId, status: current.status === "announcing" ? "countdown" : current.status,
159
+ deadline: current.status === "announcing" ? this.now() + 60_000 : undefined,
160
+ });
161
+ this.options.logger.info({ version: notice.version }, "Delivered private Agent Bot update notice.");
162
+ if (!this.stopped)
163
+ await this.refreshCard(notice.version);
164
+ }
165
+ catch (error) {
166
+ await this.fail(notice.version, error);
167
+ }
168
+ }
169
+ async apply(notice) {
170
+ if (this.stopped)
171
+ return;
172
+ const current = this.options.store.getUpdateNotice(notice.version);
173
+ if (current.status !== "countdown")
174
+ return;
175
+ this.options.store.saveUpdateNotice({ ...current, status: "preparing" });
176
+ try {
177
+ await this.refreshCard(current.version);
178
+ if (this.stopped)
179
+ return;
180
+ if (this.options.hasPendingUpdate())
181
+ throw new Error("已有更新或重启正在等待执行,本次自动更新已停止。");
182
+ await this.options.applyUpdate(current.version);
183
+ const latest = this.options.store.getUpdateNotice(current.version);
184
+ if (latest.status !== "cancelled")
185
+ this.options.store.saveUpdateNotice({ ...latest, status: "scheduled" });
186
+ }
187
+ catch (error) {
188
+ await this.fail(current.version, error);
189
+ return;
190
+ }
191
+ if (!this.stopped)
192
+ await this.refreshCard(current.version).catch((error) => this.logError(error));
193
+ }
194
+ async recover() {
195
+ for (const notice of this.options.store.listPendingUpdateNotices()) {
196
+ if (this.stopped)
197
+ return;
198
+ let recovered;
199
+ if (compareSemver(this.options.currentVersion, notice.version) >= 0) {
200
+ recovered = { ...notice, status: "completed" };
201
+ }
202
+ else if (notice.userOpenId !== this.options.userOpenId()) {
203
+ recovered = { ...notice, status: "cancelled" };
204
+ }
205
+ else if (notice.status === "countdown" && notice.messageId) {
206
+ // Renew the cancellation window on the same card after downtime.
207
+ recovered = { ...notice, deadline: this.now() + 60_000 };
208
+ }
209
+ else if ((notice.status === "scheduled" || notice.status === "preparing") && this.options.pendingVersion() === notice.version) {
210
+ recovered = { ...notice, status: "scheduled" };
211
+ }
212
+ else {
213
+ recovered = { ...notice, status: "failed", error: "上次自动更新被中断,未自动重试。" };
214
+ }
215
+ this.options.store.saveUpdateNotice(recovered);
216
+ try {
217
+ await this.refreshCard(recovered.version);
218
+ if (recovered.status === "countdown" && !this.stopped) {
219
+ const current = this.options.store.getUpdateNotice(recovered.version);
220
+ if (current.status === "countdown")
221
+ this.options.store.saveUpdateNotice({ ...current, deadline: this.now() + 60_000 });
222
+ }
223
+ }
224
+ catch (error) {
225
+ if (recovered.status === "countdown")
226
+ await this.fail(recovered.version, error);
227
+ else
228
+ this.logError(error);
229
+ }
230
+ }
231
+ }
232
+ async fail(version, error) {
233
+ const notice = this.options.store.getUpdateNotice(version);
234
+ if (notice.status !== "cancelled") {
235
+ this.options.store.saveUpdateNotice({ ...notice, status: "failed", error: error instanceof Error ? error.message : String(error) });
236
+ }
237
+ this.logError(error);
238
+ if (!this.stopped)
239
+ await this.refreshCard(version).catch((cardError) => this.logError(cardError));
240
+ }
241
+ refreshCard(version) {
242
+ const write = this.cardWrites.then(async () => {
243
+ if (this.stopped)
244
+ return;
245
+ const notice = this.options.store.getUpdateNotice(version);
246
+ if (!notice.messageId)
247
+ return;
248
+ const key = `${notice.status}:${notice.status === "countdown" ? Math.ceil((notice.deadline - this.now()) / 5_000) : ""}`;
249
+ if (this.lastCardState.get(version) === key)
250
+ return;
251
+ await this.options.outbound.updateInteractiveCard(notice.messageId, this.renderer.renderUpdateNotice(notice, this.now()));
252
+ this.lastCardState.set(version, key);
253
+ });
254
+ this.cardWrites = write.catch(() => undefined);
255
+ return write;
256
+ }
257
+ logError(error) {
258
+ this.options.logger.warn({ error }, "Automatic Agent Bot update did not complete.");
259
+ }
260
+ }
261
+ //# sourceMappingURL=DailyUpdateMonitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DailyUpdateMonitor.js","sourceRoot":"","sources":["../../src/updates/DailyUpdateMonitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { z } from "zod";
2
+ import { AGENT_BOT_PACKAGE_NAME } from "../cli/SelfUpdater.js";
3
+ export const stableVersionSchema = z.string().regex(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u);
4
+ export async function readLatestStableVersion() {
5
+ const text = await fetchText(`https://registry.npmjs.org/${encodeURIComponent(AGENT_BOT_PACKAGE_NAME)}/latest`, 256_000);
6
+ return z.object({ version: stableVersionSchema }).parse(JSON.parse(text)).version;
7
+ }
8
+ export async function readReleaseNotes(version) {
9
+ stableVersionSchema.parse(version);
10
+ const changelog = await fetchText(`https://raw.githubusercontent.com/keyou/agent-bot/v${version}/CHANGELOG.md`, 2_000_000);
11
+ return extractReleaseNotes(changelog, version);
12
+ }
13
+ export function extractReleaseNotes(changelog, version) {
14
+ const sections = changelog.split(/^## /mu).slice(1);
15
+ const section = sections.find((value) => value.split(/\r?\n/u, 1)[0]?.startsWith(`[${version}]`));
16
+ const notes = section?.includes("\n") ? section.slice(section.indexOf("\n") + 1).trim() : undefined;
17
+ if (!notes)
18
+ throw new Error(`Release notes are unavailable for Agent Bot ${version}.`);
19
+ return notes.length > 4_000 ? `${notes.slice(0, 3_900)}\n\n[完整更新日志](https://github.com/keyou/agent-bot/releases/tag/v${version})` : notes;
20
+ }
21
+ async function fetchText(url, maxBytes) {
22
+ const response = await fetch(url, { signal: AbortSignal.timeout(15_000) });
23
+ if (!response.ok || !response.body)
24
+ throw new Error(`Release request failed: HTTP ${response.status}.`);
25
+ const reader = response.body.getReader();
26
+ const chunks = [];
27
+ let size = 0;
28
+ try {
29
+ while (true) {
30
+ const { done, value } = await reader.read();
31
+ if (done)
32
+ return Buffer.concat(chunks).toString("utf8");
33
+ size += value.byteLength;
34
+ if (size > maxBytes)
35
+ throw new Error("Release response exceeds the size limit.");
36
+ chunks.push(value);
37
+ }
38
+ }
39
+ finally {
40
+ await reader.cancel();
41
+ }
42
+ }
43
+ //# sourceMappingURL=PublishedRelease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublishedRelease.js","sourceRoot":"","sources":["../../src/updates/PublishedRelease.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/updates/types.ts"],"names":[],"mappings":""}
@@ -54,6 +54,10 @@ Configuration path precedence:
54
54
 
55
55
  The default `.env` is always loaded from the Agent Bot home. YAML values in the form `${NAME}` are expanded from the process environment after `.env` loading.
56
56
 
57
+ When the Worker constructs its runtimes at startup, each recognized Codex App Server Agent also reads `.env` from its own Codex home. The home is selected from `agents.<name>.env.CODEX_HOME`, then the Worker environment's `CODEX_HOME`, then `~/.codex`. Home-relative paths are expanded and relative paths resolve against the Profile directory, matching the child working directory. The resolved home is passed to the Codex process; a `CODEX_HOME` assignment inside that `.env` cannot redirect it.
58
+
59
+ Codex environment values have the lowest priority: explicit Agent `env` values win over the Worker environment (including the Profile `.env`), which wins over Codex `.env`. Empty explicit values remain explicit. The existing main/explicit-Profile environment precedence is unchanged. The same resolved values are available to Agent Bot's Provider `/models` authentication and the Codex child. This does not mutate `process.env`, affect YAML expansion, or load Codex credentials into other Agents. Reserved Agent Bot and Feishu variables are still filtered before spawning the child. Missing files are ignored; unreadable files produce a warning containing only the path and error code. Values are loaded once per Worker startup; safely restart after changes.
60
+
57
61
  Relative `storage.sqlitePath` and `logging.path` values resolve against the directory containing the loaded configuration file. `defaults.cwd` resolves against the process startup directory.
58
62
 
59
63
  ## Initialization
@@ -54,6 +54,10 @@ CLI 通过 Node.js 国际化能力读取系统 Locale。以 `zh` 开头的 Local
54
54
 
55
55
  默认 `.env` 始终从 Agent Bot 用户目录加载。加载 `.env` 后,YAML 中的 `${NAME}` 会使用进程环境变量展开。
56
56
 
57
+ Worker 启动并创建运行时时,还会为识别为 Codex 的 App Server Agent 读取各自 Codex 目录中的 `.env`。目录依次取 `agents.<name>.env.CODEX_HOME`、Worker 环境中的 `CODEX_HOME`、`~/.codex`。支持展开 `~`,相对路径按 Profile 目录解析,与子进程工作目录一致。解析后的目录会传给 Codex 进程,文件内的 `CODEX_HOME` 赋值不能改变该目录。
58
+
59
+ Codex `.env` 优先级最低:Agent 显式 `env` 优先于 Worker 环境(含 Profile `.env`),Worker 环境优先于 Codex `.env`;显式空值也不会被覆盖。主 Profile 与显式 Profile 原有的环境变量优先级不变。Agent Bot 查询 Provider `/models` 的鉴权和 Codex 子进程共用这些值,但不会修改 `process.env`、影响 YAML 变量展开或把 Codex 凭据加载到其他 Agent。启动子进程前仍会过滤 Agent Bot 和飞书保留变量。文件不存在时忽略,无法读取时只记录路径和错误码。每次 Worker 启动读取一次,修改后需安全重启。
60
+
57
61
  相对形式的 `storage.sqlitePath` 和 `logging.path` 按配置文件所在目录解析;`defaults.cwd` 按进程启动目录解析。
58
62
 
59
63
  ## 初始化
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@keyou007/agent-bot",
3
- "version": "0.1.22",
3
+ "version": "0.1.24-alpha.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@keyou007/agent-bot",
9
- "version": "0.1.22",
9
+ "version": "0.1.24-alpha.0",
10
10
  "license": "GPL-3.0-only",
11
11
  "dependencies": {
12
12
  "@agentclientprotocol/sdk": "1.2.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keyou007/agent-bot",
3
- "version": "0.1.22",
3
+ "version": "0.1.24-alpha.0",
4
4
  "type": "module",
5
5
  "description": "Use Codex, TraeX, and compatible coding agents from Lark/Feishu.",
6
6
  "license": "GPL-3.0-only",
@@ -96,6 +96,15 @@ agentbot task forkgroup [title]
96
96
 
97
97
  Choose `new` or `newgroup` for fresh context. Choose `fork` or `forkgroup` when the new task must retain conversation history through the latest completed Turn. Forking must not interrupt an active source turn.
98
98
 
99
+ For lightweight context transfer, including between different Agents:
100
+
101
+ ```powershell
102
+ agentbot task clone [title] [--agent <name>]
103
+ agentbot task clonegroup [title] [--agent <name>]
104
+ ```
105
+
106
+ These commands export only user Prompts and final answers through the latest completed Turn to a local text file, then start a fresh task with instructions to read it, acknowledge, and wait. They do not copy tools, reasoning, images, or native session files, and never interrupt the source task. Without `--agent`, they use the conversation's default Agent, not necessarily the source Agent. Provider, model, and reasoning use the target Agent's saved defaults; project shape and permissions come from the source. `clone` switches the current conversation after import starts; `clonegroup` leaves it unchanged and creates a separate group. Both support `--json` and explicit `--task` targeting. Feishu equivalents are `/clone` and `/clonegroup` (`/cg`). ACP sources use only dialogue already saved by Agent Bot. The context file remains beside the Profile SQLite file under `context-transfers`; keep it available for the cloned task.
107
+
99
108
  Feishu `/sessions [keyword]` lists and searches tasks across projects and all Providers, not just the App Server's current Provider. Switching Provider does not hide existing tasks; listing still uses paged metadata and the latest Turn summary only.
100
109
 
101
110
  Fork creation stores source-task and branch-Turn references without synchronizing the full local Turn list. The Feishu Turn card reuses local records and loads only the summary pages needed for the requested page, not the entire history. `task turns` loads the first page if necessary and returns available local records. This does not change the Agent's inherited context. Retry listing Turns after a temporary history-read failure; unsupported summary pagination never falls back to downloading full history.
@@ -158,6 +167,8 @@ agentbot server stop
158
167
 
159
168
  `update` is only for npm-installed Agent Bot packages. It verifies the new package, sends a safe-restart card, waits for active tasks, and restarts immediately when the service is already idle. It automatically restores the prior version if activation fails. It refuses source checkouts and `npm link` installations.
160
169
 
170
+ The Feishu service also checks stable npm `latest` once daily at a persisted random time between 10:00 and 17:00 (server local time). A new version sends the owner a private release-notes card with 60 seconds to cancel. If not cancelled, preparation runs in the background and activation waits for tasks, final deliveries, and the quiet inbound window before restarting. Each version is notified once per Profile; cancellation survives restarts. An interrupted countdown resumes with a fresh minute on the same card. No private recipient, unconfirmed notification delivery, or missing release notes means no automatic installation. Source/npm-link protections still apply. Set `updates.enabled: false` in the Profile config and safely restart to disable checks. Do not bypass the countdown or idle gate.
171
+
161
172
  Autostart is Profile-specific. Use `server autostart enable` for login startup, `server autostart enable --linger` on Linux only when the user explicitly requests startup before login, and `server autostart disable` to remove registration without stopping the current Server. Disabling Agent Bot autostart must not disable Linux user lingering because other services may use it.
162
173
 
163
174
  Use safe restart by default. Prefer `agentbot task restart` when hosted so the current task is resolved automatically. Use `--immediate` or `task restart --force` only when the user explicitly accepts interruption.
@@ -43,6 +43,7 @@ export type ControlRequest =
43
43
  projectless?: boolean;
44
44
  }
45
45
  | { action: "task_fork"; localSessionId: string }
46
+ | { action: "task_clone" | "task_clone_group"; localSessionId: string; title?: string; agentName?: string }
46
47
  | {
47
48
  action: "task_switch";
48
49
  localSessionId: string;
@@ -102,6 +103,14 @@ export interface TaskGroupControlData {
102
103
  task: SessionRecord;
103
104
  }
104
105
 
106
+ export interface TaskCloneControlData {
107
+ sourceLocalSessionId: string;
108
+ contextFile: string;
109
+ turnCount: number;
110
+ task: SessionRecord;
111
+ group?: TaskGroupControlData["group"];
112
+ }
113
+
105
114
  export interface TaskDismissControlData {
106
115
  localSessionId: string;
107
116
  remoteSessionId: string;
package/src/cli/help.ts CHANGED
@@ -30,6 +30,8 @@ export function renderCliHelp(version: string, language: CliLanguage = cliLangua
30
30
  task newgroup [任务] [标题] 创建新群;支持 --agent、--dir、--nodir
31
31
  task fork [任务] 从任务最近完成的 Turn 创建分支
32
32
  task forkgroup [任务] [标题] 从任务 Fork 新群
33
+ task clone [任务] [标题] 用对话文件创建新任务;支持 --agent
34
+ task clonegroup [任务] [标题] 用对话文件创建新群;支持 --agent
33
35
  task switch [任务] [目标任务] 切换会话任务;--previous 切回上一任务
34
36
  task queue [任务] <提示词> 将提示词排队;nosteer 是别名
35
37
  task agent [任务] [名称] 查看或设置该会话的新任务默认 Agent
@@ -123,6 +125,8 @@ Task commands:
123
125
  task newgroup [task] [title] Create a group; supports --agent, --dir, --nodir
124
126
  task fork [task] Fork the task's latest completed Turn
125
127
  task forkgroup [task] [title] Fork a task into a new group
128
+ task clone [task] [title] Create a task from a conversation file; supports --agent
129
+ task clonegroup [task] [title] Create a group from a conversation file; supports --agent
126
130
  task switch [task] [target] Switch the conversation task; --previous selects the prior task
127
131
  task queue [task] <prompt> Queue a Prompt; nosteer is an alias
128
132
  task agent [task] [name] Show or set the conversation's default Agent for new tasks
@@ -113,8 +113,38 @@ export function parseTaskForkGroupOptions(
113
113
  };
114
114
  }
115
115
 
116
+ export function parseTaskCloneOptions(
117
+ input: string[],
118
+ action: "clone" | "clonegroup",
119
+ language: CliLanguage = cliLanguage,
120
+ ): TaskForkGroupOptions & { agentName?: string } {
121
+ const positionals: string[] = [];
122
+ let agentName: string | undefined;
123
+ let json = false;
124
+ for (let index = 0; index < input.length; index += 1) {
125
+ const argument = input[index]!;
126
+ if (argument === "--json") {
127
+ json = true;
128
+ } else if (argument === "--agent") {
129
+ if (agentName !== undefined) throw optionError(action, "--agent", language);
130
+ const name = input[++index];
131
+ if (!name?.trim() || name.startsWith("--")) throw new Error(cliText(
132
+ `task ${action} requires an Agent name after --agent.`,
133
+ `task ${action} 需要在 --agent 后指定 Agent 名称。`, language,
134
+ ));
135
+ agentName = name.trim();
136
+ } else {
137
+ if (argument.startsWith("--")) throw unsupportedOption(action, argument, language);
138
+ positionals.push(argument);
139
+ }
140
+ }
141
+ const [reference, ...title] = positionals;
142
+ requireTaskReference(action, reference, language);
143
+ return { reference, title: title.join(" ").trim() || undefined, agentName, json };
144
+ }
145
+
116
146
  function requireTaskReference(
117
- action: "new" | "newgroup" | "forkgroup",
147
+ action: "new" | "newgroup" | "forkgroup" | "clone" | "clonegroup",
118
148
  reference: string | undefined,
119
149
  language: CliLanguage,
120
150
  ): asserts reference is string {
@@ -127,7 +157,7 @@ function requireTaskReference(
127
157
  }
128
158
 
129
159
  function optionError(
130
- action: "new" | "newgroup",
160
+ action: "new" | "newgroup" | "clone" | "clonegroup",
131
161
  option: "--agent" | "--dir" | "--nodir",
132
162
  language: CliLanguage,
133
163
  ): Error {
@@ -147,7 +177,7 @@ function conflictingProjectOptions(action: "new" | "newgroup", language: CliLang
147
177
  }
148
178
 
149
179
  function unsupportedOption(
150
- action: "new" | "newgroup" | "forkgroup",
180
+ action: "new" | "newgroup" | "forkgroup" | "clone" | "clonegroup",
151
181
  option: string,
152
182
  language: CliLanguage,
153
183
  ): Error {
package/src/cli.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  controlEndpoint,
9
9
  type ControlResponse,
10
10
  type TaskAgentControlData,
11
+ type TaskCloneControlData,
11
12
  type TaskDirectoryControlData,
12
13
  type TaskDismissControlData,
13
14
  type TaskForkControlData,
@@ -108,6 +109,7 @@ import {
108
109
  import { isThreadContextKey } from "./feishu/contextKey.js";
109
110
  import { refreshedSystemEnvironment } from "./supervision/systemEnvironment.js";
110
111
  import {
112
+ parseTaskCloneOptions,
111
113
  parseTaskForkGroupOptions,
112
114
  parseTaskNewOptions,
113
115
  parseTaskNewGroupOptions,
@@ -1593,6 +1595,25 @@ async function taskCommand(input: string[]): Promise<void> {
1593
1595
  await serverCommand(["restart", "--task", target.session.localSessionId, ...translated]);
1594
1596
  return;
1595
1597
  }
1598
+ if (action === "clone" || action === "clonegroup") {
1599
+ const target = resolveTaskCommandTarget(allSessions, rest, action);
1600
+ const options = parseTaskCloneOptions([target.session.localSessionId, ...target.args], action);
1601
+ if (action === "clonegroup") requireCliGroupUser(config.feishu.userOpenId);
1602
+ const response = await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
1603
+ action: action === "clonegroup" ? "task_clone_group" : "task_clone",
1604
+ localSessionId: target.session.localSessionId,
1605
+ ...(options.title ? { title: options.title } : {}),
1606
+ ...(options.agentName ? { agentName: options.agentName } : {}),
1607
+ }, 120_000);
1608
+ if (!response.ok) throw new Error(response.message ?? cliText("Failed to clone the task.", "克隆任务失败。"));
1609
+ const result = response.data as TaskCloneControlData;
1610
+ if (options.json) printJson(result);
1611
+ else process.stdout.write(cliText(
1612
+ `Cloned task: ${result.task.title ?? result.task.localSessionId}\nAgent: ${result.task.agentName}\nContext: ${result.contextFile} (${result.turnCount} turns)\n${result.group ? `Group: ${result.group.name}\n` : ""}`,
1613
+ `克隆任务:${result.task.title ?? result.task.localSessionId}\nAgent:${result.task.agentName}\n上下文:${result.contextFile}(${result.turnCount} 轮)\n${result.group ? `群:${result.group.name}\n` : ""}`,
1614
+ ));
1615
+ return;
1616
+ }
1596
1617
  if (action === "newgroup") {
1597
1618
  const target = resolveTaskCommandTarget(allSessions, rest, action);
1598
1619
  const options = parseTaskNewGroupOptions([target.session.localSessionId, ...target.args]);
@@ -2,6 +2,7 @@ import type { ChildProcessWithoutNullStreams } from "node:child_process";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import readline from "node:readline";
5
+ import { config as loadDotEnv } from "dotenv";
5
6
  import type { Logger } from "pino";
6
7
  import { AppServerConnection } from "./AppServerConnection.js";
7
8
  import type { AppServerClient, AppServerClientProvider } from "./CodexRuntime.js";
@@ -12,6 +13,7 @@ import {
12
13
  import type { AgentProcessInfo } from "../runtime/types.js";
13
14
  import { spawnStdioCommand } from "../utils/spawnCommand.js";
14
15
  import { assertSupportedCodexVersion } from "./CodexVersion.js";
16
+ import { resolveUserPath } from "../config/paths.js";
15
17
 
16
18
  export class CodexProcessManager implements AppServerClientProvider {
17
19
  private static readonly RELEASE_TIMEOUT_MS = 5_000;
@@ -20,6 +22,7 @@ export class CodexProcessManager implements AppServerClientProvider {
20
22
  private version?: string;
21
23
  private agentFamily?: "codex" | "traex";
22
24
  private initializing?: Promise<AppServerClient>;
25
+ private readonly codexEnvironment: Record<string, string> = {};
23
26
  private readonly disconnectListeners = new Set<(error: Error) => void>();
24
27
 
25
28
  constructor(
@@ -28,7 +31,17 @@ export class CodexProcessManager implements AppServerClientProvider {
28
31
  private readonly env: Record<string, string>,
29
32
  private readonly logger: Logger,
30
33
  private readonly environmentContext: () => AgentEnvironmentContext = () => ({}),
31
- ) {}
34
+ ) {
35
+ if (this.getAgentFamily() !== "codex") return;
36
+ const envPath = path.join(this.getCodexHome(), ".env");
37
+ const result = loadDotEnv({ path: envPath, processEnv: this.codexEnvironment, quiet: true });
38
+ if (result.error && (result.error as NodeJS.ErrnoException).code !== "ENOENT") {
39
+ this.logger.warn(
40
+ { path: envPath, code: (result.error as NodeJS.ErrnoException).code },
41
+ "Could not load the Codex environment file; using configured and inherited values.",
42
+ );
43
+ }
44
+ }
32
45
 
33
46
  async getClient(): Promise<AppServerClient> {
34
47
  if (this.initializing) return this.initializing;
@@ -45,10 +58,12 @@ export class CodexProcessManager implements AppServerClientProvider {
45
58
  private async startClient(): Promise<AppServerClient> {
46
59
  this.version = undefined;
47
60
  const environmentContext = this.environmentContext();
61
+ const agentEnvironment = { ...this.env };
62
+ if (this.getAgentFamily() === "codex") agentEnvironment.CODEX_HOME = this.getCodexHome();
48
63
  const child = spawnStdioCommand(
49
64
  this.command,
50
65
  this.args,
51
- agentBotEnvironment(process.env, this.env, environmentContext),
66
+ agentBotEnvironment({ ...this.codexEnvironment, ...process.env }, agentEnvironment, environmentContext),
52
67
  environmentContext.profilePath ?? os.homedir(),
53
68
  );
54
69
  this.child = child;
@@ -108,11 +123,13 @@ export class CodexProcessManager implements AppServerClientProvider {
108
123
  }
109
124
 
110
125
  getCodexHome(): string {
111
- return this.env.CODEX_HOME ?? process.env.CODEX_HOME ?? path.join(os.homedir(), ".codex");
126
+ const home = this.env.CODEX_HOME?.trim() || process.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
127
+ return resolveUserPath(home, this.environmentContext().profilePath ?? os.homedir());
112
128
  }
113
129
 
114
130
  getEnvironmentVariable(name: string): string | undefined {
115
- return this.env[name] ?? process.env[name];
131
+ if (name === "CODEX_HOME" && this.getAgentFamily() === "codex") return this.getCodexHome();
132
+ return this.env[name] ?? process.env[name] ?? this.codexEnvironment[name];
116
133
  }
117
134
 
118
135
  close(): void {