patchwarden 1.5.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 (217) hide show
  1. package/README.en.md +43 -2
  2. package/README.md +43 -2
  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 -2197
  42. package/dist/direct/directGuards.js +30 -8
  43. package/dist/doctor.d.ts +18 -1
  44. package/dist/doctor.js +579 -348
  45. package/dist/goal/goalReport.d.ts +54 -0
  46. package/dist/goal/goalReport.js +204 -0
  47. package/dist/goal/goalStatus.d.ts +6 -0
  48. package/dist/goal/specKitImport.d.ts +63 -0
  49. package/dist/goal/specKitImport.js +220 -0
  50. package/dist/goal/subgoalSync.js +2 -1
  51. package/dist/httpServer.js +15 -12
  52. package/dist/index.js +7 -4
  53. package/dist/logging.d.ts +7 -1
  54. package/dist/logging.js +8 -0
  55. package/dist/runner/changeCapture.d.ts +3 -3
  56. package/dist/runner/changeCapture.js +63 -39
  57. package/dist/runner/cli.js +7 -6
  58. package/dist/runner/postTaskCleanup.js +26 -2
  59. package/dist/runner/runTask.js +245 -221
  60. package/dist/runner/simpleProcess.js +4 -4
  61. package/dist/runner/watch.js +17 -14
  62. package/dist/security/contentRedaction.d.ts +6 -0
  63. package/dist/security/contentRedaction.js +22 -0
  64. package/dist/smoke-test.js +257 -251
  65. package/dist/test/unit/apply-patch.test.d.ts +1 -0
  66. package/dist/test/unit/apply-patch.test.js +225 -0
  67. package/dist/test/unit/create-task.test.d.ts +1 -0
  68. package/dist/test/unit/create-task.test.js +197 -0
  69. package/dist/test/unit/direct-guards.test.js +124 -9
  70. package/dist/test/unit/evidence-pack.test.js +95 -1
  71. package/dist/test/unit/get-task-status.test.d.ts +1 -0
  72. package/dist/test/unit/get-task-status.test.js +174 -0
  73. package/dist/test/unit/get-task-summary.test.d.ts +1 -0
  74. package/dist/test/unit/get-task-summary.test.js +146 -0
  75. package/dist/test/unit/goal-report.test.d.ts +1 -0
  76. package/dist/test/unit/goal-report.test.js +159 -0
  77. package/dist/test/unit/goal-subgoal-task.test.js +6 -6
  78. package/dist/test/unit/goal-tools-registry.test.js +6 -4
  79. package/dist/test/unit/path-guard.test.js +24 -0
  80. package/dist/test/unit/spec-kit-import.test.d.ts +1 -0
  81. package/dist/test/unit/spec-kit-import.test.js +341 -0
  82. package/dist/test/unit/wait-for-task.test.d.ts +1 -0
  83. package/dist/test/unit/wait-for-task.test.js +144 -0
  84. package/dist/tools/auditTask.d.ts +8 -63
  85. package/dist/tools/auditTask.js +12 -8
  86. package/dist/tools/createDirectSession.d.ts +1 -1
  87. package/dist/tools/createDirectSession.js +2 -2
  88. package/dist/tools/createTask.d.ts +2 -2
  89. package/dist/tools/createTask.js +4 -4
  90. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  91. package/dist/tools/dispatch/coreDispatch.js +282 -0
  92. package/dist/tools/dispatch/diagnosticDispatch.d.ts +14 -0
  93. package/dist/tools/dispatch/diagnosticDispatch.js +78 -0
  94. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  95. package/dist/tools/dispatch/directDispatch.js +115 -0
  96. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  97. package/dist/tools/dispatch/goalDispatch.js +91 -0
  98. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  99. package/dist/tools/dispatch/releaseDispatch.js +45 -0
  100. package/dist/tools/dispatch/types.d.ts +23 -0
  101. package/dist/tools/dispatch/types.js +15 -0
  102. package/dist/tools/evidencePack.d.ts +6 -0
  103. package/dist/tools/evidencePack.js +213 -6
  104. package/dist/tools/finalizeDirectSession.d.ts +1 -1
  105. package/dist/tools/finalizeDirectSession.js +3 -3
  106. package/dist/tools/goalSubgoalTask.d.ts +1 -1
  107. package/dist/tools/goalSubgoalTask.js +2 -2
  108. package/dist/tools/healthCheck.js +3 -3
  109. package/dist/tools/registry.d.ts +3 -3
  110. package/dist/tools/registry.js +60 -503
  111. package/dist/tools/retryTask.d.ts +2 -2
  112. package/dist/tools/retryTask.js +2 -2
  113. package/dist/tools/runTaskLoop.js +4 -4
  114. package/dist/tools/safeViews.d.ts +2 -2
  115. package/dist/tools/safeViews.js +2 -2
  116. package/dist/tools/toolRegistry.js +22 -0
  117. package/dist/version.d.ts +1 -1
  118. package/dist/version.js +1 -1
  119. package/docs/CODE_WIKI.md +983 -0
  120. package/docs/agentseal-integration.md +150 -0
  121. package/docs/architecture.md +63 -0
  122. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  123. package/docs/dashboard-overview.md +86 -0
  124. package/docs/demo.md +8 -0
  125. package/docs/direct-session-workflow.md +98 -0
  126. package/docs/evidence-pack-schema.md +215 -0
  127. package/docs/execution-plan-2026-07-09.md +315 -0
  128. package/docs/lineage-evidence-pack-workflow.md +127 -0
  129. package/docs/mcp-inspector-testing.md +200 -0
  130. package/docs/open-source-application.md +162 -0
  131. package/docs/opencode-worker.md +151 -0
  132. package/docs/openhands-worker.md +181 -0
  133. package/docs/release-evidence.md +72 -0
  134. package/docs/roadmap-execution-and-acceptance.md +365 -0
  135. package/docs/spec-kit-integration.md +131 -0
  136. package/docs/task-safe-review-workflow.md +98 -0
  137. package/docs/threat-model.md +79 -0
  138. package/docs/user-feedback.md +40 -0
  139. package/docs/why-patchwarden.md +110 -0
  140. package/package.json +2 -2
  141. package/scripts/checks/control-center-smoke.js +356 -0
  142. package/scripts/checks/lifecycle-smoke.js +29 -23
  143. package/scripts/checks/mcp-smoke.js +3 -1
  144. package/scripts/generate-demo-gif.py +320 -0
  145. package/src/assessments/agentAssessor.ts +3 -3
  146. package/src/assessments/assessmentStore.ts +2 -2
  147. package/src/assessments/confirmCli.ts +5 -4
  148. package/src/config.ts +37 -0
  149. package/src/control/middleware/auth.ts +17 -0
  150. package/src/control/middleware/static.ts +71 -0
  151. package/src/control/routes/audit.ts +321 -0
  152. package/src/control/routes/evidence.ts +107 -0
  153. package/src/control/routes/lineage.ts +92 -0
  154. package/src/control/routes/policy.ts +81 -0
  155. package/src/control/routes/process.ts +204 -0
  156. package/src/control/routes/sessions.ts +251 -0
  157. package/src/control/routes/status.ts +325 -0
  158. package/src/control/routes/taskActions.ts +248 -0
  159. package/src/control/routes/tasks.ts +323 -0
  160. package/src/control/routes/workspace.ts +203 -0
  161. package/src/control/runtime.ts +472 -0
  162. package/src/control/server.ts +471 -0
  163. package/src/control/shared.ts +294 -0
  164. package/src/controlCenter.ts +7 -2347
  165. package/src/direct/directGuards.ts +28 -7
  166. package/src/doctor.ts +741 -481
  167. package/src/goal/goalReport.ts +271 -0
  168. package/src/goal/goalStatus.ts +6 -0
  169. package/src/goal/specKitImport.ts +355 -0
  170. package/src/goal/subgoalSync.ts +4 -2
  171. package/src/httpServer.ts +17 -14
  172. package/src/index.ts +7 -4
  173. package/src/logging.ts +10 -1
  174. package/src/runner/changeCapture.ts +70 -42
  175. package/src/runner/cli.ts +7 -6
  176. package/src/runner/postTaskCleanup.ts +26 -2
  177. package/src/runner/runTask.ts +325 -223
  178. package/src/runner/simpleProcess.ts +4 -4
  179. package/src/runner/watch.ts +17 -14
  180. package/src/security/contentRedaction.ts +29 -0
  181. package/src/smoke-test.ts +252 -250
  182. package/src/test/unit/apply-patch.test.ts +293 -0
  183. package/src/test/unit/create-task.test.ts +255 -0
  184. package/src/test/unit/direct-guards.test.ts +178 -8
  185. package/src/test/unit/evidence-pack.test.ts +110 -1
  186. package/src/test/unit/get-task-status.test.ts +203 -0
  187. package/src/test/unit/get-task-summary.test.ts +173 -0
  188. package/src/test/unit/goal-report.test.ts +189 -0
  189. package/src/test/unit/goal-subgoal-task.test.ts +6 -6
  190. package/src/test/unit/goal-tools-registry.test.ts +7 -5
  191. package/src/test/unit/path-guard.test.ts +30 -0
  192. package/src/test/unit/spec-kit-import.test.ts +429 -0
  193. package/src/test/unit/wait-for-task.test.ts +176 -0
  194. package/src/tools/auditTask.ts +99 -59
  195. package/src/tools/createDirectSession.ts +3 -3
  196. package/src/tools/createTask.ts +7 -7
  197. package/src/tools/dispatch/coreDispatch.ts +374 -0
  198. package/src/tools/dispatch/diagnosticDispatch.ts +101 -0
  199. package/src/tools/dispatch/directDispatch.ts +167 -0
  200. package/src/tools/dispatch/goalDispatch.ts +127 -0
  201. package/src/tools/dispatch/releaseDispatch.ts +65 -0
  202. package/src/tools/dispatch/types.ts +24 -0
  203. package/src/tools/evidencePack.ts +291 -6
  204. package/src/tools/finalizeDirectSession.ts +4 -4
  205. package/src/tools/goalSubgoalTask.ts +2 -2
  206. package/src/tools/healthCheck.ts +3 -3
  207. package/src/tools/registry.ts +68 -628
  208. package/src/tools/retryTask.ts +2 -2
  209. package/src/tools/runTaskLoop.ts +4 -4
  210. package/src/tools/safeViews.ts +2 -2
  211. package/src/tools/toolRegistry.ts +22 -0
  212. package/src/version.ts +1 -1
  213. package/ui/pages/audit.html +192 -3
  214. package/ui/pages/dashboard.html +959 -44
  215. package/ui/pages/direct-sessions.html +505 -53
  216. package/ui/pages/task-detail.html +456 -438
  217. package/ui/pages/tasks.html +598 -61
@@ -4,57 +4,15 @@
4
4
  */
5
5
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
6
6
  import { getAllConfiguredTestCommands, getAllConfiguredDirectCommands, getConfig } from "../config.js";
7
- import { savePlan } from "../tools/savePlan.js";
8
- import { getPlan } from "../tools/getPlan.js";
9
- import { createTask } from "../tools/createTask.js";
10
- import { getTaskStatus } from "../tools/getTaskStatus.js";
11
- import { getResult, getResultJson, getDiff, getTestLog, getTaskLogTail } from "../tools/taskOutputs.js";
12
- import { listWorkspace } from "../tools/listWorkspace.js";
13
- import { readWorkspaceFile } from "../tools/readWorkspaceFile.js";
14
- import { listTasks } from "../tools/listTasks.js";
15
- import { cancelTask } from "../tools/cancelTask.js";
16
- import { killTask } from "../tools/killTask.js";
17
- import { retryTask } from "../tools/retryTask.js";
18
- import { getTaskStdoutTail } from "../tools/getTaskStdoutTail.js";
19
- import { getTaskProgress } from "../tools/getTaskProgress.js";
20
- import { listAgents } from "../tools/listAgents.js";
21
- import { healthCheck } from "../tools/healthCheck.js";
22
- import { getTaskSummary } from "../tools/getTaskSummary.js";
23
- import { waitForTask } from "../tools/waitForTask.js";
24
- import { runTaskLoop } from "./runTaskLoop.js";
25
- import { getTaskLineage } from "./taskLineage.js";
26
- import { exportTaskEvidencePack } from "./evidencePack.js";
27
- import { recommendAgentForTask } from "./recommendAgentForTask.js";
28
7
  import { errorPayload, PatchWardenError } from "../errors.js";
29
- import { auditTask } from "../tools/auditTask.js";
30
- import { safeStatus } from "../tools/safeStatus.js";
31
- import { safeAudit, safeAuditDirectSession, safeDiffSummary, safeDirectSummary, safeFinalizeDirectSession, safeResult, safeTestSummary, } from "../tools/safeViews.js";
32
- import { diagnoseTask } from "../tools/diagnoseTask.js";
33
- import { reconcileTasks } from "../tools/reconcileTasks.js";
34
- import { discoverTools } from "../tools/discoverTools.js";
35
- import { explainTool } from "../tools/explainTool.js";
36
- import { invokeDiscoveredTool } from "./invokeDiscoveredTool.js";
37
8
  import { logger } from "../logging.js";
38
- import { runTask } from "../runner/runTask.js";
39
- import { createDirectSession } from "../tools/createDirectSession.js";
40
- import { searchWorkspace } from "../tools/searchWorkspace.js";
41
- import { applyPatch } from "../tools/applyPatch.js";
42
- import { runVerification } from "../tools/runVerification.js";
43
- import { runDirectVerificationBundle } from "../tools/runDirectVerificationBundle.js";
44
- import { finalizeDirectSession } from "../tools/finalizeDirectSession.js";
45
- import { auditSession } from "../tools/auditSession.js";
46
- import { syncFile } from "../tools/syncFile.js";
47
- import { createGoal, listGoals, readGoal, readGoalStatus } from "../goal/goalStore.js";
48
- import { suggestNextSubgoal } from "../goal/goalGraph.js";
49
- import { exportHandoff } from "../goal/handoffExport.js";
50
- import { acceptSubgoal, rejectSubgoal, summarizeGoalProgress } from "../goal/goalProgress.js";
51
- import { createSubgoalTask } from "./goalSubgoalTask.js";
52
- import { checkReleaseGate } from "./checkReleaseGate.js";
53
- import { getProjectPolicyTool, releaseCheck, releaseCleanup, releasePrepare, releaseVerify, } from "./releaseMode.js";
54
- import { mergeWorktreeTool } from "./mergeWorktree.js";
55
- import { discardWorktreeTool } from "./discardWorktree.js";
56
9
  import { TASK_TEMPLATE_NAMES } from "./taskTemplates.js";
57
10
  import { buildToolCatalogSnapshot, getLastToolCatalogSnapshot, resolveToolProfile, selectToolsForProfile, } from "./toolCatalog.js";
11
+ import { coreHandlers, runTaskHandler } from "./dispatch/coreDispatch.js";
12
+ import { goalHandlers } from "./dispatch/goalDispatch.js";
13
+ import { directHandlers } from "./dispatch/directDispatch.js";
14
+ import { releaseHandlers } from "./dispatch/releaseDispatch.js";
15
+ import { diagnosticHandlers } from "./dispatch/diagnosticDispatch.js";
58
16
  export function getToolDefs() {
59
17
  const config = getConfig();
60
18
  const agentNames = Object.keys(config.agents).sort();
@@ -876,6 +834,29 @@ export function getToolDefs() {
876
834
  required: ["goal_id"],
877
835
  },
878
836
  },
837
+ {
838
+ name: "export_goal_report",
839
+ description: "Export a structured final report for a Goal session, aggregating subgoal completion, task evidence, and risk summary.",
840
+ inputSchema: {
841
+ type: "object",
842
+ properties: {
843
+ goal_id: { type: "string", description: "The goal id to export report for." },
844
+ },
845
+ required: ["goal_id"],
846
+ },
847
+ },
848
+ {
849
+ name: "import_speckit_tasks",
850
+ description: "Import Spec Kit tasks into a Goal session as subgoals, mapping task files to scope hints and acceptance criteria.",
851
+ inputSchema: {
852
+ type: "object",
853
+ properties: {
854
+ goal_id: { type: "string", description: "The goal id to import tasks into." },
855
+ spec_kit_json: { type: "string", description: "Spec Kit JSON text containing spec, tasks[], and acceptance[] fields." },
856
+ },
857
+ required: ["goal_id", "spec_kit_json"],
858
+ },
859
+ },
879
860
  {
880
861
  name: "check_release_gate",
881
862
  description: "v1.0.0: Verify release readiness across five sequential stages: local_ready → packed_ready → published_verified → github_release_verified → ci_verified. Remote stages (published/github/ci) query npm registry and GitHub API via node:https read-only GET; network errors return 'not_checked' (not 'failed'). Never claims release complete before published_verified passes. Does not execute shell commands for remote queries.",
@@ -1203,13 +1184,28 @@ export function getToolCatalogSnapshot() {
1203
1184
  const config = getConfig();
1204
1185
  return buildToolCatalogSnapshot(tools, resolveToolProfile(config.toolProfile));
1205
1186
  }
1206
- // ── Request handler ───────────────────────────────────────────────
1207
- function guardDirectProfileEnabled() {
1187
+ // ── Dispatch map ─────────────────────────────────────────────────
1188
+ /**
1189
+ * Combined dispatch map assembled from all domain handler maps.
1190
+ * run_task is conditionally added when enableRunTaskTool is true,
1191
+ * mirroring the conditional registration in getToolDefs().
1192
+ */
1193
+ function buildDispatchMap() {
1208
1194
  const config = getConfig();
1209
- if (!config.enableDirectProfile) {
1210
- throw new PatchWardenError("direct_profile_disabled", "Direct profile is disabled by local config.", "Set enableDirectProfile: true in patchwarden.config.json to use Direct session tools.", true, { operation: "direct_tool_call" });
1195
+ const map = {
1196
+ ...coreHandlers,
1197
+ ...goalHandlers,
1198
+ ...directHandlers,
1199
+ ...releaseHandlers,
1200
+ ...diagnosticHandlers,
1201
+ };
1202
+ if (config.enableRunTaskTool === true) {
1203
+ map.run_task = runTaskHandler;
1211
1204
  }
1205
+ return map;
1212
1206
  }
1207
+ const dispatchMap = buildDispatchMap();
1208
+ // ── Request handler ───────────────────────────────────────────────
1213
1209
  export async function handleToolCall(name, args) {
1214
1210
  const startTime = Date.now();
1215
1211
  const taskId = args?.task_id ? String(args.task_id) : args?.session_id ? String(args.session_id) : undefined;
@@ -1225,459 +1221,11 @@ export async function handleToolCall(name, args) {
1225
1221
  }
1226
1222
  }
1227
1223
  async function handleToolCallInternal(name, args) {
1228
- switch (name) {
1229
- case "save_plan": {
1230
- return toResult(savePlan({
1231
- title: String(args?.title ?? ""),
1232
- content: args?.content !== undefined ? String(args.content) : "",
1233
- plan_ref: args?.plan_ref ? String(args.plan_ref) : undefined,
1234
- }));
1235
- }
1236
- case "get_plan": {
1237
- return toResult(getPlan({ plan_id: String(args?.plan_id ?? "") }));
1238
- }
1239
- case "create_task": {
1240
- return toResult(createTask({
1241
- plan_id: args?.plan_id ? String(args.plan_id) : undefined,
1242
- inline_plan: args?.inline_plan ? String(args.inline_plan) : undefined,
1243
- plan_title: args?.plan_title ? String(args.plan_title) : undefined,
1244
- template: args?.template ? String(args.template) : undefined,
1245
- goal: args?.goal ? String(args.goal) : undefined,
1246
- source_task_id: args?.source_task_id ? String(args.source_task_id) : undefined,
1247
- agent: String(args?.agent ?? ""),
1248
- repo_path: args?.repo_path ? String(args.repo_path) : undefined,
1249
- test_command: args?.test_command ? String(args.test_command) : undefined,
1250
- verify_commands: Array.isArray(args?.verify_commands)
1251
- ? args.verify_commands.map((command) => String(command))
1252
- : undefined,
1253
- timeout_seconds: args?.timeout_seconds !== undefined
1254
- ? Number(args.timeout_seconds)
1255
- : undefined,
1256
- execution_mode: args?.execution_mode === "assess_only" ? "assess_only" : "execute",
1257
- assessment_id: args?.assessment_id ? String(args.assessment_id) : undefined,
1258
- }));
1259
- }
1260
- case "run_task_loop": {
1261
- return toResult(await runTaskLoop({
1262
- repo_path: String(args?.repo_path ?? ""),
1263
- goal: String(args?.goal ?? ""),
1264
- verify_commands: Array.isArray(args?.verify_commands)
1265
- ? args.verify_commands.map((command) => String(command))
1266
- : [],
1267
- agent: args?.agent ? String(args.agent) : undefined,
1268
- template: args?.template === "inspect_only" || args?.template === "release_check"
1269
- ? args.template
1270
- : "feature_small",
1271
- max_iterations: args?.max_iterations !== undefined ? Number(args.max_iterations) : undefined,
1272
- task_timeout_seconds: args?.task_timeout_seconds !== undefined ? Number(args.task_timeout_seconds) : undefined,
1273
- auto_fix_tests: args?.auto_fix_tests !== undefined ? Boolean(args.auto_fix_tests) : undefined,
1274
- auto_cleanup_artifacts: args?.auto_cleanup_artifacts !== undefined ? Boolean(args.auto_cleanup_artifacts) : undefined,
1275
- stop_on_high_risk: args?.stop_on_high_risk !== undefined ? Boolean(args.stop_on_high_risk) : undefined,
1276
- direct_verify: args?.direct_verify !== undefined ? Boolean(args.direct_verify) : undefined,
1277
- direct_verify_commands: Array.isArray(args?.direct_verify_commands)
1278
- ? args.direct_verify_commands.map((command) => String(command))
1279
- : undefined,
1280
- direct_verify_timeout_seconds: args?.direct_verify_timeout_seconds !== undefined ? Number(args.direct_verify_timeout_seconds) : undefined,
1281
- scope_files: Array.isArray(args?.scope_files)
1282
- ? args.scope_files.map((entry) => String(entry))
1283
- : undefined,
1284
- isolation_mode: args?.isolation_mode === "worktree" ? "worktree" : "current_repo",
1285
- worktree_base_branch: args?.worktree_base_branch ? String(args.worktree_base_branch) : undefined,
1286
- worktree_cleanup: args?.worktree_cleanup === "archive" || args?.worktree_cleanup === "delete_ignored_only"
1287
- ? args.worktree_cleanup
1288
- : "keep",
1289
- }));
1290
- }
1291
- case "recommend_agent_for_task": {
1292
- return toResult(recommendAgentForTask({
1293
- repo_path: String(args?.repo_path ?? ""),
1294
- goal: String(args?.goal ?? ""),
1295
- scope_files: Array.isArray(args?.scope_files) ? args.scope_files.map((entry) => String(entry)) : undefined,
1296
- template: args?.template ? String(args.template) : undefined,
1297
- risk_hint: args?.risk_hint ? String(args.risk_hint) : undefined,
1298
- }));
1299
- }
1300
- case "get_task_lineage": {
1301
- return toResult(getTaskLineage(String(args?.lineage_id ?? ""), {
1302
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1303
- }));
1304
- }
1305
- case "export_task_evidence_pack": {
1306
- return toResult(exportTaskEvidencePack({
1307
- lineage_id: String(args?.lineage_id ?? ""),
1308
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1309
- }));
1310
- }
1311
- case "get_project_policy": {
1312
- return toResult(getProjectPolicyTool(String(args?.repo_path ?? "")));
1313
- }
1314
- case "get_task_status": {
1315
- return toResult(getTaskStatus(String(args?.task_id ?? "")));
1316
- }
1317
- case "get_result": {
1318
- return toResult(getResult(String(args?.task_id ?? "")));
1319
- }
1320
- case "get_result_json": {
1321
- return toResult(getResultJson(String(args?.task_id ?? "")));
1322
- }
1323
- case "get_diff": {
1324
- return toResult(getDiff(String(args?.task_id ?? "")));
1325
- }
1326
- case "get_test_log": {
1327
- return toResult(getTestLog(String(args?.task_id ?? "")));
1328
- }
1329
- case "list_workspace": {
1330
- return toResult(listWorkspace(args?.path ? String(args.path) : undefined));
1331
- }
1332
- case "read_workspace_file": {
1333
- const sessionId = args?.session_id ? String(args.session_id) : undefined;
1334
- return toResult(readWorkspaceFile({
1335
- path: String(args?.path ?? ""),
1336
- session_id: sessionId,
1337
- }));
1338
- }
1339
- case "list_tasks": {
1340
- return toResult(listTasks({
1341
- status: args?.status ? String(args.status) : undefined,
1342
- repo_path: args?.repo_path ? String(args.repo_path) : undefined,
1343
- active_only: args?.active_only !== undefined ? Boolean(args.active_only) : undefined,
1344
- limit: args?.limit ? Number(args.limit) : undefined,
1345
- }));
1346
- }
1347
- case "list_agents": {
1348
- return toResult(listAgents());
1349
- }
1350
- case "health_check": {
1351
- return toResult(healthCheck(getToolCatalogSnapshot(), {
1352
- detail: args?.detail === "self_diagnostic" ? "self_diagnostic" : "standard",
1353
- }));
1354
- }
1355
- case "cancel_task": {
1356
- return toResult(cancelTask(String(args?.task_id ?? "")));
1357
- }
1358
- case "kill_task": {
1359
- return toResult(killTask(String(args?.task_id ?? "")));
1360
- }
1361
- case "retry_task": {
1362
- return toResult(retryTask(String(args?.task_id ?? "")));
1363
- }
1364
- case "get_task_stdout_tail": {
1365
- return toResult(getTaskStdoutTail(String(args?.task_id ?? ""), args?.lines ? Number(args.lines) : undefined));
1366
- }
1367
- case "get_task_log_tail": {
1368
- return toResult(getTaskLogTail(String(args?.task_id ?? ""), args?.file || "stdout", {
1369
- lines: args?.lines ? Number(args.lines) : undefined,
1370
- redact: args?.redact !== undefined ? Boolean(args.redact) : undefined,
1371
- }));
1372
- }
1373
- case "get_task_progress": {
1374
- return toResult(getTaskProgress(String(args?.task_id ?? "")));
1375
- }
1376
- case "wait_for_task": {
1377
- const waitSeconds = normalizeWaitSeconds(args);
1378
- return toResult(await waitForTask(String(args?.task_id ?? ""), waitSeconds));
1379
- }
1380
- case "get_task_summary": {
1381
- return toResult(getTaskSummary(String(args?.task_id ?? ""), {
1382
- view: normalizeSummaryView(args?.view),
1383
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1384
- }));
1385
- }
1386
- case "audit_task": {
1387
- return toResult(auditTask(String(args?.task_id ?? "")));
1388
- }
1389
- case "safe_status": {
1390
- return toResult(safeStatus(String(args?.task_id ?? "")));
1391
- }
1392
- case "safe_result": {
1393
- return toResult(safeResult(String(args?.task_id ?? ""), {
1394
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1395
- }));
1396
- }
1397
- case "safe_audit": {
1398
- return toResult(safeAudit(String(args?.task_id ?? ""), {
1399
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1400
- }));
1401
- }
1402
- case "safe_test_summary": {
1403
- return toResult(safeTestSummary(String(args?.task_id ?? "")));
1404
- }
1405
- case "safe_diff_summary": {
1406
- return toResult(safeDiffSummary(String(args?.task_id ?? ""), {
1407
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1408
- }));
1409
- }
1410
- case "diagnose_task": {
1411
- return toResult(diagnoseTask({
1412
- task_id: String(args?.task_id ?? ""),
1413
- include_logs: args?.include_logs !== undefined ? Boolean(args.include_logs) : undefined,
1414
- }));
1415
- }
1416
- case "reconcile_tasks": {
1417
- return toResult(reconcileTasks({
1418
- mode: args?.mode === "safe_fix" ? "safe_fix" : "report_only",
1419
- max_age_minutes: args?.max_age_minutes !== undefined ? Number(args.max_age_minutes) : undefined,
1420
- include_done_candidates: args?.include_done_candidates !== undefined ? Boolean(args.include_done_candidates) : undefined,
1421
- }));
1422
- }
1423
- case "discover_tools": {
1424
- const profile = args?.profile === "full" || args?.profile === "chatgpt_core" || args?.profile === "chatgpt_direct" || args?.profile === "chatgpt_search"
1425
- ? args.profile : undefined;
1426
- const mode = args?.mode === "delegate" || args?.mode === "direct" || args?.mode === "audit" || args?.mode === "release" || args?.mode === "diagnostic"
1427
- ? args.mode : undefined;
1428
- const riskCeiling = ["readonly", "workspace_read_sensitive", "workspace_write", "command", "release", "credential_sensitive"]
1429
- .includes(String(args?.riskCeiling ?? "")) ? String(args?.riskCeiling) : undefined;
1430
- return toResult(discoverTools({
1431
- query: String(args?.query ?? ""),
1432
- profile,
1433
- mode,
1434
- maxResults: args?.maxResults !== undefined ? Number(args.maxResults) : undefined,
1435
- riskCeiling,
1436
- includeHighRisk: args?.includeHighRisk !== undefined ? Boolean(args.includeHighRisk) : undefined,
1437
- }, getToolDefs()));
1438
- }
1439
- case "explain_tool": {
1440
- return toResult(explainTool({
1441
- name: String(args?.name ?? ""),
1442
- includeSchema: args?.includeSchema !== undefined ? Boolean(args.includeSchema) : undefined,
1443
- }, getToolDefs()));
1444
- }
1445
- case "invoke_discovered_tool": {
1446
- const profile = resolveToolProfile(getConfig().toolProfile);
1447
- const result = await invokeDiscoveredTool({
1448
- toolName: String(args?.toolName ?? ""),
1449
- arguments: (args?.arguments && typeof args.arguments === "object" ? args.arguments : {}),
1450
- discoveryToken: String(args?.discoveryToken ?? ""),
1451
- assessmentId: args?.assessmentId ? String(args.assessmentId) : undefined,
1452
- }, {
1453
- tools: getToolDefs(),
1454
- profile,
1455
- dispatch: async (name, dispatchArgs) => {
1456
- return handleToolCall(name, dispatchArgs);
1457
- },
1458
- });
1459
- return toResult(result);
1460
- }
1461
- case "run_task": {
1462
- const config = getConfig();
1463
- if (config.enableRunTaskTool !== true) {
1464
- throw new Error("run_task is disabled. Set enableRunTaskTool: true in config to enable. Prefer using the local watcher (npm run watch).");
1465
- }
1466
- const taskId = String(args?.task_id ?? "");
1467
- const result = await runTask(taskId);
1468
- return toResult(result);
1469
- }
1470
- case "create_direct_session": {
1471
- guardDirectProfileEnabled();
1472
- return toResult(createDirectSession({
1473
- repo_path: String(args?.repo_path ?? ""),
1474
- title: args?.title ? String(args.title) : undefined,
1475
- }));
1476
- }
1477
- case "search_workspace": {
1478
- guardDirectProfileEnabled();
1479
- return toResult(searchWorkspace({
1480
- session_id: String(args?.session_id ?? ""),
1481
- query: String(args?.query ?? ""),
1482
- max_results: args?.max_results ? Number(args.max_results) : undefined,
1483
- case_sensitive: args?.case_sensitive !== undefined ? Boolean(args.case_sensitive) : undefined,
1484
- max_preview_chars: args?.max_preview_chars ? Number(args.max_preview_chars) : undefined,
1485
- include_globs: Array.isArray(args?.include_globs) ? args.include_globs.map(String) : undefined,
1486
- }));
1487
- }
1488
- case "apply_patch": {
1489
- guardDirectProfileEnabled();
1490
- return toResult(applyPatch({
1491
- session_id: String(args?.session_id ?? ""),
1492
- path: String(args?.path ?? ""),
1493
- expected_sha256: String(args?.expected_sha256 ?? ""),
1494
- operations: Array.isArray(args?.operations) ? args.operations : [],
1495
- }));
1496
- }
1497
- case "run_verification": {
1498
- guardDirectProfileEnabled();
1499
- return toResult(await runVerification({
1500
- session_id: String(args?.session_id ?? ""),
1501
- command: String(args?.command ?? ""),
1502
- timeout_seconds: args?.timeout_seconds ? Number(args.timeout_seconds) : undefined,
1503
- }));
1504
- }
1505
- case "run_direct_verification_bundle": {
1506
- guardDirectProfileEnabled();
1507
- return toResult(await runDirectVerificationBundle({
1508
- session_id: String(args?.session_id ?? ""),
1509
- commands: Array.isArray(args?.commands) ? args.commands.map((command) => String(command)) : [],
1510
- timeout_seconds: args?.timeout_seconds ? Number(args.timeout_seconds) : undefined,
1511
- }));
1512
- }
1513
- case "finalize_direct_session": {
1514
- guardDirectProfileEnabled();
1515
- return toResult(finalizeDirectSession({
1516
- session_id: String(args?.session_id ?? ""),
1517
- }));
1518
- }
1519
- case "audit_session": {
1520
- guardDirectProfileEnabled();
1521
- return toResult(auditSession({
1522
- session_id: String(args?.session_id ?? ""),
1523
- }));
1524
- }
1525
- case "safe_direct_summary": {
1526
- guardDirectProfileEnabled();
1527
- return toResult(safeDirectSummary(String(args?.session_id ?? ""), {
1528
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1529
- }));
1530
- }
1531
- case "safe_finalize_direct_session": {
1532
- guardDirectProfileEnabled();
1533
- return toResult(safeFinalizeDirectSession(String(args?.session_id ?? ""), {
1534
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1535
- }));
1536
- }
1537
- case "safe_audit_direct_session": {
1538
- guardDirectProfileEnabled();
1539
- return toResult(safeAuditDirectSession(String(args?.session_id ?? ""), {
1540
- max_items: args?.max_items !== undefined ? Number(args.max_items) : undefined,
1541
- }));
1542
- }
1543
- case "sync_file": {
1544
- guardDirectProfileEnabled();
1545
- return toResult(syncFile(String(args?.session_id ?? ""), String(args?.source_path ?? ""), String(args?.target_path ?? ""), {
1546
- expected_source_sha256: args?.expected_source_sha256 ? String(args.expected_source_sha256) : undefined,
1547
- expected_target_sha256: args?.expected_target_sha256 ? String(args.expected_target_sha256) : undefined,
1548
- }));
1549
- }
1550
- case "create_goal": {
1551
- return toResult(createGoal(String(args?.repo_path ?? ""), String(args?.title ?? ""), String(args?.goal_description ?? "")));
1552
- }
1553
- case "list_goals": {
1554
- return toResult({ goals: listGoals() });
1555
- }
1556
- case "read_goal": {
1557
- return toResult(readGoal(String(args?.goal_id ?? "")));
1558
- }
1559
- case "create_subgoal_task": {
1560
- return toResult(createSubgoalTask({
1561
- goal_id: String(args?.goal_id ?? ""),
1562
- subgoal_title: String(args?.subgoal_title ?? ""),
1563
- depends_on: Array.isArray(args?.depends_on) ? args.depends_on.map(String) : undefined,
1564
- plan_id: args?.plan_id ? String(args.plan_id) : undefined,
1565
- inline_plan: args?.inline_plan ? String(args.inline_plan) : undefined,
1566
- plan_title: args?.plan_title ? String(args.plan_title) : undefined,
1567
- template: args?.template ? String(args.template) : undefined,
1568
- goal: args?.goal ? String(args.goal) : undefined,
1569
- agent: args?.agent ? String(args.agent) : undefined,
1570
- repo_path: String(args?.repo_path ?? ""),
1571
- test_command: args?.test_command ? String(args.test_command) : undefined,
1572
- verify_commands: Array.isArray(args?.verify_commands) ? args.verify_commands.map(String) : undefined,
1573
- timeout_seconds: args?.timeout_seconds ? Number(args.timeout_seconds) : undefined,
1574
- scope: Array.isArray(args?.scope) ? args.scope.map(String) : undefined,
1575
- forbidden: Array.isArray(args?.forbidden) ? args.forbidden.map(String) : undefined,
1576
- verification: Array.isArray(args?.verification) ? args.verification.map(String) : undefined,
1577
- done_evidence: Array.isArray(args?.done_evidence) ? args.done_evidence.map(String) : undefined,
1578
- isolate_worktree: args?.isolate_worktree === undefined ? undefined : Boolean(args.isolate_worktree),
1579
- }));
1580
- }
1581
- case "accept_subgoal": {
1582
- return toResult(acceptSubgoal(String(args?.goal_id ?? ""), String(args?.subgoal_id ?? "")));
1583
- }
1584
- case "reject_subgoal": {
1585
- return toResult(rejectSubgoal(String(args?.goal_id ?? ""), String(args?.subgoal_id ?? ""), String(args?.reason ?? "")));
1586
- }
1587
- case "suggest_next_subgoal": {
1588
- const goalStatus = readGoalStatus(String(args?.goal_id ?? ""));
1589
- return toResult(suggestNextSubgoal(goalStatus));
1590
- }
1591
- case "summarize_goal_progress": {
1592
- return toResult(summarizeGoalProgress(String(args?.goal_id ?? "")));
1593
- }
1594
- case "export_handoff": {
1595
- const goalId = String(args?.goal_id ?? "");
1596
- const goalStatus = readGoalStatus(goalId);
1597
- return toResult(exportHandoff(goalId, goalStatus));
1598
- }
1599
- case "check_release_gate": {
1600
- return toResult(await checkReleaseGate({
1601
- repo_path: String(args?.repo_path ?? ""),
1602
- target_stage: String(args?.target_stage ?? "local_ready"),
1603
- package_name: args?.package_name ? String(args.package_name) : undefined,
1604
- version: args?.version ? String(args.version) : undefined,
1605
- github_repo: args?.github_repo ? String(args.github_repo) : undefined,
1606
- branch: args?.branch ? String(args.branch) : undefined,
1607
- }));
1608
- }
1609
- case "release_check": {
1610
- return toResult(await releaseCheck({
1611
- repo_path: String(args?.repo_path ?? ""),
1612
- target_stage: String(args?.target_stage ?? "local_ready"),
1613
- package_name: args?.package_name ? String(args.package_name) : undefined,
1614
- version: args?.version ? String(args.version) : undefined,
1615
- github_repo: args?.github_repo ? String(args.github_repo) : undefined,
1616
- branch: args?.branch ? String(args.branch) : undefined,
1617
- }));
1618
- }
1619
- case "release_prepare": {
1620
- return toResult(releasePrepare({
1621
- repo_path: String(args?.repo_path ?? ""),
1622
- required_commands: Array.isArray(args?.required_commands)
1623
- ? args.required_commands.map(String)
1624
- : undefined,
1625
- timeout_seconds: args?.timeout_seconds !== undefined ? Number(args.timeout_seconds) : undefined,
1626
- }));
1627
- }
1628
- case "release_verify": {
1629
- return toResult(await releaseVerify({
1630
- repo_path: String(args?.repo_path ?? ""),
1631
- package_name: args?.package_name ? String(args.package_name) : undefined,
1632
- version: args?.version ? String(args.version) : undefined,
1633
- github_repo: args?.github_repo ? String(args.github_repo) : undefined,
1634
- branch: args?.branch ? String(args.branch) : undefined,
1635
- }));
1636
- }
1637
- case "release_cleanup": {
1638
- return toResult(releaseCleanup({
1639
- repo_path: String(args?.repo_path ?? ""),
1640
- dry_run: args?.dry_run !== undefined ? Boolean(args.dry_run) : undefined,
1641
- patterns: Array.isArray(args?.patterns) ? args.patterns.map(String) : undefined,
1642
- }));
1643
- }
1644
- case "merge_worktree": {
1645
- return toResult(mergeWorktreeTool({
1646
- worktree_id: String(args?.worktree_id ?? ""),
1647
- repo_path: String(args?.repo_path ?? ""),
1648
- }));
1649
- }
1650
- case "discard_worktree": {
1651
- return toResult(discardWorktreeTool({
1652
- worktree_id: String(args?.worktree_id ?? ""),
1653
- repo_path: String(args?.repo_path ?? ""),
1654
- }));
1655
- }
1656
- default:
1657
- throw new Error(`Unknown tool: ${name}`);
1658
- }
1659
- }
1660
- function normalizeWaitSeconds(args) {
1661
- const legacy = args?.wait_seconds;
1662
- const preferred = args?.timeout_seconds;
1663
- if (legacy !== undefined && preferred !== undefined && Number(legacy) !== Number(preferred)) {
1664
- throw new Error("wait_seconds and timeout_seconds must match when both are supplied.");
1224
+ const handler = dispatchMap[name];
1225
+ if (!handler) {
1226
+ throw new Error(`Unknown tool: ${name}`);
1665
1227
  }
1666
- const value = preferred ?? legacy;
1667
- return value === undefined ? undefined : Number(value);
1668
- }
1669
- function normalizeSummaryView(value) {
1670
- if (value === undefined)
1671
- return "standard";
1672
- if (value !== "compact" && value !== "standard") {
1673
- throw new Error('view must be "compact" or "standard".');
1674
- }
1675
- return value;
1676
- }
1677
- function toResult(data) {
1678
- return {
1679
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
1680
- };
1228
+ return handler(args);
1681
1229
  }
1682
1230
  // ── Register on MCP Server ────────────────────────────────────────
1683
1231
  export function registerTools(server) {
@@ -1686,6 +1234,15 @@ export function registerTools(server) {
1686
1234
  // tools/list and tools/call when the profile is reconfigured at runtime.
1687
1235
  const activeTools = getToolDefs();
1688
1236
  const activeNames = new Set(activeTools.map((tool) => tool.name));
1237
+ // Verify every registered tool has a dispatch handler.
1238
+ // The dispatch map may contain extra handlers for tools exposed under
1239
+ // other profiles (e.g. get_plan is only in the "full" profile), so we
1240
+ // only check the subset direction: every active tool must have a handler.
1241
+ for (const tool of activeTools) {
1242
+ if (!dispatchMap[tool.name]) {
1243
+ throw new Error(`Dispatch map is missing handler for registered tool "${tool.name}".`);
1244
+ }
1245
+ }
1689
1246
  server.setRequestHandler(ListToolsRequestSchema, async () => {
1690
1247
  const catalog = getLastToolCatalogSnapshot();
1691
1248
  return {
@@ -1,8 +1,8 @@
1
- export declare function retryTask(taskId: string): {
1
+ export declare function retryTask(taskId: string): Promise<{
2
2
  original_task_id: string;
3
3
  new_task_id: string;
4
4
  plan_id: string;
5
5
  agent: string;
6
6
  status: import("./createTask.js").TaskStatus;
7
7
  message: string;
8
- };
8
+ }>;
@@ -3,7 +3,7 @@ import { join } from "node:path";
3
3
  import { getTasksDir, getConfig } from "../config.js";
4
4
  import { guardReadPath } from "../security/pathGuard.js";
5
5
  import { createTask } from "./createTask.js";
6
- export function retryTask(taskId) {
6
+ export async function retryTask(taskId) {
7
7
  const config = getConfig();
8
8
  const tasksDir = getTasksDir(config);
9
9
  const taskDir = join(tasksDir, taskId);
@@ -17,7 +17,7 @@ export function retryTask(taskId) {
17
17
  throw new Error(`Task "${taskId}" is missing plan_id or agent. Cannot retry.`);
18
18
  }
19
19
  // Create a new task with the same parameters
20
- const newTask = createTask({
20
+ const newTask = await createTask({
21
21
  plan_id: data.plan_id,
22
22
  agent: data.agent,
23
23
  repo_path: data.repo_path,
@@ -130,14 +130,14 @@ export async function runTaskLoopWithDeps(input, deps) {
130
130
  timeout_seconds: normalized.task_timeout_seconds,
131
131
  execution_mode: "assess_only",
132
132
  };
133
- const assessment = deps.createTask(assessmentInput);
133
+ const assessment = await deps.createTask(assessmentInput);
134
134
  if (assessment.decision === "blocked") {
135
135
  return finalize("blocked", "high_risk_blocked", "Risk assessment blocked task execution.", assessment.reason_codes?.join(", "));
136
136
  }
137
137
  if (assessment.decision === "needs_confirm") {
138
138
  return finalize("blocked", "user_confirmation_required", "Ask the user to confirm the assessment before executing the loop.");
139
139
  }
140
- const created = deps.createTask({
140
+ const created = await deps.createTask({
141
141
  execution_mode: "execute",
142
142
  assessment_id: String(assessment.assessment_id || ""),
143
143
  });
@@ -293,7 +293,7 @@ async function runDirectVerification(lineageId, normalized, repoPath, deps) {
293
293
  }
294
294
  let sessionId = "";
295
295
  try {
296
- const session = deps.createDirectSession({
296
+ const session = await deps.createDirectSession({
297
297
  repo_path: repoPath,
298
298
  title: `Direct verification for ${lineageId}`,
299
299
  });
@@ -303,7 +303,7 @@ async function runDirectVerification(lineageId, normalized, repoPath, deps) {
303
303
  commands: normalized.direct_verify_commands,
304
304
  timeout_seconds: normalized.direct_verify_timeout_seconds,
305
305
  });
306
- const finalized = deps.safeFinalizeDirectSession(sessionId, { max_items: 8 });
306
+ const finalized = await deps.safeFinalizeDirectSession(sessionId, { max_items: 8 });
307
307
  const audit = deps.safeAuditDirectSession(sessionId, { max_items: 8 });
308
308
  const evidence = {
309
309
  session_id: sessionId,