patchwarden 1.1.0 → 1.5.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 (258) hide show
  1. package/README.en.md +83 -8
  2. package/README.md +78 -10
  3. package/dist/assessments/agentAssessor.d.ts +1 -1
  4. package/dist/assessments/agentAssessor.js +3 -3
  5. package/dist/assessments/assessmentStore.d.ts +1 -1
  6. package/dist/assessments/assessmentStore.js +2 -2
  7. package/dist/assessments/confirmCli.js +5 -4
  8. package/dist/config.d.ts +7 -0
  9. package/dist/config.js +34 -0
  10. package/dist/control/middleware/auth.d.ts +10 -0
  11. package/dist/control/middleware/auth.js +8 -0
  12. package/dist/control/middleware/static.d.ts +3 -0
  13. package/dist/control/middleware/static.js +65 -0
  14. package/dist/control/routes/audit.d.ts +15 -0
  15. package/dist/control/routes/audit.js +277 -0
  16. package/dist/control/routes/evidence.d.ts +4 -0
  17. package/dist/control/routes/evidence.js +96 -0
  18. package/dist/control/routes/lineage.d.ts +3 -0
  19. package/dist/control/routes/lineage.js +71 -0
  20. package/dist/control/routes/policy.d.ts +3 -0
  21. package/dist/control/routes/policy.js +81 -0
  22. package/dist/control/routes/process.d.ts +5 -0
  23. package/dist/control/routes/process.js +200 -0
  24. package/dist/control/routes/sessions.d.ts +22 -0
  25. package/dist/control/routes/sessions.js +224 -0
  26. package/dist/control/routes/status.d.ts +6 -0
  27. package/dist/control/routes/status.js +250 -0
  28. package/dist/control/routes/taskActions.d.ts +21 -0
  29. package/dist/control/routes/taskActions.js +233 -0
  30. package/dist/control/routes/tasks.d.ts +20 -0
  31. package/dist/control/routes/tasks.js +310 -0
  32. package/dist/control/routes/workspace.d.ts +15 -0
  33. package/dist/control/routes/workspace.js +193 -0
  34. package/dist/control/runtime.d.ts +91 -0
  35. package/dist/control/runtime.js +392 -0
  36. package/dist/control/server.d.ts +13 -0
  37. package/dist/control/server.js +479 -0
  38. package/dist/control/shared.d.ts +35 -0
  39. package/dist/control/shared.js +288 -0
  40. package/dist/controlCenter.d.ts +6 -0
  41. package/dist/controlCenter.js +7 -2001
  42. package/dist/direct/directGuards.js +30 -8
  43. package/dist/direct/directSessionStore.d.ts +2 -0
  44. package/dist/direct/directVerification.js +7 -0
  45. package/dist/doctor.d.ts +18 -1
  46. package/dist/doctor.js +579 -348
  47. package/dist/goal/goalReport.d.ts +54 -0
  48. package/dist/goal/goalReport.js +204 -0
  49. package/dist/goal/goalStatus.d.ts +6 -0
  50. package/dist/goal/specKitImport.d.ts +63 -0
  51. package/dist/goal/specKitImport.js +220 -0
  52. package/dist/goal/subgoalSync.js +2 -1
  53. package/dist/httpServer.js +15 -12
  54. package/dist/index.js +7 -4
  55. package/dist/logging.d.ts +7 -1
  56. package/dist/logging.js +8 -0
  57. package/dist/policy/projectPolicy.d.ts +55 -0
  58. package/dist/policy/projectPolicy.js +286 -0
  59. package/dist/runner/changeCapture.d.ts +3 -3
  60. package/dist/runner/changeCapture.js +63 -39
  61. package/dist/runner/cli.js +7 -6
  62. package/dist/runner/postTaskCleanup.js +26 -2
  63. package/dist/runner/runTask.js +245 -221
  64. package/dist/runner/simpleProcess.js +4 -4
  65. package/dist/runner/watch.js +17 -14
  66. package/dist/security/contentRedaction.d.ts +6 -0
  67. package/dist/security/contentRedaction.js +22 -0
  68. package/dist/smoke-test.js +261 -255
  69. package/dist/test/unit/apply-patch.test.d.ts +1 -0
  70. package/dist/test/unit/apply-patch.test.js +225 -0
  71. package/dist/test/unit/create-task.test.d.ts +1 -0
  72. package/dist/test/unit/create-task.test.js +197 -0
  73. package/dist/test/unit/direct-guards.test.js +124 -9
  74. package/dist/test/unit/evidence-pack.test.d.ts +1 -0
  75. package/dist/test/unit/evidence-pack.test.js +224 -0
  76. package/dist/test/unit/get-task-status.test.d.ts +1 -0
  77. package/dist/test/unit/get-task-status.test.js +174 -0
  78. package/dist/test/unit/get-task-summary.test.d.ts +1 -0
  79. package/dist/test/unit/get-task-summary.test.js +146 -0
  80. package/dist/test/unit/goal-report.test.d.ts +1 -0
  81. package/dist/test/unit/goal-report.test.js +159 -0
  82. package/dist/test/unit/goal-subgoal-task.test.js +6 -6
  83. package/dist/test/unit/goal-tools-registry.test.js +6 -4
  84. package/dist/test/unit/path-guard.test.js +24 -0
  85. package/dist/test/unit/project-policy-release-mode.test.d.ts +1 -0
  86. package/dist/test/unit/project-policy-release-mode.test.js +125 -0
  87. package/dist/test/unit/run-task-loop.test.d.ts +1 -0
  88. package/dist/test/unit/run-task-loop.test.js +380 -0
  89. package/dist/test/unit/schema-drift-check.test.js +10 -9
  90. package/dist/test/unit/spec-kit-import.test.d.ts +1 -0
  91. package/dist/test/unit/spec-kit-import.test.js +341 -0
  92. package/dist/test/unit/wait-for-task.test.d.ts +1 -0
  93. package/dist/test/unit/wait-for-task.test.js +144 -0
  94. package/dist/tools/auditTask.d.ts +8 -63
  95. package/dist/tools/auditTask.js +12 -8
  96. package/dist/tools/createDirectSession.d.ts +1 -1
  97. package/dist/tools/createDirectSession.js +2 -2
  98. package/dist/tools/createTask.d.ts +2 -2
  99. package/dist/tools/createTask.js +4 -4
  100. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  101. package/dist/tools/dispatch/coreDispatch.js +282 -0
  102. package/dist/tools/dispatch/diagnosticDispatch.d.ts +14 -0
  103. package/dist/tools/dispatch/diagnosticDispatch.js +78 -0
  104. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  105. package/dist/tools/dispatch/directDispatch.js +115 -0
  106. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  107. package/dist/tools/dispatch/goalDispatch.js +91 -0
  108. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  109. package/dist/tools/dispatch/releaseDispatch.js +45 -0
  110. package/dist/tools/dispatch/types.d.ts +23 -0
  111. package/dist/tools/dispatch/types.js +15 -0
  112. package/dist/tools/evidencePack.d.ts +45 -0
  113. package/dist/tools/evidencePack.js +375 -0
  114. package/dist/tools/finalizeDirectSession.d.ts +1 -1
  115. package/dist/tools/finalizeDirectSession.js +3 -3
  116. package/dist/tools/goalSubgoalTask.d.ts +1 -1
  117. package/dist/tools/goalSubgoalTask.js +2 -2
  118. package/dist/tools/healthCheck.js +3 -3
  119. package/dist/tools/recommendAgentForTask.d.ts +19 -0
  120. package/dist/tools/recommendAgentForTask.js +56 -0
  121. package/dist/tools/registry.d.ts +3 -3
  122. package/dist/tools/registry.js +333 -402
  123. package/dist/tools/releaseMode.d.ts +50 -0
  124. package/dist/tools/releaseMode.js +370 -0
  125. package/dist/tools/retryTask.d.ts +2 -2
  126. package/dist/tools/retryTask.js +2 -2
  127. package/dist/tools/runDirectVerificationBundle.d.ts +26 -0
  128. package/dist/tools/runDirectVerificationBundle.js +64 -0
  129. package/dist/tools/runTaskLoop.d.ts +57 -0
  130. package/dist/tools/runTaskLoop.js +417 -0
  131. package/dist/tools/runVerification.d.ts +4 -0
  132. package/dist/tools/runVerification.js +4 -0
  133. package/dist/tools/safeViews.d.ts +8 -2
  134. package/dist/tools/safeViews.js +4 -2
  135. package/dist/tools/taskLineage.d.ts +91 -0
  136. package/dist/tools/taskLineage.js +175 -0
  137. package/dist/tools/toolCatalog.d.ts +2 -2
  138. package/dist/tools/toolCatalog.js +6 -0
  139. package/dist/tools/toolRegistry.js +132 -0
  140. package/dist/version.d.ts +2 -2
  141. package/dist/version.js +2 -2
  142. package/docs/CODE_WIKI.md +983 -0
  143. package/docs/agentseal-integration.md +150 -0
  144. package/docs/architecture.md +63 -0
  145. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  146. package/docs/chatgpt-usage.md +31 -0
  147. package/docs/control-center/README.md +9 -0
  148. package/docs/dashboard-overview.md +86 -0
  149. package/docs/demo.md +8 -0
  150. package/docs/direct-session-workflow.md +98 -0
  151. package/docs/evidence-pack-schema.md +215 -0
  152. package/docs/execution-plan-2026-07-09.md +315 -0
  153. package/docs/lineage-evidence-pack-workflow.md +127 -0
  154. package/docs/mcp-inspector-testing.md +200 -0
  155. package/docs/open-source-application.md +162 -0
  156. package/docs/opencode-worker.md +151 -0
  157. package/docs/openhands-worker.md +181 -0
  158. package/docs/release-evidence.md +72 -0
  159. package/docs/roadmap-execution-and-acceptance.md +365 -0
  160. package/docs/spec-kit-integration.md +131 -0
  161. package/docs/task-safe-review-workflow.md +98 -0
  162. package/docs/threat-model.md +79 -0
  163. package/docs/user-feedback.md +40 -0
  164. package/docs/why-patchwarden.md +110 -0
  165. package/package.json +3 -3
  166. package/scripts/checks/control-center-smoke.js +443 -0
  167. package/scripts/checks/control-smoke.js +2 -2
  168. package/scripts/checks/lifecycle-smoke.js +29 -23
  169. package/scripts/checks/mcp-manifest-check.js +12 -0
  170. package/scripts/checks/mcp-smoke.js +33 -7
  171. package/scripts/checks/watcher-supervisor-smoke.js +1 -1
  172. package/scripts/generate-demo-gif.py +320 -0
  173. package/src/assessments/agentAssessor.ts +3 -3
  174. package/src/assessments/assessmentStore.ts +2 -2
  175. package/src/assessments/confirmCli.ts +5 -4
  176. package/src/config.ts +37 -0
  177. package/src/control/middleware/auth.ts +17 -0
  178. package/src/control/middleware/static.ts +71 -0
  179. package/src/control/routes/audit.ts +321 -0
  180. package/src/control/routes/evidence.ts +107 -0
  181. package/src/control/routes/lineage.ts +92 -0
  182. package/src/control/routes/policy.ts +81 -0
  183. package/src/control/routes/process.ts +204 -0
  184. package/src/control/routes/sessions.ts +251 -0
  185. package/src/control/routes/status.ts +325 -0
  186. package/src/control/routes/taskActions.ts +248 -0
  187. package/src/control/routes/tasks.ts +323 -0
  188. package/src/control/routes/workspace.ts +203 -0
  189. package/src/control/runtime.ts +472 -0
  190. package/src/control/server.ts +471 -0
  191. package/src/control/shared.ts +294 -0
  192. package/src/controlCenter.ts +7 -2150
  193. package/src/direct/directGuards.ts +28 -7
  194. package/src/direct/directSessionStore.ts +2 -0
  195. package/src/direct/directVerification.ts +7 -0
  196. package/src/doctor.ts +741 -481
  197. package/src/goal/goalReport.ts +271 -0
  198. package/src/goal/goalStatus.ts +6 -0
  199. package/src/goal/specKitImport.ts +355 -0
  200. package/src/goal/subgoalSync.ts +4 -2
  201. package/src/httpServer.ts +17 -14
  202. package/src/index.ts +7 -4
  203. package/src/logging.ts +10 -1
  204. package/src/policy/projectPolicy.ts +344 -0
  205. package/src/runner/changeCapture.ts +70 -42
  206. package/src/runner/cli.ts +7 -6
  207. package/src/runner/postTaskCleanup.ts +26 -2
  208. package/src/runner/runTask.ts +325 -223
  209. package/src/runner/simpleProcess.ts +4 -4
  210. package/src/runner/watch.ts +17 -14
  211. package/src/security/contentRedaction.ts +29 -0
  212. package/src/smoke-test.ts +254 -252
  213. package/src/test/unit/apply-patch.test.ts +293 -0
  214. package/src/test/unit/create-task.test.ts +255 -0
  215. package/src/test/unit/direct-guards.test.ts +178 -8
  216. package/src/test/unit/evidence-pack.test.ts +251 -0
  217. package/src/test/unit/get-task-status.test.ts +203 -0
  218. package/src/test/unit/get-task-summary.test.ts +173 -0
  219. package/src/test/unit/goal-report.test.ts +189 -0
  220. package/src/test/unit/goal-subgoal-task.test.ts +6 -6
  221. package/src/test/unit/goal-tools-registry.test.ts +7 -5
  222. package/src/test/unit/path-guard.test.ts +30 -0
  223. package/src/test/unit/project-policy-release-mode.test.ts +156 -0
  224. package/src/test/unit/run-task-loop.test.ts +425 -0
  225. package/src/test/unit/schema-drift-check.test.ts +11 -9
  226. package/src/test/unit/spec-kit-import.test.ts +429 -0
  227. package/src/test/unit/wait-for-task.test.ts +176 -0
  228. package/src/tools/auditTask.ts +99 -59
  229. package/src/tools/createDirectSession.ts +3 -3
  230. package/src/tools/createTask.ts +7 -7
  231. package/src/tools/dispatch/coreDispatch.ts +374 -0
  232. package/src/tools/dispatch/diagnosticDispatch.ts +101 -0
  233. package/src/tools/dispatch/directDispatch.ts +167 -0
  234. package/src/tools/dispatch/goalDispatch.ts +127 -0
  235. package/src/tools/dispatch/releaseDispatch.ts +65 -0
  236. package/src/tools/dispatch/types.ts +24 -0
  237. package/src/tools/evidencePack.ts +490 -0
  238. package/src/tools/finalizeDirectSession.ts +4 -4
  239. package/src/tools/goalSubgoalTask.ts +2 -2
  240. package/src/tools/healthCheck.ts +3 -3
  241. package/src/tools/listWorkspace.ts +71 -71
  242. package/src/tools/recommendAgentForTask.ts +79 -0
  243. package/src/tools/registry.ts +352 -509
  244. package/src/tools/releaseMode.ts +450 -0
  245. package/src/tools/retryTask.ts +2 -2
  246. package/src/tools/runDirectVerificationBundle.ts +98 -0
  247. package/src/tools/runTaskLoop.ts +526 -0
  248. package/src/tools/runVerification.ts +8 -0
  249. package/src/tools/safeViews.ts +4 -2
  250. package/src/tools/taskLineage.ts +300 -0
  251. package/src/tools/toolCatalog.ts +6 -0
  252. package/src/tools/toolRegistry.ts +132 -0
  253. package/src/version.ts +2 -2
  254. package/ui/pages/audit.html +192 -3
  255. package/ui/pages/dashboard.html +1070 -14
  256. package/ui/pages/direct-sessions.html +505 -53
  257. package/ui/pages/task-detail.html +456 -438
  258. package/ui/pages/tasks.html +598 -61
@@ -0,0 +1,374 @@
1
+ /**
2
+ * Dispatch handlers for core task-management tools.
3
+ *
4
+ * Each handler directly calls the existing tool function — no logic is
5
+ * rewritten. Extracted from the original handleToolCallInternal switch.
6
+ */
7
+
8
+ import { savePlan } from "../savePlan.js";
9
+ import { getPlan } from "../getPlan.js";
10
+ import { createTask } from "../createTask.js";
11
+ import { getTaskStatus } from "../getTaskStatus.js";
12
+ import { getResult, getResultJson, getDiff, getTestLog, getTaskLogTail } from "../taskOutputs.js";
13
+ import { listWorkspace } from "../listWorkspace.js";
14
+ import { readWorkspaceFile } from "../readWorkspaceFile.js";
15
+ import { listTasks } from "../listTasks.js";
16
+ import { cancelTask } from "../cancelTask.js";
17
+ import { killTask } from "../killTask.js";
18
+ import { retryTask } from "../retryTask.js";
19
+ import { getTaskStdoutTail } from "../getTaskStdoutTail.js";
20
+ import { getTaskProgress } from "../getTaskProgress.js";
21
+ import { listAgents } from "../listAgents.js";
22
+ import { healthCheck } from "../healthCheck.js";
23
+ import { getTaskSummary } from "../getTaskSummary.js";
24
+ import { waitForTask } from "../waitForTask.js";
25
+ import { runTaskLoop } from "../runTaskLoop.js";
26
+ import { getTaskLineage } from "../taskLineage.js";
27
+ import { exportTaskEvidencePack } from "../evidencePack.js";
28
+ import { recommendAgentForTask } from "../recommendAgentForTask.js";
29
+ import { auditTask } from "../auditTask.js";
30
+ import { safeStatus } from "../safeStatus.js";
31
+ import { safeAudit, safeDiffSummary, safeResult, safeTestSummary } from "../safeViews.js";
32
+ import { diagnoseTask } from "../diagnoseTask.js";
33
+ import { reconcileTasks } from "../reconcileTasks.js";
34
+ import { checkReleaseGate } from "../checkReleaseGate.js";
35
+ import { getProjectPolicyTool } from "../releaseMode.js";
36
+ import { runTask } from "../../runner/runTask.js";
37
+ import { getConfig } from "../../config.js";
38
+ import { getToolCatalogSnapshot } from "../registry.js";
39
+ import type { ToolHandlerMap } from "./types.js";
40
+ import { toResult } from "./types.js";
41
+
42
+ // ── Local helpers (moved verbatim from registry.ts) ───────────────
43
+
44
+ function normalizeWaitSeconds(args: Record<string, unknown> | undefined): number | undefined {
45
+ const legacy = args?.wait_seconds;
46
+ const preferred = args?.timeout_seconds;
47
+ if (legacy !== undefined && preferred !== undefined && Number(legacy) !== Number(preferred)) {
48
+ throw new Error("wait_seconds and timeout_seconds must match when both are supplied.");
49
+ }
50
+ const value = preferred ?? legacy;
51
+ return value === undefined ? undefined : Number(value);
52
+ }
53
+
54
+ function normalizeSummaryView(value: unknown): "compact" | "standard" {
55
+ if (value === undefined) return "standard";
56
+ if (value !== "compact" && value !== "standard") {
57
+ throw new Error('view must be "compact" or "standard".');
58
+ }
59
+ return value;
60
+ }
61
+
62
+ // ── Handler map ───────────────────────────────────────────────────
63
+
64
+ export const coreHandlers: ToolHandlerMap = {
65
+ save_plan: async (args) => {
66
+ return toResult(
67
+ savePlan({
68
+ title: String(args?.title ?? ""),
69
+ content: args?.content !== undefined ? String(args.content) : "",
70
+ plan_ref: args?.plan_ref ? String(args.plan_ref) : undefined,
71
+ }),
72
+ );
73
+ },
74
+
75
+ get_plan: async (args) => {
76
+ return toResult(getPlan({ plan_id: String(args?.plan_id ?? "") }));
77
+ },
78
+
79
+ create_task: async (args) => {
80
+ return toResult(
81
+ await createTask({
82
+ plan_id: args?.plan_id ? String(args.plan_id) : undefined,
83
+ inline_plan: args?.inline_plan ? String(args.inline_plan) : undefined,
84
+ plan_title: args?.plan_title ? String(args.plan_title) : undefined,
85
+ template: args?.template ? (String(args.template) as any) : undefined,
86
+ goal: args?.goal ? String(args.goal) : undefined,
87
+ source_task_id: args?.source_task_id ? String(args.source_task_id) : undefined,
88
+ agent: String(args?.agent ?? ""),
89
+ repo_path: args?.repo_path ? String(args.repo_path) : undefined,
90
+ test_command: args?.test_command ? String(args.test_command) : undefined,
91
+ verify_commands: Array.isArray(args?.verify_commands)
92
+ ? args.verify_commands.map((command) => String(command))
93
+ : undefined,
94
+ timeout_seconds:
95
+ args?.timeout_seconds !== undefined ? Number(args.timeout_seconds) : undefined,
96
+ execution_mode: args?.execution_mode === "assess_only" ? "assess_only" : "execute",
97
+ assessment_id: args?.assessment_id ? String(args.assessment_id) : undefined,
98
+ }),
99
+ );
100
+ },
101
+
102
+ run_task_loop: async (args) => {
103
+ return toResult(
104
+ await runTaskLoop({
105
+ repo_path: String(args?.repo_path ?? ""),
106
+ goal: String(args?.goal ?? ""),
107
+ verify_commands: Array.isArray(args?.verify_commands)
108
+ ? args.verify_commands.map((command) => String(command))
109
+ : [],
110
+ agent: args?.agent ? String(args.agent) : undefined,
111
+ template:
112
+ args?.template === "inspect_only" || args?.template === "release_check"
113
+ ? args.template
114
+ : "feature_small",
115
+ max_iterations: args?.max_iterations !== undefined ? Number(args.max_iterations) : undefined,
116
+ task_timeout_seconds:
117
+ args?.task_timeout_seconds !== undefined ? Number(args.task_timeout_seconds) : undefined,
118
+ auto_fix_tests: args?.auto_fix_tests !== undefined ? Boolean(args.auto_fix_tests) : undefined,
119
+ auto_cleanup_artifacts:
120
+ args?.auto_cleanup_artifacts !== undefined
121
+ ? Boolean(args.auto_cleanup_artifacts)
122
+ : undefined,
123
+ stop_on_high_risk:
124
+ args?.stop_on_high_risk !== undefined ? Boolean(args.stop_on_high_risk) : undefined,
125
+ direct_verify: args?.direct_verify !== undefined ? Boolean(args.direct_verify) : undefined,
126
+ direct_verify_commands: Array.isArray(args?.direct_verify_commands)
127
+ ? args.direct_verify_commands.map((command) => String(command))
128
+ : undefined,
129
+ direct_verify_timeout_seconds:
130
+ args?.direct_verify_timeout_seconds !== undefined
131
+ ? Number(args.direct_verify_timeout_seconds)
132
+ : undefined,
133
+ scope_files: Array.isArray(args?.scope_files)
134
+ ? args.scope_files.map((entry) => String(entry))
135
+ : undefined,
136
+ isolation_mode: args?.isolation_mode === "worktree" ? "worktree" : "current_repo",
137
+ worktree_base_branch: args?.worktree_base_branch
138
+ ? String(args.worktree_base_branch)
139
+ : undefined,
140
+ worktree_cleanup:
141
+ args?.worktree_cleanup === "archive" || args?.worktree_cleanup === "delete_ignored_only"
142
+ ? args.worktree_cleanup
143
+ : "keep",
144
+ }),
145
+ );
146
+ },
147
+
148
+ recommend_agent_for_task: async (args) => {
149
+ return toResult(
150
+ recommendAgentForTask({
151
+ repo_path: String(args?.repo_path ?? ""),
152
+ goal: String(args?.goal ?? ""),
153
+ scope_files: Array.isArray(args?.scope_files)
154
+ ? args.scope_files.map((entry) => String(entry))
155
+ : undefined,
156
+ template: args?.template ? String(args.template) : undefined,
157
+ risk_hint: args?.risk_hint ? String(args.risk_hint) : undefined,
158
+ }),
159
+ );
160
+ },
161
+
162
+ get_task_lineage: async (args) => {
163
+ return toResult(
164
+ getTaskLineage(String(args?.lineage_id ?? ""), {
165
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
166
+ }),
167
+ );
168
+ },
169
+
170
+ export_task_evidence_pack: async (args) => {
171
+ return toResult(
172
+ exportTaskEvidencePack({
173
+ lineage_id: String(args?.lineage_id ?? ""),
174
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
175
+ }),
176
+ );
177
+ },
178
+
179
+ get_project_policy: async (args) => {
180
+ return toResult(getProjectPolicyTool(String(args?.repo_path ?? "")));
181
+ },
182
+
183
+ get_task_status: async (args) => {
184
+ return toResult(getTaskStatus(String(args?.task_id ?? "")));
185
+ },
186
+
187
+ get_result: async (args) => {
188
+ return toResult(getResult(String(args?.task_id ?? "")));
189
+ },
190
+
191
+ get_result_json: async (args) => {
192
+ return toResult(getResultJson(String(args?.task_id ?? "")));
193
+ },
194
+
195
+ get_diff: async (args) => {
196
+ return toResult(getDiff(String(args?.task_id ?? "")));
197
+ },
198
+
199
+ get_test_log: async (args) => {
200
+ return toResult(getTestLog(String(args?.task_id ?? "")));
201
+ },
202
+
203
+ list_workspace: async (args) => {
204
+ return toResult(listWorkspace(args?.path ? String(args.path) : undefined));
205
+ },
206
+
207
+ read_workspace_file: async (args) => {
208
+ const sessionId = args?.session_id ? String(args.session_id) : undefined;
209
+ return toResult(
210
+ readWorkspaceFile({
211
+ path: String(args?.path ?? ""),
212
+ session_id: sessionId,
213
+ }),
214
+ );
215
+ },
216
+
217
+ list_tasks: async (args) => {
218
+ return toResult(
219
+ listTasks({
220
+ status: args?.status ? String(args.status) : undefined,
221
+ repo_path: args?.repo_path ? String(args.repo_path) : undefined,
222
+ active_only: args?.active_only !== undefined ? Boolean(args.active_only) : undefined,
223
+ limit: args?.limit ? Number(args.limit) : undefined,
224
+ }),
225
+ );
226
+ },
227
+
228
+ list_agents: async () => {
229
+ return toResult(listAgents());
230
+ },
231
+
232
+ health_check: async (args) => {
233
+ return toResult(
234
+ healthCheck(getToolCatalogSnapshot(), {
235
+ detail: args?.detail === "self_diagnostic" ? "self_diagnostic" : "standard",
236
+ }),
237
+ );
238
+ },
239
+
240
+ cancel_task: async (args) => {
241
+ return toResult(cancelTask(String(args?.task_id ?? "")));
242
+ },
243
+
244
+ kill_task: async (args) => {
245
+ return toResult(killTask(String(args?.task_id ?? "")));
246
+ },
247
+
248
+ retry_task: async (args) => {
249
+ return toResult(await retryTask(String(args?.task_id ?? "")));
250
+ },
251
+
252
+ get_task_stdout_tail: async (args) => {
253
+ return toResult(
254
+ getTaskStdoutTail(String(args?.task_id ?? ""), args?.lines ? Number(args.lines) : undefined),
255
+ );
256
+ },
257
+
258
+ get_task_log_tail: async (args) => {
259
+ return toResult(
260
+ getTaskLogTail(
261
+ String(args?.task_id ?? ""),
262
+ (args?.file as "stdout" | "stderr" | "test" | "verify") || "stdout",
263
+ {
264
+ lines: args?.lines ? Number(args.lines) : undefined,
265
+ redact: args?.redact !== undefined ? Boolean(args.redact) : undefined,
266
+ },
267
+ ),
268
+ );
269
+ },
270
+
271
+ get_task_progress: async (args) => {
272
+ return toResult(getTaskProgress(String(args?.task_id ?? "")));
273
+ },
274
+
275
+ wait_for_task: async (args) => {
276
+ const waitSeconds = normalizeWaitSeconds(args);
277
+ return toResult(await waitForTask(String(args?.task_id ?? ""), waitSeconds));
278
+ },
279
+
280
+ get_task_summary: async (args) => {
281
+ return toResult(
282
+ getTaskSummary(String(args?.task_id ?? ""), {
283
+ view: normalizeSummaryView(args?.view),
284
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
285
+ }),
286
+ );
287
+ },
288
+
289
+ audit_task: async (args) => {
290
+ return toResult(auditTask(String(args?.task_id ?? "")));
291
+ },
292
+
293
+ safe_status: async (args) => {
294
+ return toResult(safeStatus(String(args?.task_id ?? "")));
295
+ },
296
+
297
+ safe_result: async (args) => {
298
+ return toResult(
299
+ safeResult(String(args?.task_id ?? ""), {
300
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
301
+ }),
302
+ );
303
+ },
304
+
305
+ safe_audit: async (args) => {
306
+ return toResult(
307
+ safeAudit(String(args?.task_id ?? ""), {
308
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
309
+ }),
310
+ );
311
+ },
312
+
313
+ safe_test_summary: async (args) => {
314
+ return toResult(safeTestSummary(String(args?.task_id ?? "")));
315
+ },
316
+
317
+ safe_diff_summary: async (args) => {
318
+ return toResult(
319
+ safeDiffSummary(String(args?.task_id ?? ""), {
320
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
321
+ }),
322
+ );
323
+ },
324
+
325
+ diagnose_task: async (args) => {
326
+ return toResult(
327
+ diagnoseTask({
328
+ task_id: String(args?.task_id ?? ""),
329
+ include_logs: args?.include_logs !== undefined ? Boolean(args.include_logs) : undefined,
330
+ }),
331
+ );
332
+ },
333
+
334
+ reconcile_tasks: async (args) => {
335
+ return toResult(
336
+ reconcileTasks({
337
+ mode: args?.mode === "safe_fix" ? "safe_fix" : "report_only",
338
+ max_age_minutes:
339
+ args?.max_age_minutes !== undefined ? Number(args.max_age_minutes) : undefined,
340
+ include_done_candidates:
341
+ args?.include_done_candidates !== undefined
342
+ ? Boolean(args.include_done_candidates)
343
+ : undefined,
344
+ }),
345
+ );
346
+ },
347
+
348
+ check_release_gate: async (args) => {
349
+ return toResult(
350
+ await checkReleaseGate({
351
+ repo_path: String(args?.repo_path ?? ""),
352
+ target_stage: String(args?.target_stage ?? "local_ready") as any,
353
+ package_name: args?.package_name ? String(args.package_name) : undefined,
354
+ version: args?.version ? String(args.version) : undefined,
355
+ github_repo: args?.github_repo ? String(args.github_repo) : undefined,
356
+ branch: args?.branch ? String(args.branch) : undefined,
357
+ }),
358
+ );
359
+ },
360
+ };
361
+
362
+ // run_task is conditionally registered (only when enableRunTaskTool === true).
363
+ // Exported separately so registry.ts can add it conditionally to the dispatch map.
364
+ export const runTaskHandler: ToolHandlerMap["run_task"] = async (args) => {
365
+ const config = getConfig();
366
+ if (config.enableRunTaskTool !== true) {
367
+ throw new Error(
368
+ "run_task is disabled. Set enableRunTaskTool: true in config to enable. Prefer using the local watcher (npm run watch).",
369
+ );
370
+ }
371
+ const taskId = String(args?.task_id ?? "");
372
+ const result = await runTask(taskId);
373
+ return toResult(result);
374
+ };
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Dispatch handlers for diagnostic / discovery tools (v0.7.1+).
3
+ *
4
+ * discover_tools and explain_tool are read-only catalog queries.
5
+ * invoke_discovered_tool consumes a discovery token and dispatches to
6
+ * the target tool via handleToolCall.
7
+ *
8
+ * Note: This module imports getToolDefs and handleToolCall from
9
+ * registry.ts. The import is a live ESM binding resolved at runtime,
10
+ * so the circular dependency (registry → diagnosticDispatch → registry)
11
+ * is safe: neither binding is touched during module evaluation.
12
+ */
13
+
14
+ import { discoverTools } from "../discoverTools.js";
15
+ import { explainTool } from "../explainTool.js";
16
+ import { invokeDiscoveredTool } from "../invokeDiscoveredTool.js";
17
+ import { getConfig } from "../../config.js";
18
+ import { resolveToolProfile } from "../toolCatalog.js";
19
+ import { getToolDefs, handleToolCall } from "../registry.js";
20
+ import type { ToolHandlerMap } from "./types.js";
21
+ import { toResult } from "./types.js";
22
+
23
+ export const diagnosticHandlers: ToolHandlerMap = {
24
+ discover_tools: async (args) => {
25
+ const profile =
26
+ args?.profile === "full" ||
27
+ args?.profile === "chatgpt_core" ||
28
+ args?.profile === "chatgpt_direct" ||
29
+ args?.profile === "chatgpt_search"
30
+ ? args.profile
31
+ : undefined;
32
+ const mode =
33
+ args?.mode === "delegate" ||
34
+ args?.mode === "direct" ||
35
+ args?.mode === "audit" ||
36
+ args?.mode === "release" ||
37
+ args?.mode === "diagnostic"
38
+ ? args.mode
39
+ : undefined;
40
+ const riskCeiling = [
41
+ "readonly",
42
+ "workspace_read_sensitive",
43
+ "workspace_write",
44
+ "command",
45
+ "release",
46
+ "credential_sensitive",
47
+ ].includes(String(args?.riskCeiling ?? ""))
48
+ ? (String(args?.riskCeiling) as any)
49
+ : undefined;
50
+ return toResult(
51
+ discoverTools(
52
+ {
53
+ query: String(args?.query ?? ""),
54
+ profile,
55
+ mode,
56
+ maxResults: args?.maxResults !== undefined ? Number(args.maxResults) : undefined,
57
+ riskCeiling,
58
+ includeHighRisk:
59
+ args?.includeHighRisk !== undefined ? Boolean(args.includeHighRisk) : undefined,
60
+ },
61
+ getToolDefs(),
62
+ ),
63
+ );
64
+ },
65
+
66
+ explain_tool: async (args) => {
67
+ return toResult(
68
+ explainTool(
69
+ {
70
+ name: String(args?.name ?? ""),
71
+ includeSchema:
72
+ args?.includeSchema !== undefined ? Boolean(args.includeSchema) : undefined,
73
+ },
74
+ getToolDefs(),
75
+ ),
76
+ );
77
+ },
78
+
79
+ invoke_discovered_tool: async (args) => {
80
+ const profile = resolveToolProfile(getConfig().toolProfile);
81
+ const result = await invokeDiscoveredTool(
82
+ {
83
+ toolName: String(args?.toolName ?? ""),
84
+ arguments:
85
+ args?.arguments && typeof args.arguments === "object"
86
+ ? (args.arguments as Record<string, unknown>)
87
+ : {},
88
+ discoveryToken: String(args?.discoveryToken ?? ""),
89
+ assessmentId: args?.assessmentId ? String(args.assessmentId) : undefined,
90
+ },
91
+ {
92
+ tools: getToolDefs(),
93
+ profile,
94
+ dispatch: async (name, dispatchArgs) => {
95
+ return handleToolCall(name, dispatchArgs);
96
+ },
97
+ },
98
+ );
99
+ return toResult(result);
100
+ },
101
+ };
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Dispatch handlers for Direct session tools.
3
+ *
4
+ * Direct sessions allow ChatGPT to apply patches directly within a
5
+ * repo-scoped workspace. All handlers guard on enableDirectProfile.
6
+ */
7
+
8
+ import { getConfig } from "../../config.js";
9
+ import { PatchWardenError } from "../../errors.js";
10
+ import { createDirectSession } from "../createDirectSession.js";
11
+ import { searchWorkspace } from "../searchWorkspace.js";
12
+ import { applyPatch } from "../applyPatch.js";
13
+ import { runVerification } from "../runVerification.js";
14
+ import { runDirectVerificationBundle } from "../runDirectVerificationBundle.js";
15
+ import { finalizeDirectSession } from "../finalizeDirectSession.js";
16
+ import { auditSession } from "../auditSession.js";
17
+ import { syncFile } from "../syncFile.js";
18
+ import {
19
+ safeAuditDirectSession,
20
+ safeDirectSummary,
21
+ safeFinalizeDirectSession,
22
+ } from "../safeViews.js";
23
+ import type { ToolHandlerMap } from "./types.js";
24
+ import { toResult } from "./types.js";
25
+
26
+ /** Mirror of guardDirectProfileEnabled from the original registry.ts. */
27
+ function guardDirectProfileEnabled(): void {
28
+ const config = getConfig();
29
+ if (!config.enableDirectProfile) {
30
+ throw new PatchWardenError(
31
+ "direct_profile_disabled",
32
+ "Direct profile is disabled by local config.",
33
+ "Set enableDirectProfile: true in patchwarden.config.json to use Direct session tools.",
34
+ true,
35
+ { operation: "direct_tool_call" },
36
+ );
37
+ }
38
+ }
39
+
40
+ export const directHandlers: ToolHandlerMap = {
41
+ create_direct_session: async (args) => {
42
+ guardDirectProfileEnabled();
43
+ return toResult(
44
+ await createDirectSession({
45
+ repo_path: String(args?.repo_path ?? ""),
46
+ title: args?.title ? String(args.title) : undefined,
47
+ }),
48
+ );
49
+ },
50
+
51
+ search_workspace: async (args) => {
52
+ guardDirectProfileEnabled();
53
+ return toResult(
54
+ searchWorkspace({
55
+ session_id: String(args?.session_id ?? ""),
56
+ query: String(args?.query ?? ""),
57
+ max_results: args?.max_results ? Number(args.max_results) : undefined,
58
+ case_sensitive:
59
+ args?.case_sensitive !== undefined ? Boolean(args.case_sensitive) : undefined,
60
+ max_preview_chars: args?.max_preview_chars ? Number(args.max_preview_chars) : undefined,
61
+ include_globs: Array.isArray(args?.include_globs)
62
+ ? args.include_globs.map(String)
63
+ : undefined,
64
+ }),
65
+ );
66
+ },
67
+
68
+ apply_patch: async (args) => {
69
+ guardDirectProfileEnabled();
70
+ return toResult(
71
+ applyPatch({
72
+ session_id: String(args?.session_id ?? ""),
73
+ path: String(args?.path ?? ""),
74
+ expected_sha256: String(args?.expected_sha256 ?? ""),
75
+ operations: Array.isArray(args?.operations) ? (args.operations as any) : [],
76
+ }),
77
+ );
78
+ },
79
+
80
+ run_verification: async (args) => {
81
+ guardDirectProfileEnabled();
82
+ return toResult(
83
+ await runVerification({
84
+ session_id: String(args?.session_id ?? ""),
85
+ command: String(args?.command ?? ""),
86
+ timeout_seconds: args?.timeout_seconds ? Number(args.timeout_seconds) : undefined,
87
+ }),
88
+ );
89
+ },
90
+
91
+ run_direct_verification_bundle: async (args) => {
92
+ guardDirectProfileEnabled();
93
+ return toResult(
94
+ await runDirectVerificationBundle({
95
+ session_id: String(args?.session_id ?? ""),
96
+ commands: Array.isArray(args?.commands)
97
+ ? args.commands.map((command) => String(command))
98
+ : [],
99
+ timeout_seconds: args?.timeout_seconds ? Number(args.timeout_seconds) : undefined,
100
+ }),
101
+ );
102
+ },
103
+
104
+ finalize_direct_session: async (args) => {
105
+ guardDirectProfileEnabled();
106
+ return toResult(
107
+ await finalizeDirectSession({
108
+ session_id: String(args?.session_id ?? ""),
109
+ }),
110
+ );
111
+ },
112
+
113
+ audit_session: async (args) => {
114
+ guardDirectProfileEnabled();
115
+ return toResult(
116
+ auditSession({
117
+ session_id: String(args?.session_id ?? ""),
118
+ }),
119
+ );
120
+ },
121
+
122
+ safe_direct_summary: async (args) => {
123
+ guardDirectProfileEnabled();
124
+ return toResult(
125
+ safeDirectSummary(String(args?.session_id ?? ""), {
126
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
127
+ }),
128
+ );
129
+ },
130
+
131
+ safe_finalize_direct_session: async (args) => {
132
+ guardDirectProfileEnabled();
133
+ return toResult(
134
+ await safeFinalizeDirectSession(String(args?.session_id ?? ""), {
135
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
136
+ }),
137
+ );
138
+ },
139
+
140
+ safe_audit_direct_session: async (args) => {
141
+ guardDirectProfileEnabled();
142
+ return toResult(
143
+ safeAuditDirectSession(String(args?.session_id ?? ""), {
144
+ max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
145
+ }),
146
+ );
147
+ },
148
+
149
+ sync_file: async (args) => {
150
+ guardDirectProfileEnabled();
151
+ return toResult(
152
+ syncFile(
153
+ String(args?.session_id ?? ""),
154
+ String(args?.source_path ?? ""),
155
+ String(args?.target_path ?? ""),
156
+ {
157
+ expected_source_sha256: args?.expected_source_sha256
158
+ ? String(args.expected_source_sha256)
159
+ : undefined,
160
+ expected_target_sha256: args?.expected_target_sha256
161
+ ? String(args.expected_target_sha256)
162
+ : undefined,
163
+ },
164
+ ),
165
+ );
166
+ },
167
+ };