patchwarden 0.6.4 → 1.1.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 (212) hide show
  1. package/README.en.md +18 -18
  2. package/README.md +20 -20
  3. package/dist/agents/agentRouter.d.ts +40 -0
  4. package/dist/agents/agentRouter.js +95 -0
  5. package/dist/config.d.ts +1 -1
  6. package/dist/config.js +6 -2
  7. package/dist/controlCenter.js +15 -2
  8. package/dist/doctor.js +32 -1
  9. package/dist/goal/acceptanceEngine.d.ts +62 -0
  10. package/dist/goal/acceptanceEngine.js +103 -0
  11. package/dist/goal/acceptanceTemplate.d.ts +10 -0
  12. package/dist/goal/acceptanceTemplate.js +104 -0
  13. package/dist/goal/goalGraph.d.ts +58 -0
  14. package/dist/goal/goalGraph.js +189 -0
  15. package/dist/goal/goalProgress.d.ts +81 -0
  16. package/dist/goal/goalProgress.js +216 -0
  17. package/dist/goal/goalStatus.d.ts +60 -0
  18. package/dist/goal/goalStatus.js +137 -0
  19. package/dist/goal/goalStore.d.ts +79 -0
  20. package/dist/goal/goalStore.js +211 -0
  21. package/dist/goal/handoffExport.d.ts +34 -0
  22. package/dist/goal/handoffExport.js +183 -0
  23. package/dist/goal/subgoalSync.d.ts +40 -0
  24. package/dist/goal/subgoalSync.js +72 -0
  25. package/dist/goal/worktreeManager.d.ts +96 -0
  26. package/dist/goal/worktreeManager.js +292 -0
  27. package/dist/logging.d.ts +44 -0
  28. package/dist/logging.js +68 -0
  29. package/dist/release/releaseGate.d.ts +99 -0
  30. package/dist/release/releaseGate.js +475 -0
  31. package/dist/runner/postTaskCleanup.d.ts +13 -0
  32. package/dist/runner/postTaskCleanup.js +147 -0
  33. package/dist/runner/runTask.js +50 -10
  34. package/dist/security/discoveryTokenStore.d.ts +62 -0
  35. package/dist/security/discoveryTokenStore.js +100 -0
  36. package/dist/security/toolInvocationGuard.d.ts +35 -0
  37. package/dist/security/toolInvocationGuard.js +153 -0
  38. package/dist/smoke-test.js +18 -10
  39. package/dist/taskRuntime.d.ts +17 -0
  40. package/dist/test/unit/acceptance-engine.test.d.ts +1 -0
  41. package/dist/test/unit/acceptance-engine.test.js +228 -0
  42. package/dist/test/unit/agent-router.test.d.ts +1 -0
  43. package/dist/test/unit/agent-router.test.js +287 -0
  44. package/dist/test/unit/audit-checks.test.d.ts +1 -0
  45. package/dist/test/unit/audit-checks.test.js +350 -0
  46. package/dist/test/unit/diagnose-task.test.d.ts +1 -0
  47. package/dist/test/unit/diagnose-task.test.js +457 -0
  48. package/dist/test/unit/discovery-token-store.test.d.ts +1 -0
  49. package/dist/test/unit/discovery-token-store.test.js +139 -0
  50. package/dist/test/unit/goal-graph.test.d.ts +1 -0
  51. package/dist/test/unit/goal-graph.test.js +298 -0
  52. package/dist/test/unit/goal-progress.test.d.ts +1 -0
  53. package/dist/test/unit/goal-progress.test.js +381 -0
  54. package/dist/test/unit/goal-status.test.d.ts +1 -0
  55. package/dist/test/unit/goal-status.test.js +215 -0
  56. package/dist/test/unit/goal-store.test.d.ts +1 -0
  57. package/dist/test/unit/goal-store.test.js +253 -0
  58. package/dist/test/unit/goal-subgoal-task.test.d.ts +1 -0
  59. package/dist/test/unit/goal-subgoal-task.test.js +55 -0
  60. package/dist/test/unit/goal-tools-registry.test.d.ts +1 -0
  61. package/dist/test/unit/goal-tools-registry.test.js +190 -0
  62. package/dist/test/unit/handoff-export.test.d.ts +1 -0
  63. package/dist/test/unit/handoff-export.test.js +263 -0
  64. package/dist/test/unit/invoke-discovered-tool.test.d.ts +1 -0
  65. package/dist/test/unit/invoke-discovered-tool.test.js +167 -0
  66. package/dist/test/unit/logging.test.js +127 -5
  67. package/dist/test/unit/post-task-cleanup.test.d.ts +1 -0
  68. package/dist/test/unit/post-task-cleanup.test.js +48 -0
  69. package/dist/test/unit/reconcile-tasks.test.d.ts +1 -0
  70. package/dist/test/unit/reconcile-tasks.test.js +456 -0
  71. package/dist/test/unit/release-gate.test.d.ts +1 -0
  72. package/dist/test/unit/release-gate.test.js +242 -0
  73. package/dist/test/unit/safe-views.test.d.ts +1 -0
  74. package/dist/test/unit/safe-views.test.js +171 -0
  75. package/dist/test/unit/schema-drift-check.test.d.ts +1 -0
  76. package/dist/test/unit/schema-drift-check.test.js +175 -0
  77. package/dist/test/unit/subgoal-sync.test.d.ts +1 -0
  78. package/dist/test/unit/subgoal-sync.test.js +183 -0
  79. package/dist/test/unit/tool-invocation-guard.test.d.ts +1 -0
  80. package/dist/test/unit/tool-invocation-guard.test.js +432 -0
  81. package/dist/test/unit/tool-usage-stats.test.d.ts +1 -0
  82. package/dist/test/unit/tool-usage-stats.test.js +300 -0
  83. package/dist/test/unit/toolSearch.test.d.ts +1 -0
  84. package/dist/test/unit/toolSearch.test.js +571 -0
  85. package/dist/test/unit/worktree-manager.test.d.ts +1 -0
  86. package/dist/test/unit/worktree-manager.test.js +176 -0
  87. package/dist/tools/auditTask.d.ts +103 -1
  88. package/dist/tools/auditTask.js +461 -8
  89. package/dist/tools/cancelTask.js +1 -1
  90. package/dist/tools/checkReleaseGate.d.ts +21 -0
  91. package/dist/tools/checkReleaseGate.js +22 -0
  92. package/dist/tools/createTask.d.ts +18 -2
  93. package/dist/tools/createTask.js +29 -1
  94. package/dist/tools/diagnoseTask.d.ts +45 -0
  95. package/dist/tools/diagnoseTask.js +347 -0
  96. package/dist/tools/discardWorktree.d.ts +24 -0
  97. package/dist/tools/discardWorktree.js +27 -0
  98. package/dist/tools/discoverTools.d.ts +11 -0
  99. package/dist/tools/discoverTools.js +39 -0
  100. package/dist/tools/explainTool.d.ts +11 -0
  101. package/dist/tools/explainTool.js +21 -0
  102. package/dist/tools/getTaskSummary.js +2 -1
  103. package/dist/tools/goalSubgoalTask.d.ts +51 -0
  104. package/dist/tools/goalSubgoalTask.js +110 -0
  105. package/dist/tools/healthCheck.d.ts +1 -0
  106. package/dist/tools/healthCheck.js +5 -1
  107. package/dist/tools/invokeDiscoveredTool.d.ts +37 -0
  108. package/dist/tools/invokeDiscoveredTool.js +112 -0
  109. package/dist/tools/listTasks.d.ts +3 -1
  110. package/dist/tools/listTasks.js +14 -1
  111. package/dist/tools/mergeWorktree.d.ts +24 -0
  112. package/dist/tools/mergeWorktree.js +27 -0
  113. package/dist/tools/reconcileTasks.d.ts +41 -0
  114. package/dist/tools/reconcileTasks.js +352 -0
  115. package/dist/tools/registry.js +550 -0
  116. package/dist/tools/safeStatus.d.ts +31 -1
  117. package/dist/tools/safeStatus.js +43 -1
  118. package/dist/tools/safeViews.d.ts +256 -0
  119. package/dist/tools/safeViews.js +250 -0
  120. package/dist/tools/schemaDriftCheck.d.ts +46 -0
  121. package/dist/tools/schemaDriftCheck.js +80 -0
  122. package/dist/tools/toolCatalog.d.ts +4 -3
  123. package/dist/tools/toolCatalog.js +33 -11
  124. package/dist/tools/toolRegistry.d.ts +61 -0
  125. package/dist/tools/toolRegistry.js +724 -0
  126. package/dist/tools/toolSearch.d.ts +110 -0
  127. package/dist/tools/toolSearch.js +331 -0
  128. package/dist/tools/toolUsageStats.d.ts +41 -0
  129. package/dist/tools/toolUsageStats.js +116 -0
  130. package/dist/tools/waitForTask.js +1 -0
  131. package/dist/version.d.ts +2 -2
  132. package/dist/version.js +2 -2
  133. package/dist/watcherStatus.d.ts +29 -0
  134. package/dist/watcherStatus.js +92 -1
  135. package/package.json +3 -2
  136. package/scripts/checks/control-smoke.js +2 -2
  137. package/scripts/checks/doctor-smoke.js +1 -0
  138. package/scripts/checks/lifecycle-smoke.js +13 -9
  139. package/scripts/checks/mcp-manifest-check.js +53 -6
  140. package/scripts/checks/mcp-smoke.js +36 -9
  141. package/scripts/checks/watcher-supervisor-smoke.js +2 -2
  142. package/src/agents/agentRouter.ts +149 -0
  143. package/src/config.ts +9 -3
  144. package/src/controlCenter.ts +17 -3
  145. package/src/doctor.ts +36 -1
  146. package/src/goal/acceptanceEngine.ts +160 -0
  147. package/src/goal/acceptanceTemplate.ts +121 -0
  148. package/src/goal/goalGraph.ts +225 -0
  149. package/src/goal/goalProgress.ts +301 -0
  150. package/src/goal/goalStatus.ts +234 -0
  151. package/src/goal/goalStore.ts +306 -0
  152. package/src/goal/handoffExport.ts +211 -0
  153. package/src/goal/subgoalSync.ts +82 -0
  154. package/src/goal/worktreeManager.ts +404 -0
  155. package/src/logging.ts +91 -0
  156. package/src/release/releaseGate.ts +567 -0
  157. package/src/runner/postTaskCleanup.ts +154 -0
  158. package/src/runner/runTask.ts +49 -10
  159. package/src/security/discoveryTokenStore.ts +157 -0
  160. package/src/security/toolInvocationGuard.ts +251 -0
  161. package/src/smoke-test.ts +15 -7
  162. package/src/taskRuntime.ts +17 -0
  163. package/src/test/unit/acceptance-engine.test.ts +261 -0
  164. package/src/test/unit/agent-router.test.ts +342 -0
  165. package/src/test/unit/audit-checks.test.ts +567 -0
  166. package/src/test/unit/diagnose-task.test.ts +544 -0
  167. package/src/test/unit/discovery-token-store.test.ts +181 -0
  168. package/src/test/unit/goal-graph.test.ts +347 -0
  169. package/src/test/unit/goal-progress.test.ts +538 -0
  170. package/src/test/unit/goal-status.test.ts +270 -0
  171. package/src/test/unit/goal-store.test.ts +318 -0
  172. package/src/test/unit/goal-subgoal-task.test.ts +72 -0
  173. package/src/test/unit/goal-tools-registry.test.ts +243 -0
  174. package/src/test/unit/handoff-export.test.ts +295 -0
  175. package/src/test/unit/invoke-discovered-tool.test.ts +216 -0
  176. package/src/test/unit/logging.test.ts +177 -5
  177. package/src/test/unit/post-task-cleanup.test.ts +53 -0
  178. package/src/test/unit/reconcile-tasks.test.ts +551 -0
  179. package/src/test/unit/release-gate.test.ts +314 -0
  180. package/src/test/unit/safe-views.test.ts +184 -0
  181. package/src/test/unit/schema-drift-check.test.ts +258 -0
  182. package/src/test/unit/subgoal-sync.test.ts +236 -0
  183. package/src/test/unit/tool-invocation-guard.test.ts +542 -0
  184. package/src/test/unit/tool-usage-stats.test.ts +384 -0
  185. package/src/test/unit/toolSearch.test.ts +756 -0
  186. package/src/test/unit/worktree-manager.test.ts +247 -0
  187. package/src/tools/auditTask.ts +831 -402
  188. package/src/tools/cancelTask.ts +1 -1
  189. package/src/tools/checkReleaseGate.ts +45 -0
  190. package/src/tools/createTask.ts +64 -6
  191. package/src/tools/diagnoseTask.ts +460 -0
  192. package/src/tools/discardWorktree.ts +42 -0
  193. package/src/tools/discoverTools.ts +51 -0
  194. package/src/tools/explainTool.ts +26 -0
  195. package/src/tools/getTaskSummary.ts +2 -1
  196. package/src/tools/goalSubgoalTask.ts +170 -0
  197. package/src/tools/healthCheck.ts +4 -1
  198. package/src/tools/invokeDiscoveredTool.ts +163 -0
  199. package/src/tools/listTasks.ts +16 -2
  200. package/src/tools/mergeWorktree.ts +42 -0
  201. package/src/tools/reconcileTasks.ts +464 -0
  202. package/src/tools/registry.ts +618 -1
  203. package/src/tools/safeStatus.ts +73 -2
  204. package/src/tools/safeViews.ts +271 -0
  205. package/src/tools/schemaDriftCheck.ts +120 -0
  206. package/src/tools/toolCatalog.ts +34 -11
  207. package/src/tools/toolRegistry.ts +786 -0
  208. package/src/tools/toolSearch.ts +464 -0
  209. package/src/tools/toolUsageStats.ts +151 -0
  210. package/src/tools/waitForTask.ts +1 -0
  211. package/src/version.ts +2 -2
  212. package/src/watcherStatus.ts +97 -1
package/README.en.md CHANGED
@@ -8,10 +8,9 @@
8
8
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933.svg)](https://nodejs.org/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
10
 
11
- Current stable release: **v0.6.4**. See the
12
- [release notes](docs/release-v0.6.4.md),
13
- [migration guide](docs/migration-from-safe-bifrost.md), and
14
- [GitHub Release](https://github.com/jiezeng2004-design/PatchWarden/releases/tag/v0.6.4).
11
+ Current source version: **v1.1.0**. See the
12
+ [CHANGELOG](CHANGELOG.md), [migration guide](docs/migration-from-safe-bifrost.md), and
13
+ [release checklist](docs/release-checklist.md). Verify GitHub Release / npm publication separately before release.
15
14
 
16
15
  PatchWarden is a local-first MCP safety and verification layer for AI coding
17
16
  agents, with workspace confinement, command allowlists, scope-violation
@@ -266,7 +265,7 @@ execution still requires a separate Watcher.
266
265
  New-Item -ItemType Directory .\patchwarden-runtime
267
266
  Set-Location .\patchwarden-runtime
268
267
  npm.cmd init -y
269
- npm.cmd install patchwarden@0.6.4
268
+ npm.cmd install patchwarden@<published-version>
270
269
  Copy-Item .\node_modules\patchwarden\examples\config.example.json .\patchwarden.config.json
271
270
  $env:PATCHWARDEN_CONFIG = (Resolve-Path .\patchwarden.config.json)
272
271
  node .\node_modules\patchwarden\dist\runner\watch.js
@@ -275,10 +274,10 @@ node .\node_modules\patchwarden\dist\runner\watch.js
275
274
  An MCP client can launch:
276
275
 
277
276
  ```text
278
- npx.cmd -y patchwarden@0.6.4
277
+ npx.cmd -y patchwarden@<published-version>
279
278
  ```
280
279
 
281
- Pin the version in important environments instead of using `latest`
280
+ Replace `<published-version>` with a version that exists on npm/GitHub, and pin that version in important environments instead of using `latest`
282
281
  unconditionally.
283
282
 
284
283
  ## Complete configuration guide
@@ -331,7 +330,7 @@ Configuration fields:
331
330
  | `workspaceRoot` | Yes | The only workspace root PatchWarden may access. |
332
331
  | `plansDir` | Yes | Plan directory, normally `.patchwarden/plans`. |
333
332
  | `tasksDir` | Yes | Task and result directory, normally `.patchwarden/tasks`. |
334
- | `toolProfile` | No | `full` or `chatgpt_core`; use `full` for local clients. |
333
+ | `toolProfile` | No | `full`, `chatgpt_core`, `chatgpt_direct`, or `chatgpt_search`; use `full` for local clients and `chatgpt_search` for compact discovery-driven clients. |
335
334
  | `agents` | Yes | Execution-agent allowlist; supports `{repo}` and `{prompt}` placeholders. |
336
335
  | `allowedTestCommands` | Yes | Exact allowlist for independent verification commands. |
337
336
  | `repoAllowedTestCommands` | No | Extra exact commands keyed by workspace-relative repository path; wildcards are unsupported. |
@@ -434,7 +433,7 @@ Pinned npm configuration:
434
433
  ```toml
435
434
  [mcp_servers.patchwarden]
436
435
  command = "npx.cmd"
437
- args = ["-y", "patchwarden@0.6.4"]
436
+ args = ["-y", "patchwarden@<published-version>"]
438
437
 
439
438
  [mcp_servers.patchwarden.env]
440
439
  PATCHWARDEN_CONFIG = "D:\\path\\to\\patchwarden.config.json"
@@ -494,7 +493,7 @@ PatchWarden.cmd start core
494
493
  The launcher:
495
494
 
496
495
  - Builds `dist/index.js` if it is missing.
497
- - Verifies v0.6.4, the fixed 17-tool `chatgpt_core` catalog, and its schema
496
+ - Verifies v1.1.0, the fixed 21-tool `chatgpt_core` catalog, and its schema
498
497
  manifest.
499
498
  - Reads or prompts for the Tunnel ID.
500
499
  - Reads or prompts for the runtime API key.
@@ -742,7 +741,7 @@ Optional token configuration:
742
741
  Set the token in the PowerShell process that starts the server:
743
742
 
744
743
  ```powershell
745
- $env:PATCHWARDEN_OWNER_TOKEN = "use-a-random-local-only-value"
744
+ $env:PATCHWARDEN_OWNER_TOKEN = "<token>"
746
745
  ```
747
746
 
748
747
  Clients can send `Authorization: Bearer ...` or `x-patchwarden-token`. Never
@@ -931,13 +930,13 @@ names.
931
930
 
932
931
  ## MCP tools and profiles
933
932
 
934
- `chatgpt_core` is the fixed 17-tool profile used by the ChatGPT tunnel:
933
+ `chatgpt_core` is the fixed 21-tool profile used by the ChatGPT tunnel:
935
934
 
936
935
  `health_check`, `list_agents`, `list_workspace`,
937
936
  `read_workspace_file`, `save_plan`, `create_task`,
938
937
  `wait_for_task`, `get_task_summary`, `get_diff`, `get_result`,
939
938
  `get_result_json`, `get_test_log`, `get_task_status`, `list_tasks`,
940
- `cancel_task`, `audit_task`, and `safe_status`.
939
+ `cancel_task`, `audit_task`, `safe_status`, `safe_result`, `safe_audit`, `safe_test_summary`, and `safe_diff_summary`.
941
940
 
942
941
  `get_task_summary` keeps the backward-compatible `standard` view by default.
943
942
  ChatGPT should request `view: "compact"` first; terminal `wait_for_task`
@@ -957,7 +956,7 @@ to `full`.
957
956
 
958
957
  ### ChatGPT Direct mode
959
958
 
960
- Direct mode exposes ten guarded tools so ChatGPT can create an editing
959
+ Direct mode exposes thirteen guarded tools so ChatGPT can create an editing
961
960
  session, read and search source files, apply hash-bound JSON patches, run
962
961
  exactly allowlisted verification commands, finalize the evidence, and audit
963
962
  the result without a local execution agent.
@@ -981,7 +980,7 @@ On first use, provide the `tunnel-client.exe` path and a Tunnel ID dedicated
981
980
  to the Direct Connector. The launcher uses the `patchwarden-direct` profile,
982
981
  stores runtime state under `%LOCALAPPDATA%\patchwarden\runtime-direct`, skips
983
982
  the Watcher, and retains the existing DPAPI credential handling. In a fresh
984
- ChatGPT conversation, `health_check` should report `chatgpt_direct`, ten
983
+ ChatGPT conversation, `health_check` should report `chatgpt_direct`, thirteen
985
984
  tools, and `direct_profile_enabled=true`.
986
985
 
987
986
  ## Security boundaries and local data
@@ -1017,7 +1016,7 @@ Start with a dedicated test workspace and a repository you can recover.
1017
1016
  Upgrade a pinned npm installation:
1018
1017
 
1019
1018
  ```powershell
1020
- npm.cmd install patchwarden@0.6.4
1019
+ npm.cmd install patchwarden@<published-version>
1021
1020
  ```
1022
1021
 
1023
1022
  Upgrade a source checkout:
@@ -1101,8 +1100,9 @@ and release-asset checksums independently.
1101
1100
  - [x] ChatGPT Connector / tunnel
1102
1101
  - [x] Doctor and runtime health checks
1103
1102
  - [x] Tool manifest and schema-drift detection
1104
- - [ ] Worktree isolation
1105
- - [ ] Multi-agent task queue
1103
+ - [x] Release Gate (five-stage pre-release verification)
1104
+ - [x] Worktree isolation
1105
+ - [x] Multi-agent routing
1106
1106
  - [ ] Local dashboard
1107
1107
 
1108
1108
  ## License
package/README.md CHANGED
@@ -8,10 +8,9 @@
8
8
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933.svg)](https://nodejs.org/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
10
 
11
- 当前稳定版本:**v0.6.4**。查看
12
- [发布说明](docs/release-v0.6.4.md)
13
- [迁移指南](docs/migration-from-safe-bifrost.md)
14
- [GitHub Release](https://github.com/jiezeng2004-design/PatchWarden/releases/tag/v0.6.4)。
11
+ 当前源码版本:**v1.1.0**。查看
12
+ [CHANGELOG](CHANGELOG.md)、[迁移指南](docs/migration-from-safe-bifrost.md)
13
+ [发布检查清单](docs/release-checklist.md)。GitHub Release / npm 发布状态需要在发布前单独核对。
15
14
 
16
15
  PatchWarden 是一个面向本地编程 Agent 的安全 MCP 桥接器。上游的
17
16
  ChatGPT、Codex、OpenCode 或其他 MCP 客户端负责规划与验收,
@@ -180,7 +179,7 @@ npm 包适合让 MCP 客户端通过固定版本启动 PatchWarden。为了执
180
179
  New-Item -ItemType Directory .\patchwarden-runtime
181
180
  Set-Location .\patchwarden-runtime
182
181
  npm.cmd init -y
183
- npm.cmd install patchwarden@0.6.4
182
+ npm.cmd install patchwarden@<published-version>
184
183
  Copy-Item .\node_modules\patchwarden\examples\config.example.json .\patchwarden.config.json
185
184
  $env:PATCHWARDEN_CONFIG = (Resolve-Path .\patchwarden.config.json)
186
185
  node .\node_modules\patchwarden\dist\runner\watch.js
@@ -189,10 +188,10 @@ node .\node_modules\patchwarden\dist\runner\watch.js
189
188
  MCP 客户端可以使用:
190
189
 
191
190
  ```text
192
- npx.cmd -y patchwarden@0.6.4
191
+ npx.cmd -y patchwarden@<published-version>
193
192
  ```
194
193
 
195
- 建议固定版本号,不要在重要环境中无条件使用 `latest`。
194
+ `<published-version>` 替换为已经在 npm/GitHub 上确认存在的版本;不要在重要环境中无条件使用 `latest`。
196
195
 
197
196
  ## 完整配置说明
198
197
 
@@ -244,7 +243,7 @@ Copy-Item .\examples\config.example.json .\patchwarden.config.json
244
243
  | `workspaceRoot` | 是 | PatchWarden 唯一允许访问的工作区根目录。 |
245
244
  | `plansDir` | 是 | 计划目录,通常使用 `.patchwarden/plans`。 |
246
245
  | `tasksDir` | 是 | 任务和结果目录,通常使用 `.patchwarden/tasks`。 |
247
- | `toolProfile` | 否 | `full` 或 `chatgpt_core`;本地客户端推荐 `full`。 |
246
+ | `toolProfile` | 否 | `full`、`chatgpt_core`、`chatgpt_direct` 或 `chatgpt_search`;本地客户端推荐 `full`,动态工具发现场景使用 `chatgpt_search`。 |
248
247
  | `agents` | 是 | 可执行 Agent 白名单;支持 `{repo}` 和 `{prompt}` 占位符。 |
249
248
  | `allowedTestCommands` | 是 | 独立验证命令白名单,调用时必须精确匹配。 |
250
249
  | `repoAllowedTestCommands` | 否 | 按工作区相对仓库路径增加精确验证命令;不支持通配符。 |
@@ -342,7 +341,7 @@ npm.cmd run watch
342
341
  ```toml
343
342
  [mcp_servers.patchwarden]
344
343
  command = "npx.cmd"
345
- args = ["-y", "patchwarden@0.6.4"]
344
+ args = ["-y", "patchwarden@<published-version>"]
346
345
 
347
346
  [mcp_servers.patchwarden.env]
348
347
  PATCHWARDEN_CONFIG = "D:\\path\\to\\patchwarden.config.json"
@@ -400,7 +399,7 @@ PatchWarden.cmd start core
400
399
  启动器会:
401
400
 
402
401
  - 检查 `dist/index.js`,缺失时自动构建。
403
- - 校验 `chatgpt_core` 的版本、16 个核心工具和 Schema Manifest。
402
+ - 校验 `chatgpt_core` 的版本、21 个核心工具和 Schema Manifest。
404
403
  - 读取或提示输入 Tunnel ID。
405
404
  - 读取或提示输入运行时 API Key。
406
405
  - 使用 Windows DPAPI 保存凭据到 `%APPDATA%\patchwarden`。
@@ -837,18 +836,18 @@ PatchWarden v0.4.0 不自动读取旧 CLI、旧环境变量、旧 Header、旧
837
836
 
838
837
  ## MCP 工具与 Profile
839
838
 
840
- `chatgpt_core` 是固定的 17 工具 Profile,适合 ChatGPT Tunnel:
839
+ `chatgpt_core` 是固定的 21 工具 Profile,适合 ChatGPT Tunnel:
841
840
 
842
841
  `health_check`、`list_agents`、`list_workspace`、
843
842
  `read_workspace_file`、`save_plan`、`create_task`、
844
843
  `wait_for_task`、`get_task_summary`、`get_diff`、`get_result`、
845
844
  `get_result_json`、`get_test_log`、`get_task_status`、`list_tasks`、
846
- `cancel_task`、`audit_task`、`safe_status`。
845
+ `cancel_task`、`audit_task`、`safe_status`、`safe_result`、`safe_audit`、`safe_test_summary`、`safe_diff_summary`。
847
846
 
848
847
  `get_task_summary` 默认保留兼容的 `standard` 视图;ChatGPT 应优先使用
849
848
  `view: "compact"`,终态 `wait_for_task` 也只内嵌 compact 验收证据。
850
849
 
851
- `full` 提供完整 30 工具本地开发目录,包含核心工具、管理工具和 Direct
850
+ `full` 提供完整 54 工具本地开发目录,包含核心工具、管理工具和 Direct
852
851
  工具。除 `chatgpt_core` 外,常用额外管理工具包括:
853
852
 
854
853
  - `get_plan`
@@ -858,15 +857,15 @@ PatchWarden v0.4.0 不自动读取旧 CLI、旧环境变量、旧 Header、旧
858
857
  - `get_task_stdout_tail`
859
858
  - `get_task_log_tail`
860
859
 
861
- `chatgpt_direct` 是 v0.6.0 新增的 Direct 直接开发 Profile,v0.6.4 包含 10 个工具:
860
+ `chatgpt_direct` 是 v0.6.0 新增的 Direct 直接开发 Profile,v1.1 包含 13 个工具:
862
861
 
863
862
  `health_check`、`list_workspace`、`create_direct_session`、
864
863
  `search_workspace`、`read_workspace_file`、`apply_patch`、
865
- `run_verification`、`finalize_direct_session`、`audit_session`、`sync_file`。
864
+ `run_verification`、`finalize_direct_session`、`audit_session`、`safe_direct_summary`、`safe_finalize_direct_session`、`safe_audit_direct_session`、`sync_file`。
866
865
 
867
866
  `chatgpt_direct` 默认关闭,需要通过 `enableDirectProfile: true` 或
868
867
  `PATCHWARDEN_TOOL_PROFILE=chatgpt_direct` 显式启用。`chatgpt_core` 保持
869
- 17 工具不变。
868
+ 21 工具清单。
870
869
 
871
870
  Tunnel 包装脚本会强制使用 `chatgpt_core`;普通本地开发默认使用 `full`。
872
871
 
@@ -921,7 +920,7 @@ Tunnel ID。启动器使用 `patchwarden-direct` Profile、独立的
921
920
  Tunnel API Key 仍通过现有 Windows DPAPI 缓存处理,不会写入仓库。
922
921
 
923
922
  连接 ChatGPT 后新建对话并先调用 `health_check`。预期
924
- `tool_profile=chatgpt_direct`、`tool_count=10`、
923
+ `tool_profile=chatgpt_direct`、`tool_count=13`、
925
924
  `direct_profile_enabled=true`。已有对话可能缓存旧工具清单,需要重新连接
926
925
  Connector 后再新建对话。
927
926
 
@@ -984,7 +983,7 @@ PatchWarden 能降低误操作风险,但不能替代人工审查。第一次
984
983
  升级 npm 固定版本:
985
984
 
986
985
  ```powershell
987
- npm.cmd install patchwarden@0.6.4
986
+ npm.cmd install patchwarden@<published-version>
988
987
  ```
989
988
 
990
989
  源码升级:
@@ -1067,8 +1066,9 @@ Release 和发布资产校验值。
1067
1066
  - [x] ChatGPT Connector / Tunnel
1068
1067
  - [x] Doctor 与运行时健康检查
1069
1068
  - [x] Tool Manifest 与 Schema 漂移检测
1070
- - [ ] Worktree 隔离
1071
- - [ ] Agent 任务队列
1069
+ - [x] Release Gate(发布前五阶段校验)
1070
+ - [x] Worktree 隔离
1071
+ - [x] 多 Agent 路由
1072
1072
  - [ ] 本地 Dashboard
1073
1073
 
1074
1074
  ## License
@@ -0,0 +1,40 @@
1
+ export interface AgentRouteInput {
2
+ goal?: string;
3
+ scope?: string[];
4
+ inline_plan?: string;
5
+ template?: string;
6
+ configuredAgents: string[];
7
+ }
8
+ export interface AgentRouteResult {
9
+ recommended_agent: string;
10
+ reason: string;
11
+ fallback: boolean;
12
+ }
13
+ export declare const AGENT_ROUTING_RULES: {
14
+ /** 大规模代码修改 → opencode */
15
+ readonly largeScope: {
16
+ readonly agent: "opencode";
17
+ readonly threshold: 10;
18
+ };
19
+ /** 单文件修改 → patchwarden-direct */
20
+ readonly singleFile: {
21
+ readonly agent: "patchwarden-direct";
22
+ };
23
+ /** 验收审计 → patchwarden-audit */
24
+ readonly audit: {
25
+ readonly agent: "patchwarden-audit";
26
+ readonly keywords: readonly ["审计", "audit", "验收", "verify"];
27
+ };
28
+ /** 复杂推理和重构 → codex */
29
+ readonly refactor: {
30
+ readonly agent: "codex";
31
+ readonly keywords: readonly ["重构", "refactor", "跨模块", "redesign"];
32
+ };
33
+ /** 文档整理 → claude(fallback 到 opencode) */
34
+ readonly documentation: {
35
+ readonly agent: "claude";
36
+ readonly fallbackAgent: "opencode";
37
+ readonly keywords: readonly ["文档", "readme", "changelog", "doc"];
38
+ };
39
+ };
40
+ export declare function routeAgent(input: AgentRouteInput): AgentRouteResult;
@@ -0,0 +1,95 @@
1
+ import { PatchWardenError } from "../errors.js";
2
+ // ── Routing rules (roadmap 9.3) ───────────────────────────────────
3
+ export const AGENT_ROUTING_RULES = {
4
+ /** 大规模代码修改 → opencode */
5
+ largeScope: {
6
+ agent: "opencode",
7
+ threshold: 10, // scope.length > threshold
8
+ },
9
+ /** 单文件修改 → patchwarden-direct */
10
+ singleFile: {
11
+ agent: "patchwarden-direct",
12
+ },
13
+ /** 验收审计 → patchwarden-audit */
14
+ audit: {
15
+ agent: "patchwarden-audit",
16
+ keywords: ["审计", "audit", "验收", "verify"],
17
+ },
18
+ /** 复杂推理和重构 → codex */
19
+ refactor: {
20
+ agent: "codex",
21
+ keywords: ["重构", "refactor", "跨模块", "redesign"],
22
+ },
23
+ /** 文档整理 → claude(fallback 到 opencode) */
24
+ documentation: {
25
+ agent: "claude",
26
+ fallbackAgent: "opencode",
27
+ keywords: ["文档", "readme", "changelog", "doc"],
28
+ },
29
+ };
30
+ // ── Helpers ───────────────────────────────────────────────────────
31
+ function containsAny(text, keywords) {
32
+ return keywords.some((keyword) => text.includes(keyword));
33
+ }
34
+ function resolveRoute(preferred, reason, configuredAgents) {
35
+ if (configuredAgents.includes(preferred)) {
36
+ return { recommended_agent: preferred, reason, fallback: false };
37
+ }
38
+ const fallback = configuredAgents[0];
39
+ return {
40
+ recommended_agent: fallback,
41
+ reason: `fallback: ${preferred} not configured, using ${fallback}`,
42
+ fallback: true,
43
+ };
44
+ }
45
+ // ── Route agent ───────────────────────────────────────────────────
46
+ export function routeAgent(input) {
47
+ if (!input.configuredAgents || input.configuredAgents.length === 0) {
48
+ throw new PatchWardenError("no_agent_configured", "No agent is configured for routing.", "Configure at least one agent in patchwarden.config.json under the agents field.");
49
+ }
50
+ const scopeCount = input.scope?.length ?? 0;
51
+ const text = `${input.goal || ""} ${input.inline_plan || ""}`.toLowerCase();
52
+ // Rule 1: 大规模代码修改 → opencode (scope files > 10)
53
+ if (scopeCount > AGENT_ROUTING_RULES.largeScope.threshold) {
54
+ return resolveRoute(AGENT_ROUTING_RULES.largeScope.agent, `large scope (${scopeCount} files)`, input.configuredAgents);
55
+ }
56
+ // Rule 2: 单文件修改 → patchwarden-direct (scope files === 1)
57
+ if (scopeCount === 1) {
58
+ return resolveRoute(AGENT_ROUTING_RULES.singleFile.agent, "single file scope", input.configuredAgents);
59
+ }
60
+ // Rule 3: 验收审计 → patchwarden-audit
61
+ if (containsAny(text, AGENT_ROUTING_RULES.audit.keywords)) {
62
+ return resolveRoute(AGENT_ROUTING_RULES.audit.agent, "audit keywords", input.configuredAgents);
63
+ }
64
+ // Rule 4: 复杂推理和重构 → codex
65
+ if (containsAny(text, AGENT_ROUTING_RULES.refactor.keywords)) {
66
+ return resolveRoute(AGENT_ROUTING_RULES.refactor.agent, "refactor keywords", input.configuredAgents);
67
+ }
68
+ // Rule 5: 文档整理 → claude(fallback 到 opencode)
69
+ if (containsAny(text, AGENT_ROUTING_RULES.documentation.keywords)) {
70
+ const preferred = AGENT_ROUTING_RULES.documentation.agent;
71
+ const fallbackAgent = AGENT_ROUTING_RULES.documentation.fallbackAgent;
72
+ if (input.configuredAgents.includes(preferred)) {
73
+ return { recommended_agent: preferred, reason: "documentation keywords", fallback: false };
74
+ }
75
+ if (input.configuredAgents.includes(fallbackAgent)) {
76
+ return {
77
+ recommended_agent: fallbackAgent,
78
+ reason: `fallback: ${preferred} not configured, using ${fallbackAgent}`,
79
+ fallback: true,
80
+ };
81
+ }
82
+ const firstAgent = input.configuredAgents[0];
83
+ return {
84
+ recommended_agent: firstAgent,
85
+ reason: `fallback: ${preferred} not configured, using ${firstAgent}`,
86
+ fallback: true,
87
+ };
88
+ }
89
+ // Rule 6: 无匹配 → fallback 到 configuredAgents[0]
90
+ return {
91
+ recommended_agent: input.configuredAgents[0],
92
+ reason: `no specific routing rule matched, using default agent ${input.configuredAgents[0]}`,
93
+ fallback: true,
94
+ };
95
+ }
package/dist/config.d.ts CHANGED
@@ -15,7 +15,7 @@ export interface PatchWardenConfig {
15
15
  defaultTaskTimeoutSeconds: number;
16
16
  maxTaskTimeoutSeconds: number;
17
17
  watcherStaleSeconds: number;
18
- toolProfile?: "full" | "chatgpt_core" | "chatgpt_direct";
18
+ toolProfile?: "full" | "chatgpt_core" | "chatgpt_direct" | "chatgpt_search";
19
19
  repoAliases?: Record<string, string>;
20
20
  enableAgentAssessment?: boolean;
21
21
  agentAssessmentTimeoutSeconds?: number;
package/dist/config.js CHANGED
@@ -175,8 +175,12 @@ function normalizeConfig(config) {
175
175
  if (!Number.isInteger(config.watcherStaleSeconds) || config.watcherStaleSeconds < 5 || config.watcherStaleSeconds > 3600) {
176
176
  throw new Error("watcherStaleSeconds must be an integer from 5 to 3600");
177
177
  }
178
- if (config.toolProfile !== undefined && config.toolProfile !== "full" && config.toolProfile !== "chatgpt_core" && config.toolProfile !== "chatgpt_direct") {
179
- throw new Error('toolProfile must be "full", "chatgpt_core", or "chatgpt_direct"');
178
+ if (config.toolProfile !== undefined &&
179
+ config.toolProfile !== "full" &&
180
+ config.toolProfile !== "chatgpt_core" &&
181
+ config.toolProfile !== "chatgpt_direct" &&
182
+ config.toolProfile !== "chatgpt_search") {
183
+ throw new Error('toolProfile must be "full", "chatgpt_core", "chatgpt_direct", or "chatgpt_search"');
180
184
  }
181
185
  if (config.enableAgentAssessment !== undefined && typeof config.enableAgentAssessment !== "boolean") {
182
186
  throw new Error("enableAgentAssessment must be a boolean");
@@ -419,6 +419,7 @@ function resolveWorkspaceRootSafe() {
419
419
  }
420
420
  const TERMINAL_TASK_STATUSES = new Set([
421
421
  "done",
422
+ "done_by_agent",
422
423
  "failed",
423
424
  "failed_verification",
424
425
  "failed_scope_violation",
@@ -928,13 +929,19 @@ function handleReconcile(res, taskId) {
928
929
  const statusData = readJsonFileSafe(statusPath) ?? {};
929
930
  const runtimeData = readJsonFileSafe(runtimePath) ?? {};
930
931
  const watcher = readWatcherStatusSafe();
932
+ const VALID_ACCEPTANCE = ["pending", "accepted", "rejected", "needs_fix", "blocked"];
933
+ const taskStatus = String(statusData.status || "pending");
934
+ const taskAcceptanceStatus = taskStatus === "done_by_agent"
935
+ ? (typeof statusData.acceptance_status === "string" && VALID_ACCEPTANCE.includes(statusData.acceptance_status) ? statusData.acceptance_status : "pending")
936
+ : null;
931
937
  const taskEntry = {
932
938
  task_id: taskId,
933
939
  plan_id: String(statusData.plan_id || ""),
934
940
  title: "",
935
941
  agent: String(statusData.agent || ""),
936
- status: String(statusData.status || "pending"),
942
+ status: taskStatus,
937
943
  phase: String(runtimeData.phase || statusData.phase || "queued"),
944
+ acceptance_status: taskAcceptanceStatus,
938
945
  created_at: String(statusData.created_at || ""),
939
946
  updated_at: String(statusData.updated_at || ""),
940
947
  workspace_root: String(statusData.workspace_root || config.workspaceRoot),
@@ -1072,13 +1079,19 @@ function handleTaskDetail(res, taskId) {
1072
1079
  let stale = null;
1073
1080
  if (statusData) {
1074
1081
  const watcher = readWatcherStatusSafe();
1082
+ const VALID_ACCEPTANCE2 = ["pending", "accepted", "rejected", "needs_fix", "blocked"];
1083
+ const taskStatus2 = String(statusData.status || "pending");
1084
+ const taskAcceptanceStatus2 = taskStatus2 === "done_by_agent"
1085
+ ? (typeof statusData.acceptance_status === "string" && VALID_ACCEPTANCE2.includes(statusData.acceptance_status) ? statusData.acceptance_status : "pending")
1086
+ : null;
1075
1087
  const entry = {
1076
1088
  task_id: taskId,
1077
1089
  plan_id: String(statusData.plan_id || ""),
1078
1090
  title: "",
1079
1091
  agent: String(statusData.agent || ""),
1080
- status: String(statusData.status || "pending"),
1092
+ status: taskStatus2,
1081
1093
  phase: String(runtimeData?.phase || statusData.phase || "queued"),
1094
+ acceptance_status: taskAcceptanceStatus2,
1082
1095
  created_at: String(statusData.created_at || ""),
1083
1096
  updated_at: String(statusData.updated_at || ""),
1084
1097
  workspace_root: String(statusData.workspace_root || config.workspaceRoot),
package/dist/doctor.js CHANGED
@@ -18,6 +18,9 @@ import { guardPlanContent } from "./security/planGuard.js";
18
18
  import { TASK_READ_ONLY_FILES } from "./tools/getTaskFile.js";
19
19
  import { getToolDefs } from "./tools/registry.js";
20
20
  import { CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, selectToolsForProfile } from "./tools/toolCatalog.js";
21
+ import { buildToolRegistry } from "./tools/toolRegistry.js";
22
+ import { runAllSchemaDriftChecks } from "./tools/schemaDriftCheck.js";
23
+ import { runReleaseGateCheck } from "./release/releaseGate.js";
21
24
  import { PATCHWARDEN_VERSION } from "./version.js";
22
25
  // ── State ──────────────────────────────────────────────────────────
23
26
  let ok = 0;
@@ -218,7 +221,7 @@ async function main() {
218
221
  const coreTools = selectToolsForProfile(fullTools, "chatgpt_core", config?.enableDirectProfile);
219
222
  const createSchema = coreTools.find((tool) => tool.name === "create_task")?.inputSchema;
220
223
  const waitSchema = coreTools.find((tool) => tool.name === "wait_for_task")?.inputSchema;
221
- check("Full tool profile exposes 30 tools", fullTools.length === 30, `${fullTools.length} tools`);
224
+ check("Full tool profile exposes 54 tools", fullTools.length === 54, `${fullTools.length} tools`);
222
225
  check(`chatgpt_core profile exposes the exact ${CHATGPT_CORE_TOOL_NAMES.length}-tool manifest`, JSON.stringify(coreTools.map((tool) => tool.name)) === JSON.stringify(CHATGPT_CORE_TOOL_NAMES), coreTools.map((tool) => tool.name).join(", "));
223
226
  check("Core task schemas expose inline_plan, verify_commands, and wait aliases", Boolean(createSchema?.properties?.inline_plan &&
224
227
  createSchema?.properties?.verify_commands &&
@@ -235,6 +238,25 @@ async function main() {
235
238
  results.push(`[OK] chatgpt_direct enabled check skipped (enableDirectProfile=false)`);
236
239
  ok++;
237
240
  }
241
+ // 16. Schema drift 检查(v0.9.0)—— warn 级别,不阻断 doctor:ci
242
+ // 用 fullTools 同时构建 registry 和 toolDefs Map,确保 schema digest 比对基准一致。
243
+ const driftRegistry = buildToolRegistry(fullTools);
244
+ const driftToolDefs = new Map();
245
+ for (const tool of fullTools) {
246
+ driftToolDefs.set(tool.name, { inputSchema: tool.inputSchema });
247
+ }
248
+ const driftResult = runAllSchemaDriftChecks(driftRegistry, driftToolDefs);
249
+ if (driftResult.ok) {
250
+ results.push(`[OK] Schema drift check — no drift detected`);
251
+ ok++;
252
+ }
253
+ else {
254
+ // 每个 warning 单独输出为 WARN,与其他自检项的多警告模式一致
255
+ for (const w of driftResult.warnings) {
256
+ results.push(`[WARN] Schema drift: ${w}`);
257
+ warn++;
258
+ }
259
+ }
238
260
  }
239
261
  finally {
240
262
  if (previousProfile === undefined)
@@ -242,6 +264,15 @@ async function main() {
242
264
  else
243
265
  process.env.PATCHWARDEN_TOOL_PROFILE = previousProfile;
244
266
  }
267
+ // 17. Release gate module loadable (v1.0.0) — module integrity only.
268
+ // Does NOT execute local_ready (would recurse into doctor:ci).
269
+ try {
270
+ const releaseGateReady = typeof runReleaseGateCheck === "function";
271
+ check("Release gate module loadable", releaseGateReady, releaseGateReady ? "runReleaseGateCheck exported" : "runReleaseGateCheck missing or invalid");
272
+ }
273
+ catch (error) {
274
+ check("Release gate module loadable", false, error instanceof Error ? error.message : String(error));
275
+ }
245
276
  // 9. HTTP port check
246
277
  const httpPort = config?.http?.port || 7331;
247
278
  try {
@@ -0,0 +1,62 @@
1
+ /**
2
+ * v0.7.2: 验收引擎 — 基于证据推导 4 级验收结论。
3
+ *
4
+ * 输入:status.json、task_meta(goal/scope/forbidden/verification/done_evidence)、
5
+ * audit checks(pass/warn/fail)、release claims、artifact 信息
6
+ * 输出:ACCEPTED / NEEDS_FIX / REJECTED / BLOCKED_BY_APPROVAL
7
+ * + reasons + required_evidence + next_suggested_task
8
+ *
9
+ * 决策规则:
10
+ * 1. 存在 fail 级检查项 → REJECTED
11
+ * 2. 存在 release claims 但无法验证远端 → BLOCKED_BY_APPROVAL
12
+ * 3. 存在 warn 级检查项或文档不同步 → NEEDS_FIX
13
+ * 4. 所有检查通过 → ACCEPTED
14
+ */
15
+ import type { AcceptanceStatus } from "../tools/createTask.js";
16
+ export type AcceptanceVerdict = "ACCEPTED" | "NEEDS_FIX" | "REJECTED" | "BLOCKED_BY_APPROVAL";
17
+ export interface AcceptanceCheck {
18
+ name: string;
19
+ result: "pass" | "warn" | "fail";
20
+ detail: string;
21
+ }
22
+ export interface AcceptanceEvidence {
23
+ task_id: string;
24
+ task_status: string;
25
+ result_md_exists: boolean;
26
+ result_json_exists: boolean;
27
+ verify_json_exists: boolean;
28
+ test_log_exists: boolean;
29
+ git_diff_exists: boolean;
30
+ verify_status: "passed" | "failed" | "skipped" | null;
31
+ new_out_of_scope_changes: Array<{
32
+ path: string;
33
+ change: string;
34
+ }>;
35
+ goal: string | null;
36
+ scope: string[] | null;
37
+ forbidden: string[] | null;
38
+ verification: string[] | null;
39
+ done_evidence: string[] | null;
40
+ artifact_status: string | null;
41
+ release_claims_unverified: boolean;
42
+ checks: AcceptanceCheck[];
43
+ }
44
+ export interface AcceptanceResult {
45
+ verdict: AcceptanceVerdict;
46
+ acceptance_status: AcceptanceStatus;
47
+ reason: string;
48
+ reasons: string[];
49
+ required_evidence: string[];
50
+ next_suggested_task: string;
51
+ fail_checks: AcceptanceCheck[];
52
+ warn_checks: AcceptanceCheck[];
53
+ }
54
+ /**
55
+ * 核心验收决策函数。
56
+ * 输入所有证据,输出 4 级验收结论。
57
+ */
58
+ export declare function evaluateAcceptance(evidence: AcceptanceEvidence): AcceptanceResult;
59
+ /**
60
+ * 将 AcceptanceVerdict 映射到 AcceptanceStatus(用于回写 status.json)。
61
+ */
62
+ export declare function verdictToStatus(verdict: AcceptanceVerdict): AcceptanceStatus;