@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
|
@@ -35,7 +35,7 @@ Agent Bot 是基于 Node.js 22+、ESM 和 TypeScript 的应用,主要组件如
|
|
|
35
35
|
|
|
36
36
|
CLI 还支持显式指定目录的 Profile。不使用 `--profile` 时,命令使用主 Profile 以及原有的环境变量路径规则。`--profile <目录>` 会为当前命令及其启动的 Supervisor、Worker 固定 `AGENT_BOT_HOME` 和 `AGENT_BOT_CONFIG`,配置文件固定为 `<目录>/config.yaml`。它还会先清除继承的飞书凭据环境变量,再加载所选 Profile 的 `.env`,避免从主 Agent Bot 进程树中启动辅助实例时误用主机器人的凭据。`--profile` 不能和 `--config` 同时使用。其他 Profile 必须在每次命令中显式选择;Agent Bot 不维护按名称注册的 Profile。
|
|
37
37
|
|
|
38
|
-
CLI 通过 Node.js 国际化能力读取系统 Locale。以 `zh` 开头的 Locale 使用中文界面;英文及所有未支持 Locale 使用英文。帮助、状态、进度、交互提示和 CLI 自身错误都遵循此规则;JSON 字段名及枚举值不做本地化。系统生成的重启原因仍使用中文,因为它会显示在中文 Lark 状态卡中;显式传入的 `--reason` 保持原样。`agentbot server status` 会报告运行中 Worker 的 Lark App ID,JSON 字段名为 `feishuAppId`;服务停止或旧版健康协议尚未提供该字段时,CLI 会回退到当前 Profile 配置的 App ID
|
|
38
|
+
CLI 通过 Node.js 国际化能力读取系统 Locale。以 `zh` 开头的 Locale 使用中文界面;英文及所有未支持 Locale 使用英文。帮助、状态、进度、交互提示和 CLI 自身错误都遵循此规则;JSON 字段名及枚举值不做本地化。系统生成的重启原因仍使用中文,因为它会显示在中文 Lark 状态卡中;显式传入的 `--reason` 保持原样。`agentbot server status` 会报告运行中 Worker 的 Lark App ID,JSON 字段名为 `feishuAppId`;服务停止或旧版健康协议尚未提供该字段时,CLI 会回退到当前 Profile 配置的 App ID。`agents` 数组会报告每个已配置 Agent 进程的 PID 和协议初始化返回的版本号;Agent 尚未启动时两者均为 `null`。
|
|
39
39
|
|
|
40
40
|
| 默认路径 | 内容 |
|
|
41
41
|
| ------------------------------------ | -------------------- |
|
|
@@ -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
|
|
|
@@ -183,13 +184,19 @@ Agent 配置的标准名是运行时隔离键。每个配置的 Agent 都拥有
|
|
|
183
184
|
|
|
184
185
|
`kind: "acp"` 或未填写 `kind` 的 Agent 使用各自的 ACP 进程。同一 Agent 的多个任务会在该连接上创建独立 ACP session。Agent 配置中的 `env` 只会加入该 Agent 的环境变量。
|
|
185
186
|
|
|
186
|
-
|
|
187
|
+
Agent 进程会继承普通父进程变量。启动 App Server 或 ACP 进程前,Agent Bot 会移除继承环境及 Agent 配置中名称不区分大小写匹配 `FEISHU_*` 或 `AGENT_BOT_*` 的全部变量,然后注入以下受控的非敏感上下文:
|
|
187
188
|
|
|
188
189
|
```text
|
|
189
190
|
AGENT_BOT=1
|
|
191
|
+
AGENT_BOT_HOME=<当前 Profile 根目录>
|
|
192
|
+
AGENT_BOT_CONFIG=<当前 config.yaml 路径>
|
|
193
|
+
AGENT_BOT_AGENT_NAME=<Agent 配置标准名>
|
|
194
|
+
AGENT_BOT_LARK_APP_ID=<Lark App ID>
|
|
195
|
+
AGENT_BOT_LARK_BOT_OPEN_ID=<Lark 机器人 open_id,可用时提供>
|
|
196
|
+
AGENT_BOT_LARK_USER_OPEN_ID=<已保存的授权用户 open_id,可用时提供>
|
|
190
197
|
```
|
|
191
198
|
|
|
192
|
-
内置 Skill
|
|
199
|
+
内置 Skill 使用 `AGENT_BOT` 判断自己是否运行在 Agent Bot 中。Lark App Secret、Supervisor 状态、重启原因和重启通知路由不会传给 Agent。`agents.<name>.env` 中不使用保留命名空间的变量,仍是显式配置该 Agent 专用环境的方式。
|
|
193
200
|
|
|
194
201
|
## 聊天路由
|
|
195
202
|
|
|
@@ -222,13 +229,15 @@ Agent Bot 会在每个 `thread/fork` 请求中默认发送实验性的 `excludeT
|
|
|
222
229
|
|
|
223
230
|
每个 turn 只有一张进度卡。普通更新最多每两秒一次,关键更新最短间隔 500 毫秒。完成时先把进度卡更新为终态,再单独发送 Markdown 最终回答。
|
|
224
231
|
|
|
232
|
+
成功完成的思考卡片包含 `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,本地文件修改也不会被回退。
|
|
233
|
+
|
|
225
234
|
完成持久化消息去重占位后,Agent Bot 会等待 `OnIt` 表情添加成功,再进行聊天信息持久化、图片下载、队列等待、命令执行或 Runtime 调用。Turn 成功、失败或取消时分别替换为 `DONE`、`ERROR` 或 `CrossMark`。表情操作失败会记录日志,但不会阻塞任务。
|
|
226
235
|
|
|
227
236
|
富文本图片会下载到输入图片缓存,并以 `localImage` 传给 App Server。纯图片消息使用默认 Prompt `请查看这张图片`。ACP Runtime 不支持图片输入时会明确报错。
|
|
228
237
|
|
|
229
238
|
## 任务、项目与外部 App Server 工作
|
|
230
239
|
|
|
231
|
-
`/sessions` 通过 `thread/list` 读取每个已配置 App Server Agent 的任务。对于 Codex,可发现同一 `CODEX_HOME` 下由 Codex Desktop、CLI、Agent Bot 或其他 App Server 客户端创建的任务;其他 Agent
|
|
240
|
+
`/sessions` 通过 `thread/list` 读取每个已配置 App Server Agent 的任务。对于 Codex,可发现同一 `CODEX_HOME` 下由 Codex Desktop、CLI、Agent Bot 或其他 App Server 客户端创建的任务;其他 Agent 通过同一协议暴露各自的任务存储。多 Agent 任务合并并全局排序后,每页显示 5 个;`Previous` 和 `Next` 会替换当前卡片内容而不是继续追加任务,同时保留搜索条件和全局任务序号。
|
|
232
241
|
|
|
233
242
|
每个任务条目都提供 `NewGroup` 和 `ForkGroup` 回调。回调数据保留所选任务 ID 与来源上下文,并使用飞书操作者的 `open_id` 邀请用户进入新群。`NewGroup` 解析所选任务的项目和执行设置;`ForkGroup` 解析该任务最新可用的已完成 turn。
|
|
234
243
|
|
|
@@ -290,9 +299,13 @@ Supervisor、Worker、替换 Supervisor 和 Console Worker 默认启用 Node fat
|
|
|
290
299
|
|
|
291
300
|
飞书 `/restart` 命令默认使用这条安全重启路径;`/restart --force` 会立即重启,并可能中断正在执行的任务。该命令拒绝其他所有参数。新消息会重置静默计时,CLI 的 `--immediate` 和 `--force` 也会跳过这些检查。退出码 `75` 表示主动 worker 重启。
|
|
292
301
|
|
|
302
|
+
待执行的安全重启计划会收集每一个触发它的精确会话路由。收到任何用户消息时,包括斜杠命令,都会把所属基础私聊或群聊标为活跃;每次状态更新还会加入此前 1 分钟内活跃的所有已知会话。每个已加入的会话都会收到各自的状态卡片和真正开始重启前的提示,而且即使之后超过 1 分钟未活跃,也会一直保留到本次计划结束。同一路由会去重;如果话题路由最初缺少消息锚点,后续带锚点的触发会补全它。话题请求会保留原消息 ID,并通过话题回复发送,避免重启状态落到群主会话;话题中的最近活动记在所属普通群,只有显式从话题触发时才向该话题本身发送。后续请求也会更新重启原因。替换进程前,Agent Bot 会提取并去重所有已加入路由所属的普通群,经替换 Supervisor 传给 Worker,再把这些群加入服务级启动卡目标。Supervisor 会保留该列表,直到 Worker 连续稳定运行 60 秒,避免启动阶段异常退出并重新拉起时丢失必发群。
|
|
303
|
+
|
|
304
|
+
当 CLI 重启属于某个具体任务时,使用 `agentbot server restart --task <任务>`。任务序号、完整 ID 和不冲突的 ID 前缀会在发送控制请求前解析。未传 `--task` 时,只有所有运行中任务都属于同一个会话,Server 才会自动推断安全重启状态的发送目标;如果同时有多个运行中的会话,请求会被拒绝。没有运行中任务的本地 CLI 重启没有等待状态所属会话。
|
|
305
|
+
|
|
293
306
|
首次安全重启状态卡会延迟 3 秒发送,让任务最终回答尽可能先到达;延迟期间的状态变化会合并到首张卡片。该延迟不阻塞调度器轮询,真正关闭前会立即 flush 尚未发送的卡片。
|
|
294
307
|
|
|
295
|
-
等待中的安全重启状态卡底部带有 `Cancel` 操作。回调会携带调度器单调递增的计划 ID
|
|
308
|
+
等待中的安全重启状态卡底部带有 `Cancel` 操作。回调会携带调度器单调递增的计划 ID,因此旧卡片不会取消较新的重启。取消成功后,所有已加入会话中的状态卡都会原地更新并移除按钮;调度器进入不可逆的正在重启阶段后也不再显示该按钮。
|
|
296
309
|
|
|
297
310
|
本地控制服务负责修改任务、执行指定任务的 NewGroup 和 ForkGroup,以及请求服务重启;只读任务查询直接访问 SQLite。
|
|
298
311
|
|
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.2",
|
|
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.2",
|
|
10
10
|
"license": "GPL-3.0-only",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@agentclientprotocol/sdk": "1.2.1",
|
package/package.json
CHANGED
|
@@ -31,6 +31,19 @@ running_in_agent_bot = os.environ.get("AGENT_BOT") == "1"
|
|
|
31
31
|
|
|
32
32
|
Treat this environment marker as authoritative. Do not infer Agent Bot execution merely because the CLI is installed, `~/.agent-bot` exists, or an Agent Bot process is running elsewhere. Configured agent environment variables cannot override the marker. Child processes normally inherit it, so the marker identifies the Agent Bot-started process tree unless a child explicitly replaces its environment.
|
|
33
33
|
|
|
34
|
+
Agent Bot preserves ordinary parent-process variables but removes inherited and Agent-configured `FEISHU_*` and `AGENT_BOT_*` variables before starting an Agent. It then exposes only this controlled, non-secret context when values are available:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
AGENT_BOT_HOME
|
|
38
|
+
AGENT_BOT_CONFIG
|
|
39
|
+
AGENT_BOT_AGENT_NAME
|
|
40
|
+
AGENT_BOT_LARK_APP_ID
|
|
41
|
+
AGENT_BOT_LARK_BOT_OPEN_ID
|
|
42
|
+
AGENT_BOT_LARK_USER_OPEN_ID
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`AGENT_BOT_HOME` is the active Profile root. Never expect `FEISHU_APP_SECRET`, Supervisor state, restart reasons, or restart notification routes in the Agent environment.
|
|
46
|
+
|
|
34
47
|
## Behave safely inside Agent Bot
|
|
35
48
|
|
|
36
49
|
When `AGENT_BOT=1`:
|
|
@@ -52,7 +65,9 @@ After the maintenance choice, interactive initialization requires the user to se
|
|
|
52
65
|
|
|
53
66
|
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
67
|
|
|
55
|
-
|
|
68
|
+
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.
|
|
69
|
+
|
|
70
|
+
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
71
|
|
|
57
72
|
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
73
|
|
|
@@ -86,7 +101,7 @@ agentbot server status
|
|
|
86
101
|
agentbot task list
|
|
87
102
|
```
|
|
88
103
|
|
|
89
|
-
Agent Bot CLI interface text follows the system locale: locales beginning with `zh` use Chinese, while English and unsupported locales use English. JSON output is not localized. `agentbot server status`
|
|
104
|
+
Agent Bot CLI interface text follows the system locale: locales beginning with `zh` use Chinese, while English and unsupported locales use English. JSON output is not localized. `agentbot server status` reports the running profile's Lark App ID and each configured Agent process's PID and initialized version. With `--json`, read them from `feishuAppId` and `agents`; an Agent that has not started has `null` process fields.
|
|
90
105
|
|
|
91
106
|
Add `--json` to status and list commands when machine-readable output helps. If `agentbot` is unavailable, report that the CLI is not installed or linked instead of guessing process state.
|
|
92
107
|
|
|
@@ -99,12 +114,14 @@ Use these commands:
|
|
|
99
114
|
```powershell
|
|
100
115
|
agentbot server start
|
|
101
116
|
agentbot server status
|
|
102
|
-
agentbot server restart --reason "reason for restart"
|
|
117
|
+
agentbot server restart --task <current-task-id> --reason "reason for restart"
|
|
103
118
|
agentbot server stop
|
|
104
119
|
```
|
|
105
120
|
|
|
106
121
|
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
122
|
|
|
123
|
+
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.
|
|
124
|
+
|
|
108
125
|
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
126
|
|
|
110
127
|
In Feishu, `/restart` schedules the same safe restart. Use `/restart --force` only when the user explicitly accepts interruption; the command accepts no other arguments.
|
|
@@ -128,6 +145,7 @@ List tasks before resolving a numeric reference because task numbers follow the
|
|
|
128
145
|
|
|
129
146
|
```powershell
|
|
130
147
|
agentbot task list
|
|
148
|
+
agentbot task current [--json]
|
|
131
149
|
agentbot task chat <number-or-task-id> [--json]
|
|
132
150
|
agentbot task status <number-or-task-id>
|
|
133
151
|
agentbot task stop <number-or-task-id>
|
|
@@ -139,59 +157,78 @@ agentbot task forkgroup <number-or-task-id> [title] [--json]
|
|
|
139
157
|
|
|
140
158
|
Task IDs may be supplied in full or as an unambiguous prefix. Use `task stop` to ask the running worker to send the App Server Interrupt signal; leave child-process lifecycle management to the selected Agent.
|
|
141
159
|
|
|
160
|
+
Read all task identity fields from `task list`: every entry shows its Agent and `AgentBot task ID`; App Server tasks also show `App Server thread ID`, while ACP tasks show `ACP session ID`. The status, title, conversation context, and update time remain visible. The CLI reads `CODEX_THREAD_ID` for Codex and `TRAECLI_THREAD_ID` for TraeX; when the available native Thread IDs identify one task, it marks that entry as `Current`. Use `task current` to print that task's details directly. Use any displayed full ID or an unambiguous prefix as a task reference. With `--json`, read the local and native values from `localSessionId`, `remoteSessionId`, and `acpSessionId`.
|
|
161
|
+
|
|
142
162
|
Use `task chat <number-or-task-id>` to read the Feishu chat ID associated with a task from local routing state. Plain output is only the chat ID for scripting. Add `--json` to include the stable task ID, complete context key, and the thread ID for a topic-bound task. This read-only command does not require the Agent Bot server to be running.
|
|
143
163
|
|
|
144
164
|
Use `task prompt` to send input to a specific task without changing any chat's current task. The bot first posts the Prompt text to the task's existing Feishu chat, thread, or private-chat route, then submits it to the task. If posting fails, the task is not started or steered. The thinking card and final response continue to the same route; the command line only reports whether the Prompt was accepted.
|
|
145
165
|
|
|
146
|
-
### Create a
|
|
166
|
+
### Create or fork a group from the CLI
|
|
167
|
+
|
|
168
|
+
Choose by whether the new task needs the source conversation:
|
|
169
|
+
|
|
170
|
+
- Use **New Group** for a fresh task in a separate private Lark group. Inherit the project and execution settings, but do not carry over prior turns. Use it for a separate assignment in the same codebase or for a different project or Agent.
|
|
171
|
+
- Use **Fork Group** to continue or parallelize work with the source task's conversation through a completed turn. Preserve that context in a separate private Lark group.
|
|
172
|
+
|
|
173
|
+
Both operations create and bind the destination task immediately. Neither interrupts the source task nor changes the source chat's current task.
|
|
174
|
+
|
|
175
|
+
When creating or forking from the task that is currently running this Skill, run `agentbot task current --json` first and pass its `localSessionId` as `<number-or-task-id>`. The command resolves Codex through `CODEX_THREAD_ID` and TraeX through `TRAECLI_THREAD_ID`, while preferring the only running match when another Agent's variable was inherited from a parent process. If the command cannot identify one task, ask for an explicit AgentBot task ID instead of guessing from list order, title, or project directory.
|
|
147
176
|
|
|
148
|
-
|
|
177
|
+
List tasks first, identify the intended source explicitly, then run one of these commands:
|
|
149
178
|
|
|
150
179
|
```powershell
|
|
151
|
-
|
|
180
|
+
agentbot task list
|
|
181
|
+
|
|
182
|
+
# Fresh task: inherit the source Agent, project, and execution settings.
|
|
152
183
|
agentbot task newgroup <number-or-task-id> "Review"
|
|
153
184
|
|
|
154
|
-
#
|
|
185
|
+
# Fresh task: override the inherited project directory.
|
|
155
186
|
agentbot task newgroup <number-or-task-id> "Review" --dir ~/dev/project
|
|
156
187
|
|
|
157
|
-
#
|
|
188
|
+
# Fresh task: use a Projectless App Server workspace.
|
|
158
189
|
agentbot task newgroup <number-or-task-id> "Question" --nodir
|
|
159
190
|
|
|
160
|
-
#
|
|
191
|
+
# Fresh task: use another configured Agent and that Agent's defaults.
|
|
161
192
|
agentbot task newgroup <number-or-task-id> "Review" --agent traex
|
|
193
|
+
|
|
194
|
+
# Context-preserving branch from the source task.
|
|
195
|
+
agentbot task forkgroup <number-or-task-id> "Parallel review"
|
|
162
196
|
```
|
|
163
197
|
|
|
164
|
-
Apply these rules:
|
|
198
|
+
Apply these New Group rules:
|
|
165
199
|
|
|
166
|
-
-
|
|
167
|
-
- `--dir
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
- Creating the group must not interrupt the source task or switch the source chat's current task. Add `--json` when the caller needs `group.chatId`, `group.contextKey`, or the created task IDs.
|
|
200
|
+
- Treat `<number-or-task-id>` as the source context; never substitute the shell's current working directory for its project directory.
|
|
201
|
+
- With neither `--dir` nor `--nodir`, inherit the source project. If it is Projectless, create a fresh Projectless workspace rather than reusing its generated directory.
|
|
202
|
+
- Resolve `~`, `~/...`, and `~\...` in `--dir <cwd>` from the user's home directory. Reject combining `--dir` with `--nodir`.
|
|
203
|
+
- Allow `--nodir` only for an App Server Agent.
|
|
204
|
+
- With no `--agent`, inherit the source Agent, Provider, model, reasoning effort, and permission mode. With `--agent <standard-name>`, preserve the selected project shape but use the selected Agent's runtime defaults. `--agent` is a CLI-only New Group option.
|
|
172
205
|
|
|
173
|
-
|
|
206
|
+
Apply these Fork Group rules:
|
|
174
207
|
|
|
175
|
-
|
|
208
|
+
- Fork the latest available completed turn and preserve its conversation context. Never include or interrupt a newer active turn. Reject the request when no completed turn is available.
|
|
209
|
+
- Keep the source Agent, project, Provider, model, reasoning effort, and permission mode. Fork Group does not accept Agent or project overrides; use New Group when those must change.
|
|
210
|
+
- Use the optional title as the destination task title and group suffix.
|
|
176
211
|
|
|
177
|
-
|
|
212
|
+
Require a running Server for either CLI command. Require `FEISHU_USER_OPEN_ID` so the Server can invite the saved authorizing user; the CLI has no Lark operator identity of its own. Add `--json` when the caller needs `group.chatId`, `group.contextKey`, or the created task IDs.
|
|
178
213
|
|
|
179
|
-
|
|
214
|
+
Name a New Group without an explicit title as `[agent] [project directory] 新任务 (mm-dd)`, or `[agent] 新任务 (mm-dd)` when Projectless. Name an untitled Fork Group with the source task's persistent `source title(分支 N)` sequence, without a date suffix. Cap the displayed Lark group name at 60 characters by truncating only the group title portion; preserve the complete Agent Bot task title. Do not automatically send a Sessions or Status card to the destination group. Send a welcome message that reports Provider, model, reasoning effort, and permission mode.
|
|
180
215
|
|
|
181
|
-
|
|
216
|
+
Give every created group a deterministic scheme-C Identicon avatar. Display the first word for Latin project names or up to four characters for Chinese names, and hash the normalized full project path into the palette and symmetric node pattern. Use the title as the stable seed for Projectless groups. Replace the user's home prefix with `~` in the group-name project segment. Limit that segment to 15 characters, preferring the final two directories, then the final directory, and only then tail truncation. Use `\` on Windows and `/` on macOS/Linux.
|
|
182
217
|
|
|
183
|
-
|
|
218
|
+
Use `/queue <prompt>` in Feishu to persist a separate follow-up Prompt instead of steering the active turn. Queued Prompts run in FIFO order after the current turn completes and survive Agent Bot restarts. `/nosteer <prompt>` is an equivalent compatibility spelling.
|
|
184
219
|
|
|
185
|
-
|
|
220
|
+
For a long-running objective in the active Feishu task, use `/goal <objective>`. Use `/goal` to inspect it, `/goal pause` or `/goal resume` to control automatic continuation, `/goal edit <objective>` to revise it, and `/goal clear` to remove it. Stopping a Goal turn through Agent Bot also pauses the Goal before sending Interrupt so it does not immediately continue.
|
|
186
221
|
|
|
187
|
-
Use `/
|
|
222
|
+
Use `/agent` in Feishu to open the unified execution-settings card on its Agent tab and `/agent <name>` to directly select the default agent. The Agent tab is present only when multiple agents are configured and remains usable before the chat has a current task. When only one Agent is configured, `/agent` reports that current Agent directly instead of sending a card. Agent selection affects future tasks and does not replace the Agent of an existing task; use `/new` after selecting when a new task is needed.
|
|
188
223
|
|
|
189
224
|
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
225
|
|
|
191
|
-
|
|
226
|
+
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
227
|
|
|
193
228
|
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
229
|
|
|
230
|
+
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.
|
|
231
|
+
|
|
195
232
|
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
233
|
|
|
197
234
|
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 +250,7 @@ Use `--context <key>` or `--status <status>` to narrow `task list`. Use `--profi
|
|
|
213
250
|
After changing Agent Bot code, run the relevant tests, typecheck, and build. If a running service must pick up the result, schedule:
|
|
214
251
|
|
|
215
252
|
```powershell
|
|
216
|
-
agentbot server restart --reason "brief description of the completed change"
|
|
253
|
+
agentbot server restart --task <current-task-id> --reason "brief description of the completed change"
|
|
217
254
|
```
|
|
218
255
|
|
|
219
256
|
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.
|
|
@@ -3,7 +3,10 @@ import readline from "node:readline";
|
|
|
3
3
|
import type { Logger } from "pino";
|
|
4
4
|
import type { AgentConfig } from "../config/schema.js";
|
|
5
5
|
import { AcpJsonRpcConnection } from "./AcpJsonRpcConnection.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
agentBotEnvironment,
|
|
8
|
+
type AgentEnvironmentContext,
|
|
9
|
+
} from "../runtime/agentEnvironment.js";
|
|
7
10
|
import { spawnStdioCommand } from "../utils/spawnCommand.js";
|
|
8
11
|
|
|
9
12
|
export interface ManagedAcpProcess {
|
|
@@ -16,14 +19,24 @@ export interface ManagedAcpProcess {
|
|
|
16
19
|
export class AcpProcessManager {
|
|
17
20
|
private readonly processes = new Map<string, ManagedAcpProcess>();
|
|
18
21
|
|
|
19
|
-
constructor(
|
|
22
|
+
constructor(
|
|
23
|
+
private readonly logger: Logger,
|
|
24
|
+
private readonly environmentContext: () => AgentEnvironmentContext = () => ({}),
|
|
25
|
+
) {}
|
|
20
26
|
|
|
21
27
|
start(processKey: string, agentName: string, agent: AgentConfig): ManagedAcpProcess {
|
|
22
28
|
if (this.processes.has(processKey)) {
|
|
23
29
|
throw new Error(`ACP process already exists: ${processKey}`);
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
const child = spawnStdioCommand(
|
|
32
|
+
const child = spawnStdioCommand(
|
|
33
|
+
agent.command,
|
|
34
|
+
agent.args,
|
|
35
|
+
agentBotEnvironment(process.env, agent.env, {
|
|
36
|
+
...this.environmentContext(),
|
|
37
|
+
agentName,
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
27
40
|
|
|
28
41
|
const childLogger = this.logger.child({ processKey, agentName });
|
|
29
42
|
const connection = new AcpJsonRpcConnection(child, childLogger);
|
|
@@ -40,6 +40,7 @@ export class AcpSessionManager {
|
|
|
40
40
|
private readonly sessions = new Map<string, RuntimeSession>();
|
|
41
41
|
private readonly sessionsByAcpId = new Map<string, RuntimeSession>();
|
|
42
42
|
private processStart?: Promise<ManagedAcpProcess>;
|
|
43
|
+
private processVersion?: string;
|
|
43
44
|
|
|
44
45
|
constructor(
|
|
45
46
|
private readonly agentName: string,
|
|
@@ -52,6 +53,15 @@ export class AcpSessionManager {
|
|
|
52
53
|
return this.sessions.get(localSessionId);
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
getProcessInfo(): { pid?: number; version?: string } {
|
|
57
|
+
const pid = this.processManager.get(this.agentName)?.child.pid;
|
|
58
|
+
if (!pid) return {};
|
|
59
|
+
return {
|
|
60
|
+
pid,
|
|
61
|
+
...(this.processVersion ? { version: this.processVersion } : {}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
55
65
|
async create(input: CreateRuntimeSessionInput): Promise<RuntimeSession> {
|
|
56
66
|
if (input.agentName !== this.agentName) throw new Error(`Unexpected agent: ${input.agentName}`);
|
|
57
67
|
|
|
@@ -133,6 +143,7 @@ export class AcpSessionManager {
|
|
|
133
143
|
this.sessions.clear();
|
|
134
144
|
this.sessionsByAcpId.clear();
|
|
135
145
|
this.processStart = undefined;
|
|
146
|
+
this.processVersion = undefined;
|
|
136
147
|
this.processManager.stopAll();
|
|
137
148
|
}
|
|
138
149
|
|
|
@@ -181,6 +192,7 @@ export class AcpSessionManager {
|
|
|
181
192
|
}
|
|
182
193
|
|
|
183
194
|
private async startProcess(): Promise<ManagedAcpProcess> {
|
|
195
|
+
this.processVersion = undefined;
|
|
184
196
|
const managed = this.processManager.start(this.agentName, this.agentName, this.agent);
|
|
185
197
|
try {
|
|
186
198
|
const connection = managed.connection;
|
|
@@ -210,6 +222,8 @@ export class AcpSessionManager {
|
|
|
210
222
|
version: "0.1.0",
|
|
211
223
|
},
|
|
212
224
|
});
|
|
225
|
+
const version = initializeResult.agentInfo?.version?.trim();
|
|
226
|
+
if (version) this.processVersion = version;
|
|
213
227
|
if (initializeResult.authMethods?.length) {
|
|
214
228
|
const method = initializeResult.authMethods[0];
|
|
215
229
|
this.logger.info({ methodId: method.id }, "Authenticating ACP agent with first advertised method.");
|
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> {
|
|
@@ -6,7 +6,12 @@ import type { SessionRecord } from "../state/StateStore.js";
|
|
|
6
6
|
|
|
7
7
|
export type ControlRequest =
|
|
8
8
|
| { action: "health" }
|
|
9
|
-
| {
|
|
9
|
+
| {
|
|
10
|
+
action: "server_restart";
|
|
11
|
+
mode: "safe" | "immediate";
|
|
12
|
+
reason: string;
|
|
13
|
+
notificationSessionId?: string;
|
|
14
|
+
}
|
|
10
15
|
| { action: "server_stop" }
|
|
11
16
|
| { action: "task_status"; localSessionId: string }
|
|
12
17
|
| { action: "task_stop"; localSessionId: string }
|
package/src/cli/help.ts
CHANGED
|
@@ -13,10 +13,11 @@ export function renderCliHelp(version: string, language: CliLanguage = cliLangua
|
|
|
13
13
|
init --reset 重置并重新配置显式指定的 Profile
|
|
14
14
|
console 打开本地控制台
|
|
15
15
|
server start 在后台启动 Agent Bot
|
|
16
|
-
server status 显示服务、飞书 App ID
|
|
17
|
-
server restart
|
|
16
|
+
server status 显示服务、飞书 App ID、Agent 进程和安全重启状态
|
|
17
|
+
server restart [--task <任务>] 安排安全重启并将状态发回指定任务会话
|
|
18
18
|
server stop 停止 Agent Bot
|
|
19
19
|
task list 列出任务
|
|
20
|
+
task current [--json] 显示当前调用任务的详情
|
|
20
21
|
task status <任务> 显示任务状态
|
|
21
22
|
task chat <任务> 显示任务绑定的飞书会话
|
|
22
23
|
task prompt <任务> <提示词> 向任务发送提示词
|
|
@@ -39,6 +40,11 @@ export function renderCliHelp(version: string, language: CliLanguage = cliLangua
|
|
|
39
40
|
--skip-feishu 仅初始化 Console 环境
|
|
40
41
|
--reconfigure-feishu 仅替换现有飞书凭据
|
|
41
42
|
|
|
43
|
+
重启选项:
|
|
44
|
+
--task <任务> 将安全重启状态发回任务所在会话
|
|
45
|
+
--reason <原因> 设置重启原因
|
|
46
|
+
--immediate 立即重启,可能中断任务
|
|
47
|
+
|
|
42
48
|
示例:
|
|
43
49
|
agentbot init
|
|
44
50
|
agentbot --profile ~/.agent-bot init --reset
|
|
@@ -61,10 +67,11 @@ Common commands:
|
|
|
61
67
|
init --reset Reset and reconfigure an explicit profile
|
|
62
68
|
console Open the local console
|
|
63
69
|
server start Start Agent Bot in the background
|
|
64
|
-
server status Show the server, Lark App ID, and safe-restart status
|
|
65
|
-
server restart
|
|
70
|
+
server status Show the server, Lark App ID, Agent processes, and safe-restart status
|
|
71
|
+
server restart [--task <task>] Schedule a safe restart and return status to the task's conversation
|
|
66
72
|
server stop Stop Agent Bot
|
|
67
73
|
task list List tasks
|
|
74
|
+
task current [--json] Show details for the task invoking the CLI
|
|
68
75
|
task status <task> Show task status
|
|
69
76
|
task chat <task> Show the task's Lark chat binding
|
|
70
77
|
task prompt <task> <prompt> Send a Prompt to a task
|
|
@@ -87,6 +94,11 @@ Init options:
|
|
|
87
94
|
--skip-feishu Initialize for Console only
|
|
88
95
|
--reconfigure-feishu Replace only the existing Lark credentials
|
|
89
96
|
|
|
97
|
+
Restart options:
|
|
98
|
+
--task <task> Return safe-restart status to the task's conversation
|
|
99
|
+
--reason <reason> Set the restart reason
|
|
100
|
+
--immediate Restart immediately and possibly interrupt tasks
|
|
101
|
+
|
|
90
102
|
Examples:
|
|
91
103
|
agentbot init
|
|
92
104
|
agentbot --profile ~/.agent-bot init --reset
|
package/src/cli/serverStatus.ts
CHANGED
|
@@ -29,16 +29,44 @@ export function formatServerStatus(data: unknown, language: CliLanguage = cliLan
|
|
|
29
29
|
language,
|
|
30
30
|
)
|
|
31
31
|
: cliText("not scheduled", "未计划", language);
|
|
32
|
+
const agents = formatAgentProcesses(value.agents, language);
|
|
32
33
|
return [
|
|
33
34
|
`${cliText("Agent Bot server: ", "Agent Bot 服务:", language)}${state}`,
|
|
34
35
|
`${cliText("Lark App ID: ", "飞书 App ID:", language)}${value.feishuAppId ?? cliText("not configured", "未配置", language)}`,
|
|
35
36
|
`${cliText("PID: ", "PID:", language)}${value.pid ?? "-"}`,
|
|
36
37
|
`${cliText("Started at: ", "启动时间:", language)}${value.startedAt ?? "-"}`,
|
|
37
38
|
`${cliText("Supervisor: ", "Supervisor:", language)}${value.supervised ? cliText("enabled", "已启用", language) : cliText("disabled", "未启用", language)}`,
|
|
39
|
+
...agents,
|
|
38
40
|
`${cliText("Running tasks: ", "运行中任务:", language)}${activity.runningSessions ?? 0}`,
|
|
39
41
|
`${cliText("Pending final deliveries: ", "待发送最终结果:", language)}${activity.pendingFinalDeliveries ?? 0}`,
|
|
40
42
|
`${cliText("Safe restart: ", "安全重启:", language)}${safeRestart}`,
|
|
41
43
|
"",
|
|
42
44
|
].join("\n");
|
|
43
45
|
}
|
|
46
|
+
|
|
47
|
+
function formatAgentProcesses(value: unknown, language: CliLanguage): string[] {
|
|
48
|
+
const agents = Array.isArray(value)
|
|
49
|
+
? value.filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === "object")
|
|
50
|
+
: [];
|
|
51
|
+
if (agents.length === 0) {
|
|
52
|
+
return [`${cliText("Agents: ", "Agent 进程:", language)}-`];
|
|
53
|
+
}
|
|
54
|
+
return [
|
|
55
|
+
cliText("Agents:", "Agent 进程:", language),
|
|
56
|
+
...agents.map((agent) => {
|
|
57
|
+
const name = typeof agent.name === "string" && agent.name.trim() ? agent.name.trim() : "-";
|
|
58
|
+
const title = typeof agent.title === "string" && agent.title.trim() ? agent.title.trim() : undefined;
|
|
59
|
+
const label = title && title !== name ? `${name} (${title})` : name;
|
|
60
|
+
const pid = typeof agent.pid === "number" && Number.isInteger(agent.pid) && agent.pid > 0
|
|
61
|
+
? String(agent.pid)
|
|
62
|
+
: "-";
|
|
63
|
+
const version = typeof agent.version === "string" && agent.version.trim()
|
|
64
|
+
? agent.version.trim()
|
|
65
|
+
: "-";
|
|
66
|
+
return language === "zh"
|
|
67
|
+
? ` ${label}:PID ${pid},版本 ${version}`
|
|
68
|
+
: ` ${label}: PID ${pid}, version ${version}`;
|
|
69
|
+
}),
|
|
70
|
+
];
|
|
71
|
+
}
|
|
44
72
|
import { cliLanguage, cliText, type CliLanguage } from "./i18n.js";
|