@realqiyan/agent-task 0.0.1-rc.3 → 0.1.0-rc.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.
Files changed (52) hide show
  1. package/CONTEXT.md +17 -0
  2. package/README.md +28 -6
  3. package/bin/agent-task.mjs +18 -6
  4. package/config.example.json +1 -1
  5. package/dist/application/launcher.js +30 -16
  6. package/dist/application/main.js +5 -2
  7. package/dist/application/mcp-entry.js +71 -0
  8. package/dist/application/task-runtime.js +3 -3
  9. package/dist/configuration/contract.js +2 -2
  10. package/dist/configuration/files.js +2 -3
  11. package/dist/configuration/management.js +1 -1
  12. package/dist/configuration/policy.js +3 -8
  13. package/dist/configuration/settings.js +10 -33
  14. package/dist/executors/ports.js +2 -0
  15. package/dist/executors/registry.js +117 -15
  16. package/dist/executors/validation.js +4 -5
  17. package/dist/infrastructure/lock.js +25 -13
  18. package/dist/integrations/acp/connection.js +5 -5
  19. package/dist/integrations/acp/discovery.js +271 -0
  20. package/dist/integrations/acp/runtime.js +38 -23
  21. package/dist/integrations/acp/worker.js +5 -3
  22. package/dist/integrations/local-service/control.js +36 -11
  23. package/dist/protocol/cli/commands.js +41 -46
  24. package/dist/protocol/http/server.js +8 -0
  25. package/dist/protocol/http/web.js +7 -1
  26. package/dist/protocol/mcp.js +3 -3
  27. package/dist/protocol/settings-contract.js +4 -7
  28. package/dist/protocol/stdio.js +71 -0
  29. package/dist/protocol/task-contract.js +1 -1
  30. package/dist/tasks/definitions.js +8 -5
  31. package/dist/tasks/runs.js +2 -2
  32. package/dist/tasks/storage/records.js +2 -2
  33. package/dist/tasks/storage/store.js +5 -4
  34. package/dist/tasks/supervisor.js +1 -1
  35. package/dist/web/settings-view.js +43 -37
  36. package/docs/adr/0001-task-request-scope.md +3 -0
  37. package/docs/adr/0002-mcp-workspace-source.md +3 -0
  38. package/docs/adr/0003-adapter-installation.md +3 -0
  39. package/docs/architecture-guidelines.md +2 -2
  40. package/docs/cli.md +12 -4
  41. package/docs/deployment.md +52 -36
  42. package/docs/design-proposal.md +3 -0
  43. package/docs/release.md +5 -5
  44. package/docs/technical-design.md +5 -3
  45. package/docs/validation.md +51 -9
  46. package/docs/web-console.md +7 -4
  47. package/package.json +3 -1
  48. package/scripts/install-lib.mjs +5 -23
  49. package/scripts/probe-agent.mjs +10 -9
  50. package/scripts/service-runner.mjs +8 -3
  51. package/scripts/setup-wizard.mjs +38 -11
  52. package/scripts/setup.mjs +69 -95
@@ -1,75 +1,91 @@
1
1
  # 安装与运行
2
2
 
3
- 支持 Linux 和 macOS,要求 Node.js **22.19+** 与 npm。安装包包含运行产物,不需要源码或 TypeScript。发布状态和平台证据见[验证说明](validation.md),发布操作见[发布说明](release.md)。
3
+ 支持 Linux 和 macOS,要求 Node.js **22.19+** 与 npm。安装包包含运行产物,不需要源码或 TypeScript。本文描述当前实现;已发布版本和平台证据见[验证说明](validation.md),发布操作见[发布说明](release.md)。
4
4
 
5
- ## 安装与交互配置
5
+ ## 安装与初始化
6
6
 
7
7
  ```bash
8
8
  npm install -g @realqiyan/agent-task
9
9
  agent-task setup
10
- agent-task start
11
10
  ```
12
11
 
13
- 使用当前用户可写的 npm prefix,不以 root 运行。
12
+ 使用当前用户可写的 npm prefix。向导要求确认安装目录、数据目录、端口、令牌配置及协调员 provider;选择 provider 后收集模型和凭据。令牌可以确认后随机生成,也可以从私有 JSON 文件提供。取消确认不写配置,密钥输入不回显。未配置 provider 时仍可管理服务,但不能接收可执行任务。
14
13
 
15
- 向导收集工作空间、可选执行端、可选协调模型及端口。取消确认不写配置。无终端环境须明确提供参数:
14
+ 无终端环境必须明确确认这些初始化选项。例如,私有凭据文件已包含协调员密钥时:
16
15
 
17
16
  ```bash
18
- agent-task setup --executors none --cwd /absolute/workspace
19
- # 另一个首次安装选择:安装 Pi ACP 组件,但不授权执行。
20
17
  agent-task setup --home /absolute/private/agent-task \
21
- --executors pi --cwd /absolute/workspace \
22
- --provider deepseek --model deepseek-flash
18
+ --data-dir /absolute/private/agent-task/data --port 8765 \
19
+ --provider deepseek --model deepseek-flash \
20
+ --credentials-file /absolute/private/coordinator.json \
21
+ --generate-tokens --yes
23
22
  ```
24
23
 
25
- 工作空间须预先存在,且位于安装目录及 npm 包之外。完整选项见 `agent-task setup --help`;组件版本及平台实现见 [install-lib.mjs](../scripts/install-lib.mjs)。安装不执行原生登录或批准任务。
24
+ 此例从文件读取协调员密钥,并生成独立的服务和管理员令牌。已有服务令牌和管理员令牌时,由同一私有文件提供,省略 `--generate-tokens`。不通过命令参数传密钥值。完整选项见 `agent-task setup --help`,凭据文件约束见 [environment.ts](../src/configuration/environment.ts)。
26
25
 
27
- 默认安装目录为 Linux 的 `~/.local/share/agent-task` 或 macOS 的 `~/Library/Application Support/agent-task`。自定义 `--home` 后,后续命令也传同一路径。setup 输出私有文件路径和控制台地址,**不打印 token**。
26
+ 安装目录和数据目录须属于当前用户,使用规范绝对路径和私有权限。setup 输出文件路径,不打印 token。自定义 `--home` 后,后续命令及 MCP 配置使用同一路径。初始化不安装执行 CLI 或 ACP 适配器,不执行原生登录。
28
27
 
29
- ## 数据目录与配置
28
+ ## MCP 接入与工作目录
30
29
 
31
- 初始 `dataDir` 是安装目录下的 `data/`。运行配置、私有环境文件、任务和协调会话记录、结果及服务文件日志均位于当前 `dataDir`。配置契约见 [配置契约](../src/configuration/contract.ts),路径选择与新目录准备见 [startup.ts](../src/application/startup.ts)。
30
+ 在宿主的 MCP 配置中添加命令,例如采用 `mcpServers` 格式的宿主:
32
31
 
33
- 安装目录保留固定的 `launch.json` 定位文件、安装清单、组件及启动器控制资源;它不是另一份运行配置。CLI 通过这个固定入口定位当前数据目录。npm 包、用户工作空间和执行端原生配置不随 `dataDir` 移动。
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "agent-task": {
36
+ "command": "agent-task",
37
+ "args": ["mcp"]
38
+ }
39
+ }
40
+ }
41
+ ```
34
42
 
35
- 在 Web 修改数据目录后,下次启动只在目标目录准备已保存的配置和私有环境,并切换定位文件;**不复制任务、会话、结果或日志,不删除旧目录**。目标已有不同启动文件时拒绝覆盖;须停服核对并显式准备目标,不自动合并或修复。新目录没有旧任务,不能把切换目录当成故障恢复或重放许可。
43
+ 自定义安装目录时,参数改为 `["mcp", "--home", "/absolute/private/agent-task"]`。宿主找不到命令时,使用已安装 `agent-task` 的绝对路径。配置文件位置和外层结构由宿主决定,本服务不自动修改宿主配置或识别宿主当前项目。
36
44
 
37
- 1. 运行 `agent-task start`,打开输出的本机控制台地址。用本地编辑器读取当前 `dataDir/environment.json` 的 `AGENT_TASK_ADMIN_TOKEN`,在连接表单输入。不要把 token 放进 URL、命令行、Git、截图或日志。
38
- 2. 在[控制台](web-console.md)或 [CLI](cli.md) 配置协调模型、单独保存模型密钥及注册执行端。两端使用同一管理服务。未配置协调模型时可管理服务,但不能接收可执行任务。
39
- 3. 按执行端官方方式准备原生认证及权限:[Pi](https://github.com/earendil-works/pi/tree/v1.3.0/packages/coding-agent/docs)、[Codex ACP](https://github.com/agentclientprotocol/codex-acp#readme)、[Claude ACP](https://github.com/zed-industries/claude-agent-acp)、[OpenCode](https://opencode.ai/docs/acp/)。运行进程与原生登录须使用同一 OS 用户及相应 HOME/配置目录。
40
- 4. 审核原生工具、沙箱、凭据和结果文件读取权限后,再明确批准无人值守执行。注册只选类型,不配置命令、参数、环境变量清单或 ACP 工具白名单;系统映射见 [命令映射](../src/integrations/acp/runtime.ts)。额外 ACP 审批请求被拒绝,不自动批准。
45
+ `agent-task mcp` 通过 stdio 接入,自动启动或连接同一安装目录的共用后台,并从私有文件读取内部服务 token。无需先执行 `start`,也无需把端口和 token 复制到 MCP 配置。stdout 仅传输 MCP 消息。
41
46
 
42
- 服务和协调员的秘密只从私有环境文件加载,不回退到 shell 模型密钥。执行端沿用启动上下文中的用户原生环境,但排除私有文件变量、服务/管理员令牌、协调员凭据及同值别名。不要把执行端原生凭据放入服务的私有环境文件。启动器不执行 shell 启动脚本,也不导入原生认证文件。隔离实现见 [服务环境校验](../src/configuration/environment.ts)、[执行端环境过滤](../src/executors/environment.ts)。这不是同一 OS 用户下的沙箱。
47
+ 任务显式提供 `workspace` 时使用该目录;省略时使用本次 stdio 进程的启动目录。宿主须从目标项目目录启动命令,或在创建任务时明确传目录。服务不使用共用后台的启动目录猜测项目,也不根据 MCP roots 或宿主名称推断。直接 HTTP 接入需自行携带服务 token,并在 `task_create` 中指定目录。
43
48
 
44
- 显式握手检查:
49
+ 创建时校验目录存在并规范化,随后固定到任务;周期执行也使用同一目录。需要修改目录时取消旧任务的未来触发,再创建新任务。创建请求按最终目录和 `requestKey` 去重,不同项目可独立使用同一请求键。契约见 [task-contract.ts](../src/protocol/task-contract.ts),决策原因见[目录来源](adr/0002-mcp-workspace-source.md)与[请求去重](adr/0001-task-request-scope.md)。
45
50
 
46
- ```bash
47
- agent-task setup --check --probe
48
- ```
51
+ ## 执行端发现与准备
52
+
53
+ 每次 MCP 连接初始化时,后台从自己的原生 PATH 检测 pi、codex、claude 和 opencode。四者均不存在时拒绝 MCP 连接,提示至少安装一种;CLI/Web 管理入口仍可使用。安装和认证由对应官方工具负责:[Pi](https://github.com/earendil-works/pi/tree/v1.3.0/packages/coding-agent/docs)、[Codex](https://github.com/openai/codex)、[Claude Code](https://code.claude.com/docs/en/overview)、[OpenCode](https://opencode.ai/docs/)。
54
+
55
+ 发现原生 CLI 后,后台为缺少 ACP 适配器的执行端安装当时最新版,已有安装直接复用;OpenCode 使用原生 ACP 命令。安装在后台进行,可用 MCP `executor_list`、`agent-task executor list` 或 Web 查看准备结果。任务只能选择就绪执行端。适配器安装失败时保留管理入口和其他就绪执行端,修正原因后显式刷新或升级。包映射和安装实现见 [discovery.ts](../src/integrations/acp/discovery.ts)。
49
56
 
50
- 这只做 ACP initialize,不验证模型认证、任务成功或原生工具权限。普通 Pi CLI 不是 ACP server,须配合 pi-acp。自行安装的受支持适配器也可使用,只要在服务的原生 PATH 中;无需填写绝对可执行文件路径。
57
+ 适配器保存在安装目录的私有 `adapters/` 中,不修改全局 npm 包或执行 CLI 的原生配置。普通重连不访问 registry 升级已有适配器。显式更新使用 `agent-task executor upgrade codex --yes`,再查询状态;新版本发布成功前保留原安装,活动 ACP 进程不因升级被重启。高级启停设置及 Web/CLI 操作见 [CLI 说明](cli.md#执行端发现与更新)。
51
58
 
52
- ## 前台、重启与健康检查
59
+ 后台的原生环境来自启动它的进程,不随不同 MCP 客户端切换。新 CLI 位于已有 PATH 时可用 `executor refresh` 重新检测;PATH 本身变化时先停止后台,再从正确环境连接或启动。原生登录须与后台使用同一 OS 用户及 HOME/配置目录。
60
+
61
+ 就绪只表示启动依赖已准备,不证明原生认证、模型或工具权限可用。服务沿用执行端原生授权,不另设手工批准开关;额外 ACP 审批请求一律拒绝,不自动扩大权限。服务和协调员秘密只从私有环境文件加载。执行端继承用户原生环境前,过滤私有文件变量、服务令牌、协调员凭据及同值别名;不要将执行端原生凭据放入服务的私有文件。过滤实现见 [environment.ts](../src/executors/environment.ts),它不构成同一用户下的沙箱。
62
+
63
+ ## 后台、前台与健康检查
53
64
 
54
65
  ```bash
55
- agent-task start
56
- # 另一个终端:
57
66
  agent-task status
58
67
  agent-task restart
68
+ agent-task stop
69
+ # 需要前台管理时:
70
+ agent-task start
59
71
  ```
60
72
 
61
- Ctrl-C 关闭前台启动器及其运行进程。`restart` 和 Web 的“重启并检查可用性”使用同一启动器:先停止新派发、关闭活动执行并释放数据所有权,再用已保存配置创建新进程。
73
+ stdio 退出只关闭当前连接。由 `mcp` 启动的后台使用独立进程组,宿主关闭或原终端退出后,已提交任务继续调度;没有自动空闲退出。`stop` 显式关闭后台和活动执行。`start` 是前台入口,Ctrl-C 关闭该启动器及其运行进程。
74
+
75
+ 独立后台不保证机器关机、系统清理用户进程或后台自身故障后继续运行,也不配置开机自启。再次启动后读取持久任务;已中断且结果不明的执行保持阻断,不自动重试、重放或改派。
76
+
77
+ `restart` 与 Web 的重启入口使用同一启动器,应用已保存配置。只有新实例通过带 MCP token 的健康检查才返回成功,并给出实例标识及地址。端口变化时以该地址为准。重启关闭 ACP 会话,不保证等待人工回复的运行能无损继续。请求超时先用 `status` 核对,不盲目重复操作。
62
78
 
63
- 只有新实例通过带 MCP token 的健康检查才返回成功,并给出新实例标识及地址。端口变化或自动分配端口时,以返回地址为准;Web 不通过链接传递管理员令牌,须重新连接。
79
+ MCP 接入不会自动重启一个已存在但不可用的后台。故障时查看安装目录的 `launcher.log` 和当前数据目录的 `service.log`;修正私有启动文件后显式重启,启动器已退出时重新连接或运行 `start`。启动失败不会回滚配置或重放工作。
64
80
 
65
- 重启会关闭 ACP 会话,不保证等待人工回复的运行能无损继续。结果不明的执行保持阻断,不自动重试、重放或改派执行端。请求超时不代表重启没发生,先用 `agent-task status` 核对;不要盲目重复操作。
81
+ ## 数据目录与管理
66
82
 
67
- 启动失败不会回滚配置或重放工作。查看当前 `dataDir/service.log`;确认没有运行实例持有数据后,修正私有文件,再显式 `agent-task restart`。启动器也已退出时,用原启动方式重新启动。
83
+ 运行配置、私有环境文件、任务和协调会话记录、结果及服务日志位于当前 `dataDir`。安装目录保留 `launch.json` 定位文件、初始化清单、适配器及启动器控制资源。npm 包、项目工作空间和执行端原生配置与数据目录分开。路径实现见 [startup.ts](../src/application/startup.ts)。
68
84
 
69
- 源码开发者执行 `npm ci --ignore-scripts && npm run build` 后,可用 `npm run setup -- ...` 和 `npm start -- --home ...`;npm 安装用户不需要构建。内部 `dist/application/main.js` 不是独立的重启入口。
85
+ 打开 `status` 返回地址同源的 `/admin/`,用本地编辑器读取当前 `dataDir/environment.json` 中的 `AGENT_TASK_ADMIN_TOKEN`,在连接表单输入。不要把 token 放入 URL、命令参数、Git 或截图。CLI 从私有文件读取管理员令牌。两端使用同一管理服务,见[控制台说明](web-console.md)和 [CLI 说明](cli.md)。
70
86
 
71
- ## 更新
87
+ 修改数据目录后,下次启动只准备目标配置和私有环境,并切换定位文件;不复制任务、会话、结果或日志,不删除旧目录。目标已有不同启动文件时拒绝覆盖,须停服核对并显式准备目标。新目录没有旧任务,目录切换不是恢复或重放许可。
72
88
 
73
- 重复 setup 保留配置和授权。中断安装只能显式执行 `agent-task setup --resume`;所有权或清单不明时停止核查。
89
+ 重复 setup 保留已有配置。初始化中断后须显式执行 `setup --resume`,它只接续已保存且一致的初始化文件;缺少凭据或所有权不明时停止核查,不猜测修复。更新前停止后台并备份安装目录和数据目录。调研阶段不保证旧配置或数据兼容,普通启动不迁移历史。
74
90
 
75
- 更新前停止运行进程,并备份安装目录和当前数据目录。调研阶段不保证旧数据兼容,普通启动不执行迁移。
91
+ 源码开发者执行 `npm ci --ignore-scripts && npm run build` 后,可用 `node bin/agent-task.mjs setup`、`node bin/agent-task.mjs mcp` 或 `npm start -- --home ...`。内部 `dist/application/main.js` 不是独立的重启入口。
@@ -2,10 +2,13 @@
2
2
 
3
3
  本文保留产品目标和架构取舍。实现入口见[代码导航](technical-design.md),实际证据见[验证说明](validation.md)。
4
4
 
5
+ 使用流程简化的决策原因见[请求去重范围](adr/0001-task-request-scope.md)、[工作目录来源](adr/0002-mcp-workspace-source.md)和[适配器安装](adr/0003-adapter-installation.md)。
6
+
5
7
  ## 产品目标
6
8
 
7
9
  - 调用方通过 MCP 提交整体目标、DAG 和每节点的 ACP 执行端指派,支持立即、定时及周期执行。
8
10
  - 任务不依赖发起客户端持续在线,保留执行、交付、失败和缺少用户事实的记录。
11
+ - 初始化由用户确认目录、端口、令牌及协调模型;执行端自动发现并按需准备适配器。stdio 接入连接独立后台,宿主退出后后台继续调度;宿主配置由用户添加,不自动识别宿主当前项目。
9
12
  - 协调员推进既定 DAG、解释中间结果、处理澄清并组织交付;领域分析和工具操作交给执行端。
10
13
  - 缺少用户事实时可持久等待,回复后续接原运行,不丢失已接受工作。
11
14
  - 提供本机 Web 控制台,管理配置、查看任务,并只控制任务的未来触发;CLI 覆盖全部 Web 管理能力,两端复用同一套服务。配置保存后通过 Web 或 CLI 重启生效;切换数据目录不搬迁或删除旧历史。
package/docs/release.md CHANGED
@@ -12,10 +12,10 @@ npm run check
12
12
  npm test
13
13
  git diff --check
14
14
  npm run pack:release
15
- npm run test:install -- ./realqiyan-agent-task-0.0.1-rc.3.tgz
15
+ npm run test:install -- ./realqiyan-agent-task-0.1.0-rc.1.tgz
16
16
  ```
17
17
 
18
- 安装验证先检查当前 OS 和 Node.js 版本,再在独立临时目录执行真正的全局 npm 安装,禁用 lifecycle scripts,不使用项目源码或编译器;随后配置、前台启动、调用 Web 配置接口、重启、进行认证健康检查。验证脚本不调用模型或启动 ACP 工作。
18
+ 安装验证先检查当前 OS 和 Node.js 版本,再在独立临时目录执行真正的全局 npm 安装,禁用 lifecycle scripts,不使用项目源码或编译器;随后验证初始化、前台启动、Web 配置、重启和认证健康检查,再验证 stdio 自动启动后台、断开后后台仍可用及显式 stop。MCP 客户端使用安装包的 SDK 依赖。验证脚本不调用模型或启动 ACP 工作。
19
19
 
20
20
  输出报告只包含结果和证据目录,不包含 token;只将实际运行的宿主平台记为通过,其他平台明确记为未执行。临时目录权限为私有,内有验证专用随机凭据及空任务库,不上传这些目录。基础测试中的真实 PTY 用例先检查平台及 `script` 能否创建终端;能力不可用时明确跳过,不能把跳过当成终端验收通过。
21
21
 
@@ -30,7 +30,7 @@ npm run test:install -- ./realqiyan-agent-task-0.0.1-rc.3.tgz
30
30
  首次启用前,由维护者完成以下配置:
31
31
  1. 创建 GitHub Environment **npm**,仅允许 master 部署;按仓库需要配置审核人。工作流中的 environment 名称本身**不自动创建审核保护**。
32
32
  2. 在 npm 包的 Trusted Publisher 设置中授权 GitHub 仓库 `realqiyan/agent-task`、工作流 `publish.yml`、Environment `npm`,允许 `npm publish`。认证与 2FA 在 npm 官方界面完成,不上传本机登录凭据。
33
- 3. 核查分支保护及工作流修改权限,再在 Actions 选择 Publish npm,输入已验收的版本(本次为 `0.0.1-rc.3`)。认证/权限失败须停止,不能换账号或跳过验证。
33
+ 3. 核查分支保护及工作流修改权限,再在 Actions 选择 Publish npm,输入已验收的版本(本次为 `0.1.0-rc.1`)。认证/权限失败须停止,不能换账号或跳过验证。
34
34
 
35
35
  发布作业使用 Node 24 与支持 OIDC 的 npm;命令明确传 `--tag latest --access public --provenance`。Trusted Publisher 授权和 provenance 是不同条件,设置工作流不等于 npm 已授权。首次配置和有效期限制以 [npm 官方说明](https://docs.npmjs.com/trusted-publishers/)为准。
36
36
 
@@ -42,9 +42,9 @@ npm run test:install -- ./realqiyan-agent-task-0.0.1-rc.3.tgz
42
42
  npm login --registry=https://registry.npmjs.org
43
43
  npm whoami --registry=https://registry.npmjs.org
44
44
  # 必须确认输出 realqiyan,再发布已检查的 tarball:
45
- npm publish ./realqiyan-agent-task-0.0.1-rc.3.tgz --ignore-scripts \
45
+ npm publish ./realqiyan-agent-task-0.1.0-rc.1.tgz --ignore-scripts \
46
46
  --access public --tag latest --registry=https://registry.npmjs.org
47
- npm view @realqiyan/agent-task@0.0.1-rc.3 version license --registry=https://registry.npmjs.org
47
+ npm view @realqiyan/agent-task@0.1.0-rc.1 version license --registry=https://registry.npmjs.org
48
48
  npm view @realqiyan/agent-task dist-tags --json --registry=https://registry.npmjs.org
49
49
  node scripts/verify-install.mjs --registry
50
50
  ```
@@ -12,16 +12,16 @@
12
12
  | Run 与节点状态的业务修改入口、派发与提交顺序 | [TaskRuns](../src/tasks/runs.ts)、[数据模型](../src/tasks/model.ts)、[窄能力契约](../src/tasks/ports.ts) | [tasks.test.mjs](../tests/tasks.test.mjs)、[task-storage.test.mjs](../tests/task-storage.test.mjs) |
13
13
  | 时钟、协调会话监督、取消与收尾 | [RunSupervisor](../src/tasks/supervisor.ts)、[NodeSessions](../src/tasks/sessions.ts) | [tasks.test.mjs](../tests/tasks.test.mjs)、[restart.test.mjs](../tests/restart.test.mjs) |
14
14
  | 人工事实等待与回复 | [纯状态规则](../src/tasks/human-input.ts)、[TaskRuns](../src/tasks/runs.ts) | [human-input.test.mjs](../tests/human-input.test.mjs) |
15
- | 执行端注册、授权比较、凭据隔离 | [ExecutorRegistry](../src/executors/registry.ts)、[环境过滤](../src/executors/environment.ts)、[审批策略](../src/executors/permissions.ts) | [policy.test.mjs](../tests/policy.test.mjs)、[config-preflight.test.mjs](../tests/config-preflight.test.mjs) |
15
+ | 本机执行端目录、准入比较、凭据隔离 | [ExecutorRegistry](../src/executors/registry.ts)、[发现与安装](../src/integrations/acp/discovery.ts)、[环境过滤](../src/executors/environment.ts)、[审批策略](../src/executors/permissions.ts) | [executors.test.mjs](../tests/executors.test.mjs)、[policy.test.mjs](../tests/policy.test.mjs)、[config-preflight.test.mjs](../tests/config-preflight.test.mjs) |
16
16
  | 配置与凭据编辑、发布顺序、影响预览 | [Settings](../src/configuration/settings.ts)、[配置规则](../src/configuration/policy.ts)、[预览组合](../src/configuration/management.ts) | [settings.test.mjs](../tests/settings.test.mjs)、[web.test.mjs](../tests/web.test.mjs) |
17
17
  | 请求规范化、内部数据与严格记录校验 | [任务请求](../src/protocol/task-contract.ts)、[配置请求](../src/protocol/settings-contract.ts)、[内部数据校验](../src/tasks/validation.ts)、[持久记录](../src/tasks/storage/records.ts) | [task-storage.test.mjs](../tests/task-storage.test.mjs) |
18
18
  | 持久读写与事务 | [TaskStore](../src/tasks/storage/store.ts)、[配置文件](../src/configuration/files.ts)、[私有文件操作](../src/infrastructure/private-files.ts) | [task-storage.test.mjs](../tests/task-storage.test.mjs)、[settings.test.mjs](../tests/settings.test.mjs) |
19
19
  | 结果文件授权与文件持久化 | [Results](../src/results/service.ts)、[文件能力](../src/results/ports.ts)、[文件实现](../src/results/files.ts) | [results.test.mjs](../tests/results.test.mjs) |
20
20
  | ACP、worker 与进程协议 | [ACP 连接](../src/integrations/acp/connection.ts)、[worker 监督](../src/integrations/acp/execution.ts)、[worker 入口](../src/integrations/acp/worker.ts)、[命令映射](../src/integrations/acp/runtime.ts) | [execution.test.mjs](../tests/execution.test.mjs)、[tasks.test.mjs](../tests/tasks.test.mjs) |
21
21
  | durable 工具、模型与会话存储 | [协调适配](../src/integrations/coordinator/coordinator.ts)、[provider](../src/integrations/coordinator/provider.ts)、[存储](../src/integrations/coordinator/storage.ts) | [coordinator-provider.test.mjs](../tests/coordinator-provider.test.mjs)、[durable.test.mjs](../tests/durable.test.mjs) |
22
- | MCP、HTTP 与 Web | [MCP 工具](../src/protocol/mcp.ts)、[HTTP 服务](../src/protocol/http/server.ts)、[Web 路由](../src/protocol/http/web.ts)、[页面](../src/web/) | [service.test.mjs](../tests/service.test.mjs)、[web.test.mjs](../tests/web.test.mjs) |
22
+ | MCP、HTTP 与 Web | [MCP 工具](../src/protocol/mcp.ts)、[stdio 接入](../src/protocol/stdio.ts)、[HTTP 服务](../src/protocol/http/server.ts)、[Web 路由](../src/protocol/http/web.ts)、[页面](../src/web/) | [service.test.mjs](../tests/service.test.mjs)、[web.test.mjs](../tests/web.test.mjs) |
23
23
  | CLI 与本机控制协议 | [CLI](../src/protocol/cli/commands.ts)、[管理客户端](../src/protocol/cli/admin-client.ts)、[启动器客户端](../src/integrations/local-service/control.ts) | [web.test.mjs](../tests/web.test.mjs)、[restart.test.mjs](../tests/restart.test.mjs) |
24
- | 装配、启动、关闭与重启 | [main](../src/application/main.ts)、[业务运行实例](../src/application/task-runtime.ts)、[launcher](../src/application/launcher.ts)、[启动准备](../src/application/startup.ts) | [restart.test.mjs](../tests/restart.test.mjs)、[service.test.mjs](../tests/service.test.mjs) |
24
+ | 装配、启动、关闭与重启 | [main](../src/application/main.ts)、[MCP 后台启动](../src/application/mcp-entry.ts)、[业务运行实例](../src/application/task-runtime.ts)、[launcher](../src/application/launcher.ts)、[启动准备](../src/application/startup.ts) | [stdio.test.mjs](../tests/stdio.test.mjs)、[restart.test.mjs](../tests/restart.test.mjs)、[service.test.mjs](../tests/service.test.mjs) |
25
25
  | 安装与发布 | [命令入口](../bin/agent-task.mjs)、[setup](../scripts/setup.mjs)、[安装库](../scripts/install-lib.mjs)、[包门禁](../scripts/pack-release.mjs)、[工作流](../.github/workflows/) | [install.test.mjs](../tests/install.test.mjs)、[setup.test.mjs](../tests/setup.test.mjs)、[发布说明](release.md) |
26
26
 
27
27
  ## 依赖与一致性边界
@@ -42,6 +42,8 @@
42
42
  | 业务 SQLite 与业务服务 | `createTaskRuntime` 创建;关闭先停止业务入口与监督,再关闭存储 |
43
43
  | 协调调用、时钟与取消信号 | `RunSupervisor` 创建并等待其结束;`TaskRuns` 不持有进程或 SDK 对象 |
44
44
  | 节点会话句柄、超时与等待期间的会话 | `NodeSessions` 持有;关闭调用共用同一个完成结果 |
45
+ | 适配器安装进程与安装锁 | `ExecutorRegistry` 管理安装准入与取消,`LocalExecutors` 持有进程和锁;发布后保留旧组件,关闭时等待安装退出 |
46
+ | stdio 接入与共享后台 | `mcp-entry` 连接或启动独立 launcher;stdio 关闭只释放本连接,后台由显式 stop 或服务管理器关闭 |
45
47
  | worker 与 ACP 进程组 | ACP 接入创建与回收;监督器只持有窄会话能力 |
46
48
  | durable storage 与 Harness | 每次协调调用创建;打开失败关闭存储,成功后由 Harness 关闭其存储 |
47
49
  | HTTP listener 与 MCP transport/server | `HttpServer` 持有;请求断开只释放请求资源,应用关闭停止新请求并收尾 |
@@ -1,6 +1,6 @@
1
1
  # 验证导航与证据边界
2
2
 
3
- **模块化重构后,单节点与三节点 DAG 的真实模型 E2E 均一次通过,详见下文复验记录。** 项目不宣称生产可用或外部副作用 exactly-once。
3
+ **当前使用流程简化已通过本地基础测试、适配器安装、独立安装包验收,以及单节点与三节点 DAG 的真实模型复验。** 具体版本、环境和证据见下文。项目不宣称生产可用或外部副作用 exactly-once。
4
4
 
5
5
  ## 本地验证
6
6
 
@@ -12,34 +12,56 @@ git diff --check
12
12
 
13
13
  各职责的测试入口见[代码导航](technical-design.md)。基础测试使用真实本地文件、SQLite、进程和 SDK,不调用外部模型,不使用假模型或模拟 provider。失败进程和 no_model 只证明失败路径;结构化状态用例不证明模型语义判断。`check` 同时执行 TypeScript 与[依赖边界检查](../scripts/check-boundaries.mjs),后者检查模块依赖、循环、SDK 位置和 Run 写入口;它不证明运行时权限或模型语义。
14
14
 
15
- 2026-10-23,Linux x64、Node 24.15.0:模块化重构通过类型与依赖边界检查、42 项基础测试和本地包清单门禁。关键新增证据见 [存储测试](../tests/task-storage.test.mjs)与[任务测试](../tests/tasks.test.mjs):严格记录拒绝而不修复、事务失败回滚触发游标、派发提交先于真实进程执行,以及关闭、取消与迟到回调。临时源码副本中的越层导入、循环依赖和绕过 Run 写入口均被检查器拒绝。
15
+ 2026-10-23,Linux x64、Node 24.15.0:使用流程简化通过类型与依赖边界检查、49 项基础测试和包清单门禁。重点证据:
16
16
 
17
- 本地测试不证明真实模型或 ACP 成功路径;后续真实验收见[模块化重构后复验](#模块化重构后复验)。本次未重新执行跨平台安装验收。
17
+ - [stdio 进程测试](../tests/stdio.test.mjs):两个项目并发连接只保留一个后台;目录默认值、显式覆盖及跨项目请求键独立。关闭 stdin 和向客户端进程组发送 SIGHUP 后,后台仍触发未来任务。这些运行因 `no_model` 失败,只证明持续调度与无误派发,不证明 ACP 成功。
18
+ - [MCP 服务测试](../tests/service.test.mjs):没有原生 CLI 时拒绝连接,管理接口可用;安装到已有 PATH 后,下次连接重新发现。直接 HTTP 提交缺少目录时拒绝。
19
+ - [执行端测试](../tests/executors.test.mjs):原生 CLI 与适配器分开检测,禁用不安装,已有安装离线复用;真实失败安装进程不替换已发布组件,关闭时取消等待且不释放其他进程的锁。
20
+ - [初始化 PTY 测试](../tests/setup.test.mjs):目录、端口和令牌须确认,取消不写文件;协调员密钥隐藏输入,生成令牌不输出。初始化不安装执行端。
21
+ - [启动锁测试](../tests/sqlite.test.mjs):四个真实进程同时争用全新锁文件时,只有一个取得所有权;持锁进程强杀后可重新取得锁。修复前曾复现所有启动器同时争锁失败,修复后使用有限的错开重试,不触发任务重放。
22
+ - [存储测试](../tests/task-storage.test.mjs)与[任务测试](../tests/tasks.test.mjs):固定规范工作目录、按目录去重、严格记录拒绝而不修复、事务失败回滚、先持久派发意图再启动真实进程,以及关闭与迟到回调。
23
+
24
+ 当前版本的真实模型证据见 [0.1.0-rc.1 复验](#010-rc1-复验)。本次未运行 macOS 验收。此前模块化重构的 42 项测试及临时源码副本中的依赖违规拒绝实验也已通过,属于本次改造前的证据。
18
25
 
19
26
  ## 控制台验证
20
27
 
21
- [Web/CLI 用例](../tests/web.test.mjs)使用真实本机服务进程验证两端查询与保存的互通、旧草稿拒绝、执行端授权清除、凭据引用保护、离线失败不写文件,以及独立管理员认证、Host/Origin/请求类型边界、有限任务写入、密钥不回显或进入日志。也验证重启后的配置差异和目录切换不复制历史。[重启用例](../tests/restart.test.mjs)验证前台 CLI、固定端口进程更换、绑定失败不能报成功,以及持久化的不确定派发在重复重启后仍被阻断。实际任务记录来自业务服务,不使用假模型输出。
28
+ [Web/CLI 用例](../tests/web.test.mjs)使用真实本机服务进程验证两端查询与保存的互通、旧草稿拒绝、发现状态和禁用策略一致、凭据引用保护、离线失败不写文件,以及独立管理员认证、Host/Origin/请求类型边界、有限任务写入、密钥不回显或进入日志。也验证重启后的配置差异和目录切换不复制历史。[重启用例](../tests/restart.test.mjs)验证前台 CLI、固定端口进程更换、绑定失败不能报成功,以及持久化的不确定派发在重复重启后仍被阻断。实际任务记录来自业务服务,不使用假模型输出。
22
29
 
23
30
  [配置用例](../tests/settings.test.mjs)验证私有文件边界、旧编辑拒绝、显式凭据环境、端点与密钥引用分离、服务令牌别名不能作为模型凭据、失败时保留文件,以及关闭后拒绝操作。这些用例不构成断电、磁盘满或目录同步失败窗口的完整证据。
24
31
 
25
32
  尚未完成真实浏览器交互、CSP 执行和跨浏览器缓存/自动填充验收;CLI 的编辑器交互和隐藏终端凭据输入也未做终端验收,凭据测试使用 stdin。需在浏览器核对:不可信任务文本不执行脚本或加载外链;离开、刷新和退出后无令牌残留;配置草稿不被轮询覆盖;密钥保存与配置保存的提示明确。HTTP 头检查不能替代这些验证,也不证明模型认证或 ACP 执行成功。
26
33
 
27
- ### 启动与注册调整
34
+ ### 执行端与安装包
35
+
36
+ 同日使用隔离 HOME,从 npm registry 真实安装 pi-acp 0.0.34、codex-acp 2.2.2 和 claude-agent-acp 0.94.0,再确认后续准备复用同一安装。报告位于 `/tmp/agent-task-adapter-install-T68wMA/report.json`。没有启动原生 CLI、发送 ACP 会话或调用模型;成功下载与准备不等于认证或任务成功。
37
+
38
+ 由当前源码打包的本地 tarball 通过独立 prefix 安装验收,不依赖源码或 TypeScript。前台启动、Web HTTP 保存、Web/CLI 重启、stdio 自动启动后台、执行端发现、客户端断开后后台健康,以及显式 stop 均通过。使用真实失败程序提供可发现的原生路径,未执行 ACP 或模型工作;捕获输出未发现验证凭据。报告位于 `/tmp/agent-task-npm-LD5GQJ/report.json`。临时目录可能被系统清理,长期证据需安全归档。
28
39
 
29
- Web/CLI 重启证据见下文 [rc.2 发布验收](#001-rc2当前-latest)。四类执行端的固定命令映射和凭据过滤有本地契约测试;Claude ACP 新增注册目标没有真实握手、认证或任务证据。其他执行端的历史结果也不能代替新原生环境和审批策略下的真实复验。
40
+ 当前候选的发布状态见下文;历史包验收不代替当前 macOS、真实宿主应用或四种执行端的模型任务复验。
30
41
 
31
42
  ## 真实模型验收入口
32
43
 
33
44
  仅在获准环境显式运行,会产生模型费用;不使用个人凭据或真实交易:
34
45
 
35
- - 单节点:`npm run test:e2e`。需安全注入 `DEEPSEEK_API_KEY`,并将 `AGENT_TASK_E2E_EXECUTOR` 指向已审核的 ACP profile。执行端须另行准备原生认证及无领域副作用的配置,不继承协调密钥。
36
- - DAG 与人工回复:`npm run test:e2e:human -- --quantity 4`。需已安装 Pi、pi-acp 和获准 DeepSeek 测试凭据;数量由测试客户端提供,不冒充真人回答。
46
+ - 单节点:`npm run test:e2e`。需安全注入 `DEEPSEEK_API_KEY`,并将 `AGENT_TASK_E2E_EXECUTOR` 设为获准的 `pi`、`codex`、`claude` 或 `opencode`。执行端须另行准备原生认证及无领域副作用的配置,不继承协调密钥。连接时准备缺少的适配器。
47
+ - DAG 与人工回复:`npm run test:e2e:human -- --quantity 4`。需已安装 Pi 和获准 DeepSeek 测试凭据;适配器在连接时准备。数量由测试客户端提供,不冒充真人回答。
37
48
  - 真人模式:`npm run test:e2e:human -- --answer-file /absolute/private/path/quantity.txt`。使用新文件,等待旁边的 `.question.json` 出现,再写入答案。
38
49
 
39
50
  完整前置检查、参数和断言见 [E2E 用例](../tests/e2e/)、[Pi 启动器](../scripts/run-pi-human-e2e.mjs)。Pi 启动器为两端共用获准测试密钥,创建独立原生配置,只启用 read;这不是 OS 沙箱或独立账户隔离。强杀或断电不能保证临时认证清理,需管理员核查。
40
51
 
41
52
  运行证据保留在命令输出的私有目录,可能包含敏感内容;不自动删除或将一次成功推广为普遍支持。
42
53
 
54
+ ### 0.1.0-rc.1 复验
55
+
56
+ 2026-10-23,Linux x64、Node 24.15.0,使用已有获准 DeepSeek 测试凭据、`deepseek-flash` 协调员、隔离的只读 Pi 原生配置及连接时安装的 pi-acp 0.0.34。现有单节点和三节点用例各执行一次,均通过;没有修改交付断言或重试任务。
57
+
58
+ - 单节点约 16.3 秒:真实 ACP 交付包含标识和结果 323,一个会话、一轮执行,结果文件持久化通过。证据:`/tmp/agent-task-real-e2e-Igj91J`;Run:`5d4f2b74-eb41-402c-874e-441b1fb29d91`。
59
+ - 三节点约 36.5 秒:测试客户端提供数量 4,结果 1292;同一 Run 等待并接受回复,节点轮数 1 / 2 / 1,已接受的上游不重放,三个交付均保留标识和必需数值。证据:`/tmp/agent-task-real-e2e-oreA3j`;Run:`3e6dd91b-35a5-4038-945c-c9d402d2829f`。
60
+
61
+ 两项测试均在提交任务后断开客户端,再由新连接观察结果。原始测试日志:`/tmp/agent-task-pi-human-6N0Zlj/test-1.log`。服务正常退出,临时原生认证和服务环境文件已清空,任务、会话、结果及验收摘要保留。临时目录可能被系统清理,长期证据需安全归档。
62
+
63
+ 此证据覆盖当前任务契约和适配器准备路径。系统级自启、真实 Pi 宿主退出、模型执行中重启及 Codex、Claude、OpenCode 模型任务未验收;stdio 后台生命周期由本地进程测试覆盖。只读配置不是 OS 沙箱。
64
+
43
65
  ### 本机三节点验收
44
66
 
45
67
  2026-10-22,Linux x64、Node 24.15.0,使用 DeepSeek 协调员和只读 Pi ACP 执行端,此前带 JSON 格式约束的用例运行 `npm run test:e2e:human -- --quantity 4` 一次通过,约 28 秒。数量由测试客户端提供,结果为 1292,不是真人输入。
@@ -98,7 +120,27 @@ Linux x64、Node 24.15.0 的本机记录:
98
120
 
99
121
  ## npm 发布候选验证
100
122
 
101
- ### 0.0.1-rc.2(当前 latest)
123
+ ### 0.1.0-rc.1(本地验收完成,等待发布)
124
+
125
+ 本候选包含 stdio 共用后台、本机执行端发现与适配器准备、固定任务目录和按目录去重,以及调度周期调整。初始化须确认目录、端口、令牌及协调模型;普通启动不转换旧配置或业务数据。
126
+
127
+ 2026-10-23,Linux x64、Node 24.15.0:锁定依赖重装、类型与依赖边界检查、49 项基础测试、两项真实模型测试、包清单门禁及独立安装验收均通过。真实模型范围见 [0.1.0-rc.1 复验](#010-rc1-复验)。
128
+
129
+ 本地候选包安装报告:`/tmp/agent-task-npm-SxC74r/report.json`。使用独立 prefix 与原生 HOME,不依赖源码或 TypeScript;前台、Web/CLI 重启、stdio 自动启动、断开后后台健康和 stop 均通过。此次依赖下载使用 npm 镜像;发布后的默认安装须另以 npmjs 验证。报告仅把本机 Linux 记为通过,未执行 macOS 验收。发布完成以工作流结果和 registry 核对为准。
130
+
131
+ ### 0.0.1-rc.3(历史证据)
132
+
133
+ 2026-10-23,[GitHub 发布运行 39980466554](https://github.com/realqiyan/agent-task/actions/runs/39980466554)验证提交 `80b12de`:Node 22.19.0/24.x 类型与依赖检查、基础测试、包清单门禁,以及当前 Linux runner 的独立安装、启动、重启和认证检查均通过。当前发布门禁验证宿主环境;macOS 需在对应环境另行验收。安装报告明确标记其他平台未执行,PTY 用例在缺少终端能力时明确跳过。
134
+
135
+ 发布作业使用 Node 24.21.0、npm 11.19.0 和 GitHub OIDC。包版本检查及 provenance 签名成功,但上传包的 npm PUT 请求返回 E404;发布后的默认安装验证因此未执行。随后 registry 查询未发现 `0.0.1-rc.3`,不能把生成 provenance 当作包已发布。
136
+
137
+ 错误指向 npm 发布授权未生效,需核对该包的 Trusted Publisher 与仓库、工作流、Environment 和允许操作是否匹配。本机凭据读取 trust 设置返回 403,尚未确认具体缺失或不匹配项。GitHub 的 `npm` Environment 已限制为 master;工作流配置与 npm 端授权仍需分别核实,见[发布说明](release.md)。
138
+
139
+ 本机检查与 42 项基础测试通过;独立空工具 PATH 下的真实测试进程确认 PTY 用例明确跳过。发布诊断及已去除下载 URL 查询参数的日志保留于 `/tmp/agent-task-rc3-final-d4tqqpt9`,临时目录可能被系统清理。
140
+
141
+ 2026-10-23 后续匿名 registry 核对确认 `0.0.1-rc.3` 已存在,发布元数据包含 provenance;当时 `latest=0.0.1-rc.3`。上述失败运行是历史记录,不代表后续 registry 状态,也不能单凭元数据判定最终成功发布的操作渠道。
142
+
143
+ ### 0.0.1-rc.2(历史证据)
102
144
 
103
145
  2026-10-22,`realqiyan` 已将 **[@realqiyan/agent-task@0.0.1-rc.2](https://www.npmjs.com/package/@realqiyan/agent-task/v/0.0.1-rc.2)** 发布到 npmjs。匿名 registry 查询确认版本、发布账号、MIT 许可证及 `latest=0.0.1-rc.2`。本机直接发布了下述 CI 已验收的同一 tarball,没有重新构建,也没有 GitHub provenance。
104
146
 
@@ -18,20 +18,23 @@
18
18
 
19
19
  - 任务列表、固定 DAG、运行历史、节点进度、已接受交付、错误和问题只读。触发计划结束不等于运行成功,原始输出不等于交付。
20
20
  - 任务写操作仅复用 `task_update`,暂停、恢复或取消未来触发。不停止当前运行,不补跑错过的触发,不回答问题或修改 DAG。已有不确定外部结果的任务拒绝恢复未来触发;当前不提供人工核对后强制恢复能力。
21
- - 数据目录、监听端口、MCP token 环境变量名、执行端注册和协调模型可编辑,下次启动生效。修改 token 变量名保留令牌值,不在页面回显。
22
- - 执行端只选择 pi、codex、claude 或 opencode,保留 ID、默认工作空间和无人值守审核,并可填写备注。启动方式由系统决定;原生认证和权限继续用官方工具管理。没有命令、参数、环境变量清单、结果目录或 ACP 工具白名单配置。
21
+ - 协调模型是常规配置入口。数据目录、监听端口、MCP token 环境变量名和执行端启停位于高级设置,下次启动生效。修改 token 变量名保留令牌值,不在页面回显。
22
+ - 执行端自动检测本机 pi、codex、claude 和 opencode。页面显示本机路径、准备结果和错误,提供重新检测与显式适配器升级;准备过程中自动更新状态。就绪不代表原生认证或工具权限已经验收。安装与更新边界见[部署说明](deployment.md)。
23
+ - 无须手工注册执行端、设置默认工作目录或另行批准。原生认证和权限继续用官方工具管理;任务工作目录通过 MCP 上下文或创建参数确定。
23
24
 
24
25
  页面与接口入口见 [web.ts](../src/protocol/http/web.ts)、[页面模块](../src/web/),任务契约见 [task-contract.ts](../src/protocol/task-contract.ts)。提供受管理员认证保护的重启入口;没有通用 MCP 代理、文件浏览器或下载接口。固定结果目录不授予整个 dataDir 的读取能力。
25
26
 
26
27
  ## 保存与重启
27
28
 
28
29
  1. 密钥只显示是否设置,原值不回显。不提交即保留;替换和删除必须明确操作。先保存新密钥,再保存引用它的配置。新凭据命名和受保护变量见 [settings.ts](../src/configuration/settings.ts)。
29
- 2. 配置先校验并预览影响,再确认保存。校验不调用模型或启动 ACP,不能证明认证、原生权限或实际执行成功。修改执行端类型、ID 或工作空间后,配置服务要求重新明确批准无人值守执行;仅修改备注不改变授权。
30
+ 2. 配置先校验并预览影响,再确认保存。校验不调用模型或启动 ACP,不能证明认证、原生权限或实际执行成功。禁用执行端时显示受影响任务,不自动改派。
30
31
  3. 配置与密钥分别原子替换,不是跨文件事务。更换模型端点须使用新的凭据引用,避免中途重启将新密钥交给旧端点。旧凭据不自动清理。
31
32
  4. 保存不热更新。检查活动运行及待回答运行后,点击“重启并检查可用性”或执行 `agent-task restart`。目录切换、原生环境来源及故障处理见[部署说明](deployment.md)。
32
33
  5. 只有新进程通过认证健康检查才显示成功。页面清除令牌并给出新地址,重新连接后核对运行配置。请求未确认时先用 CLI status 核查,不删除历史、自动回滚或重放执行来修复。
33
34
 
34
- 待回答运行也可能持有 ACP 会话,重启不保证无损续接。执行端类型、工作空间或批准状态与任务快照不一致时,旧任务的派发会被拒绝;页面显示受影响任务,但不改写快照、DAG 或执行端指派。需要新任务时由 MCP 调用方处理。
35
+ 待回答运行也可能持有 ACP 会话,重启不保证无损续接。执行端未就绪、被禁用或本机路径与任务快照不一致时,旧任务的派发会被拒绝;页面不改写快照、DAG 或执行端指派。需要新任务时由 MCP 调用方处理。
36
+
37
+ 重新检测和适配器升级直接作用于当前执行端目录,无需重启;它们不保存启动配置。普通刷新复用已经准备的适配器,只有显式升级才请求更新。升级期间暂停向该执行端派发新工作,完成后可在状态表核对结果。OpenCode 使用原生 ACP,通过原生 CLI 的安装渠道更新。
35
38
 
36
39
  Web 与 CLI 的旧编辑都会被拒绝,不做自动合并。离线编辑配置须先停服。校验与保存职责见 [settings.ts](../src/configuration/settings.ts)、[配置影响预览](../src/configuration/management.ts)、[settings-contract.ts](../src/protocol/settings-contract.ts)、[配置文件适配](../src/configuration/files.ts)。
37
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realqiyan/agent-task",
3
- "version": "0.0.1-rc.3",
3
+ "version": "0.1.0-rc.1",
4
4
  "license": "MIT",
5
5
  "author": "realqiyan",
6
6
  "repository": {
@@ -24,6 +24,8 @@
24
24
  "scripts/probe-agent.mjs",
25
25
  "scripts/mcp-call.mjs",
26
26
  "docs/*.md",
27
+ "docs/adr/*.md",
28
+ "CONTEXT.md",
27
29
  "config.example.json",
28
30
  "LICENSE"
29
31
  ],
@@ -1,25 +1,18 @@
1
- import { spawnSync } from "node:child_process";
2
1
  import { DatabaseSync } from "../dist/infrastructure/sqlite.js";
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync, lstatSync, realpathSync, readdirSync, unlinkSync, openSync, closeSync, fsyncSync, renameSync } from "node:fs";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, lstatSync, realpathSync, unlinkSync, openSync, closeSync, fsyncSync, renameSync } from "node:fs";
4
3
  import { dirname, join, resolve, isAbsolute, basename } from "node:path";
5
4
  import { homedir } from "node:os";
6
5
  import { fileURLToPath } from "node:url";
7
6
 
8
7
  export const packageRoot = realpathSync(fileURLToPath(new URL("..", import.meta.url)));
9
8
 
10
- export const packages = {
11
- pi: ["@earendil-works/pi-coding-agent@1.3.0", "pi-acp@0.0.34"],
12
- codex: ["@agentclientprotocol/codex-acp@2.2.2"],
13
- claude: ["@zed-industries/claude-agent-acp@0.23.1"],
14
- opencode: ["opencode-ai@1.19.3"],
15
- };
16
9
  export function argumentsFor(argv) {
17
- const flags = new Set(["check", "probe", "resume", "help"]), options = {};
10
+ const flags = new Set(["check", "resume", "help", "yes", "generate-tokens"]), options = {};
18
11
  for (let i = 0; i < argv.length; i++) {
19
12
  const key = argv[i].replace(/^--/, "");
20
13
  if (!argv[i].startsWith("--") || key in options) throw new Error("Invalid or repeated option");
21
14
  if (flags.has(key)) options[key] = true;
22
- else if (["home", "executors", "cwd", "provider", "model", "port"].includes(key) && argv[i + 1] && !argv[i + 1].startsWith("--")) options[key] = argv[++i];
15
+ else if (["home", "data-dir", "provider", "model", "port", "token-env", "credentials-file", "base-url", "api", "api-key-env", "context-window", "max-tokens"].includes(key) && argv[i + 1] && !argv[i + 1].startsWith("--")) options[key] = argv[++i];
23
16
  else throw new Error("Unknown option or missing value: " + key);
24
17
  }
25
18
  return options;
@@ -48,12 +41,6 @@ export function createFile(path, text) {
48
41
  mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
49
42
  writeFileSync(path, text, { flag: "wx", mode: 0o600 });
50
43
  }
51
- export function run(command, args, options = {}) {
52
- const env = Object.fromEntries(["HOME", "PATH", "LANG", "LC_ALL", "TZ", "TMPDIR"].filter(key => process.env[key] !== undefined).map(key => [key, process.env[key]]));
53
- const result = spawnSync(command, args, { encoding: "utf8", timeout: 300000, stdio: "pipe", env, ...options });
54
- if (result.error || result.status !== 0) throw new Error("Command failed (diagnostics withheld; may contain credentials): " + command + " " + args[0]);
55
- return result.stdout?.trim();
56
- }
57
44
  export function operationLock(root) {
58
45
  const db = new DatabaseSync(join(root, "setup-lock.sqlite"));
59
46
  try { db.exec("PRAGMA busy_timeout=0; BEGIN EXCLUSIVE"); }
@@ -67,7 +54,7 @@ export function readManifest(root) {
67
54
  return value;
68
55
  }
69
56
  export function saveManifest(root, manifest) {
70
- // Preserve the last committed inventory even if a disk-full write fails.
57
+ // Preserve the last committed initialization record if a write fails.
71
58
  const temporary = join(root, `installation-${process.pid}.tmp`);
72
59
  let fd;
73
60
  try {
@@ -84,10 +71,5 @@ export function saveManifest(root, manifest) {
84
71
  }
85
72
  export function runtimeBaseEnv(manifest) {
86
73
  // Capture the OS user environment; the runtime isolates private service values before worker creation.
87
- return { ...process.env, HOME: manifest.home, PATH: [join(manifest.root, "components/node_modules/.bin"), dirname(manifest.node), process.env.PATH ?? "", "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].join(":"), LANG: process.env.LANG ?? "en_US.UTF-8" };
88
- }
89
- export function inventory(root, relative) {
90
- const path = join(root, relative), stat = lstatSync(path);
91
- if (!stat.isDirectory() || stat.isSymbolicLink()) return [relative];
92
- return readdirSync(path).flatMap(name => inventory(root, join(relative, name))).concat(relative);
74
+ return { ...process.env, HOME: manifest.home, PATH: [process.env.PATH ?? "", dirname(manifest.node), "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].join(":"), LANG: process.env.LANG ?? "en_US.UTF-8" };
93
75
  }
@@ -2,22 +2,23 @@
2
2
  import { spawn } from "node:child_process";
3
3
  import { Readable, Writable } from "node:stream";
4
4
  import * as acp from "@agentclientprotocol/sdk";
5
- import { loadConfig } from "../dist/configuration/files.js";
5
+ import { loadConfig, startupPaths } from "../dist/configuration/files.js";
6
6
  import { executorEnvironment } from "../dist/executors/environment.js";
7
- import { executorCommand } from "../dist/integrations/acp/runtime.js";
7
+ import { executorLaunch } from "../dist/integrations/acp/runtime.js";
8
+ import { LocalExecutors } from "../dist/integrations/acp/discovery.js";
8
9
  import { readEnvironment } from "../dist/configuration/files.js";
9
10
  import { join } from "node:path";
10
11
  import { terminateGroup } from "../dist/integrations/acp/connection.js";
11
12
  import { version } from "../dist/infrastructure/version.js";
12
- const [file, id] = process.argv.slice(2);
13
- if (!file || !id) { console.error("Usage: node scripts/probe-agent.mjs <config.json> <executorId>"); process.exit(2); }
14
- const config = loadConfig(file);
15
- const executor = config.executors.find(value => value.id === id);
16
- if (!executor) throw new Error("Executor not found");
13
+ const [home, id] = process.argv.slice(2);
14
+ if (!home || !id) { console.error("Usage: node scripts/probe-agent.mjs <installation-home> <pi|codex|claude|opencode>"); process.exit(2); }
15
+ const config = loadConfig(startupPaths(home).configPath);
17
16
  const env = executorEnvironment(process.env, readEnvironment(join(config.dataDir, "environment.json")), config);
18
- const launch = executorCommand(executor.type, env);
17
+ const executor = new LocalExecutors(join(home, "adapters"), env).discover().executors.find(value => value.id === id);
18
+ if (!executor || config.disabledExecutors.includes(executor.type)) throw new Error("Executor unavailable; connect MCP to prepare its adapter first");
19
+ const launch = executorLaunch(executor, env);
19
20
  const child = spawn(launch.command, launch.args, {
20
- cwd: executor.cwd, env, detached: true, stdio: ["pipe", "pipe", "pipe"],
21
+ cwd: process.cwd(), env: launch.env, detached: true, stdio: ["pipe", "pipe", "pipe"],
21
22
  });
22
23
  child.stderr.resume();
23
24
  let connection;
@@ -4,6 +4,11 @@ import { launch } from "../dist/application/launcher.js";
4
4
  process.umask(0o077);
5
5
  const root = process.argv[2];
6
6
  if (!root || process.argv.length !== 3) throw new Error("Expected installation home");
7
- const manifest = readManifest(root);
8
- if (manifest.phase !== "ready") throw new Error("Installation is not ready");
9
- await launch(root, runtimeBaseEnv(manifest));
7
+ try {
8
+ const manifest = readManifest(root);
9
+ if (manifest.phase !== "ready") throw new Error("Installation is not ready; run agent-task setup --resume");
10
+ await launch(root, runtimeBaseEnv(manifest));
11
+ } catch (error) {
12
+ console.error(error.message);
13
+ process.exitCode = 1;
14
+ }
@@ -1,24 +1,51 @@
1
1
  import { createInterface } from "node:readline/promises";
2
+ import { Writable } from "node:stream";
3
+ import { join } from "node:path";
2
4
 
5
+ async function secret(question) {
6
+ let muted = false;
7
+ const output = new Writable({ write(chunk, _encoding, callback) { if (!muted) process.stderr.write(chunk); callback(); } });
8
+ const input = createInterface({ input: process.stdin, output, terminal: true });
9
+ try {
10
+ process.stderr.write(question + ": "); muted = true;
11
+ return (await input.question("")).trim();
12
+ } finally { muted = false; process.stderr.write("\n"); input.close(); output.end(); }
13
+ }
14
+
15
+ /** Terminal collection only; setup validates and publishes the settings. */
3
16
  export async function setupWizard(options, root) {
4
- if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Interactive setup requires a terminal; use --executors none|pi,codex,claude,opencode and --cwd ABSOLUTE_DIR for noninteractive setup");
17
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("Setup requires a terminal or explicit --home --data-dir --port --generate-tokens/--credentials-file and --yes");
5
18
  const input = createInterface({ input: process.stdin, output: process.stdout });
6
19
  const abort = new AbortController();
7
20
  input.on("SIGINT", () => abort.abort());
8
- const ask = async (question, fallback) => (await input.question(`${question}${fallback ? " [" + fallback + "]" : ""}: `, { signal: abort.signal })).trim() || fallback;
21
+ const ask = async (question, fallback = "") => (await input.question(`${question}${fallback ? " [" + fallback + "]" : ""}: `, { signal: abort.signal })).trim() || fallback;
22
+ let needsKey = false;
9
23
  try {
10
- console.log(`Private installation: ${root}`);
11
- options.executors ??= await ask("Executors to install (none or comma-separated pi,codex,claude,opencode)", "none");
12
- options.cwd ??= await ask("Existing absolute workspace (outside installation and npm package)", process.cwd());
13
- if (!options.provider) {
14
- const provider = await ask("Coordinator provider (none/openai/anthropic/deepseek; configurable later in Web)", "none");
15
- if (provider !== "none") options.provider = provider;
24
+ options.home = await ask("Private installation directory", options.home ?? root);
25
+ options["data-dir"] = await ask("Private data directory", options["data-dir"] ?? join(options.home, "data"));
26
+ options.port = await ask("Loopback HTTP port (0 assigns an available port)", options.port ?? "8765");
27
+ options["token-env"] = await ask("MCP token variable name", options["token-env"] ?? "AGENT_TASK_TOKEN");
28
+ const tokens = await ask("Service/admin tokens (generate/file)", options["credentials-file"] ? "file" : "generate");
29
+ if (tokens === "generate") options["generate-tokens"] = true;
30
+ else if (tokens === "file") options["credentials-file"] = await ask("Private credentials JSON file", options["credentials-file"]);
31
+ else throw new Error("Choose generate or file for tokens");
32
+ options.provider = await ask("Coordinator provider (none/openai/anthropic/deepseek/openai-compatible)", options.provider ?? "none");
33
+ if (options.provider !== "none") {
34
+ options.model = await ask("Coordinator model ID", options.model);
35
+ if (options.provider === "openai-compatible") {
36
+ options["base-url"] = await ask("Model endpoint URL", options["base-url"]);
37
+ options.api = await ask("API (openai-completions/openai-responses)", options.api ?? "openai-completions");
38
+ options["api-key-env"] = await ask("Coordinator credential name", options["api-key-env"] ?? "COORDINATOR_API_KEY");
39
+ options["context-window"] = await ask("Model context window", options["context-window"]);
40
+ options["max-tokens"] = await ask("Maximum output tokens", options["max-tokens"]);
41
+ }
42
+ needsKey = !options["credentials-file"];
16
43
  }
17
- if (options.provider) options.model ??= await ask("Coordinator model ID", "");
18
- options.port ??= await ask("Loopback HTTP port", "8765");
19
- if (await ask("Write private configuration and install selected components? (yes/no)", "no") !== "yes") throw new Error("Setup cancelled; no configuration written");
44
+ if (await ask("Confirm directories, port, token configuration and coordinator? (yes/no)", "no") !== "yes") throw new Error("Setup cancelled; no configuration written");
45
+ options.yes = true;
20
46
  } catch (error) {
21
47
  if (abort.signal.aborted) throw new Error("Setup cancelled; no configuration written");
22
48
  throw error;
23
49
  } finally { input.close(); }
50
+ if (needsKey) options.coordinatorKey = await secret("Coordinator API key (hidden)");
24
51
  }