patchwarden 1.1.0 → 1.5.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 (70) hide show
  1. package/README.en.md +41 -7
  2. package/README.md +36 -9
  3. package/dist/controlCenter.js +197 -1
  4. package/dist/direct/directSessionStore.d.ts +2 -0
  5. package/dist/direct/directVerification.js +7 -0
  6. package/dist/doctor.js +1 -1
  7. package/dist/policy/projectPolicy.d.ts +55 -0
  8. package/dist/policy/projectPolicy.js +286 -0
  9. package/dist/smoke-test.js +8 -8
  10. package/dist/test/unit/evidence-pack.test.d.ts +1 -0
  11. package/dist/test/unit/evidence-pack.test.js +130 -0
  12. package/dist/test/unit/project-policy-release-mode.test.d.ts +1 -0
  13. package/dist/test/unit/project-policy-release-mode.test.js +125 -0
  14. package/dist/test/unit/run-task-loop.test.d.ts +1 -0
  15. package/dist/test/unit/run-task-loop.test.js +380 -0
  16. package/dist/test/unit/schema-drift-check.test.js +10 -9
  17. package/dist/tools/evidencePack.d.ts +39 -0
  18. package/dist/tools/evidencePack.js +168 -0
  19. package/dist/tools/recommendAgentForTask.d.ts +19 -0
  20. package/dist/tools/recommendAgentForTask.js +56 -0
  21. package/dist/tools/registry.js +376 -2
  22. package/dist/tools/releaseMode.d.ts +50 -0
  23. package/dist/tools/releaseMode.js +370 -0
  24. package/dist/tools/runDirectVerificationBundle.d.ts +26 -0
  25. package/dist/tools/runDirectVerificationBundle.js +64 -0
  26. package/dist/tools/runTaskLoop.d.ts +57 -0
  27. package/dist/tools/runTaskLoop.js +417 -0
  28. package/dist/tools/runVerification.d.ts +4 -0
  29. package/dist/tools/runVerification.js +4 -0
  30. package/dist/tools/safeViews.d.ts +6 -0
  31. package/dist/tools/safeViews.js +2 -0
  32. package/dist/tools/taskLineage.d.ts +91 -0
  33. package/dist/tools/taskLineage.js +175 -0
  34. package/dist/tools/toolCatalog.d.ts +2 -2
  35. package/dist/tools/toolCatalog.js +6 -0
  36. package/dist/tools/toolRegistry.js +110 -0
  37. package/dist/version.d.ts +2 -2
  38. package/dist/version.js +2 -2
  39. package/docs/chatgpt-usage.md +31 -0
  40. package/docs/control-center/README.md +9 -0
  41. package/package.json +2 -2
  42. package/scripts/checks/control-center-smoke.js +87 -0
  43. package/scripts/checks/control-smoke.js +2 -2
  44. package/scripts/checks/mcp-manifest-check.js +12 -0
  45. package/scripts/checks/mcp-smoke.js +31 -7
  46. package/scripts/checks/watcher-supervisor-smoke.js +1 -1
  47. package/src/controlCenter.ts +198 -1
  48. package/src/direct/directSessionStore.ts +2 -0
  49. package/src/direct/directVerification.ts +7 -0
  50. package/src/doctor.ts +1 -1
  51. package/src/policy/projectPolicy.ts +344 -0
  52. package/src/smoke-test.ts +5 -5
  53. package/src/test/unit/evidence-pack.test.ts +142 -0
  54. package/src/test/unit/project-policy-release-mode.test.ts +156 -0
  55. package/src/test/unit/run-task-loop.test.ts +425 -0
  56. package/src/test/unit/schema-drift-check.test.ts +11 -9
  57. package/src/tools/evidencePack.ts +205 -0
  58. package/src/tools/listWorkspace.ts +71 -71
  59. package/src/tools/recommendAgentForTask.ts +79 -0
  60. package/src/tools/registry.ts +405 -2
  61. package/src/tools/releaseMode.ts +450 -0
  62. package/src/tools/runDirectVerificationBundle.ts +98 -0
  63. package/src/tools/runTaskLoop.ts +526 -0
  64. package/src/tools/runVerification.ts +8 -0
  65. package/src/tools/safeViews.ts +2 -0
  66. package/src/tools/taskLineage.ts +300 -0
  67. package/src/tools/toolCatalog.ts +6 -0
  68. package/src/tools/toolRegistry.ts +110 -0
  69. package/src/version.ts +2 -2
  70. package/ui/pages/dashboard.html +143 -2
@@ -0,0 +1,56 @@
1
+ import { getConfig } from "../config.js";
2
+ import { guardWorkspacePath } from "../security/pathGuard.js";
3
+ import { routeAgent } from "../agents/agentRouter.js";
4
+ export function recommendAgentForTask(input) {
5
+ const config = getConfig();
6
+ const repoPath = String(input.repo_path || "").trim();
7
+ const goal = String(input.goal || "").trim();
8
+ if (!repoPath)
9
+ throw new Error("repo_path is required.");
10
+ if (!goal)
11
+ throw new Error("goal is required.");
12
+ const resolvedRepoPath = guardWorkspacePath(repoPath, config.workspaceRoot);
13
+ const configuredAgents = Object.keys(config.agents);
14
+ const route = routeAgent({
15
+ goal,
16
+ scope: normalizeScope(input.scope_files),
17
+ inline_plan: input.risk_hint,
18
+ template: input.template,
19
+ configuredAgents,
20
+ });
21
+ return {
22
+ repo_path: repoPath,
23
+ resolved_repo_path: resolvedRepoPath,
24
+ recommended_agent: route.recommended_agent,
25
+ fallback_agent: route.fallback ? route.recommended_agent : configuredAgents.find((name) => name !== route.recommended_agent) || null,
26
+ fallback: route.fallback,
27
+ reason: route.reason,
28
+ risk_notes: buildRiskNotes(goal, input.risk_hint),
29
+ suggested_verify_commands: suggestVerifyCommands(config.allowedTestCommands),
30
+ bounded: true,
31
+ };
32
+ }
33
+ function normalizeScope(value) {
34
+ if (!Array.isArray(value))
35
+ return undefined;
36
+ return value.map((entry) => String(entry).trim()).filter(Boolean).slice(0, 50);
37
+ }
38
+ function buildRiskNotes(goal, riskHint) {
39
+ const text = `${goal} ${riskHint || ""}`.toLowerCase();
40
+ const notes = [];
41
+ if (/release|publish|push|tag|npm/.test(text)) {
42
+ notes.push("release_or_remote_write_language_detected");
43
+ }
44
+ if (/secret|token|cookie|\.env|ssh/.test(text)) {
45
+ notes.push("sensitive_file_language_detected");
46
+ }
47
+ if (/refactor|rewrite|redesign|migration/.test(text)) {
48
+ notes.push("broad_change_language_detected");
49
+ }
50
+ return notes.slice(0, 8);
51
+ }
52
+ function suggestVerifyCommands(commands) {
53
+ const preferred = ["npm test", "npm run test", "npm run build", "npm run lint"];
54
+ const configured = new Set(commands);
55
+ return preferred.filter((command) => configured.has(command)).slice(0, 4);
56
+ }
@@ -21,6 +21,10 @@ import { listAgents } from "../tools/listAgents.js";
21
21
  import { healthCheck } from "../tools/healthCheck.js";
22
22
  import { getTaskSummary } from "../tools/getTaskSummary.js";
23
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";
24
28
  import { errorPayload, PatchWardenError } from "../errors.js";
25
29
  import { auditTask } from "../tools/auditTask.js";
26
30
  import { safeStatus } from "../tools/safeStatus.js";
@@ -36,6 +40,7 @@ import { createDirectSession } from "../tools/createDirectSession.js";
36
40
  import { searchWorkspace } from "../tools/searchWorkspace.js";
37
41
  import { applyPatch } from "../tools/applyPatch.js";
38
42
  import { runVerification } from "../tools/runVerification.js";
43
+ import { runDirectVerificationBundle } from "../tools/runDirectVerificationBundle.js";
39
44
  import { finalizeDirectSession } from "../tools/finalizeDirectSession.js";
40
45
  import { auditSession } from "../tools/auditSession.js";
41
46
  import { syncFile } from "../tools/syncFile.js";
@@ -45,6 +50,7 @@ import { exportHandoff } from "../goal/handoffExport.js";
45
50
  import { acceptSubgoal, rejectSubgoal, summarizeGoalProgress } from "../goal/goalProgress.js";
46
51
  import { createSubgoalTask } from "./goalSubgoalTask.js";
47
52
  import { checkReleaseGate } from "./checkReleaseGate.js";
53
+ import { getProjectPolicyTool, releaseCheck, releaseCleanup, releasePrepare, releaseVerify, } from "./releaseMode.js";
48
54
  import { mergeWorktreeTool } from "./mergeWorktree.js";
49
55
  import { discardWorktreeTool } from "./discardWorktree.js";
50
56
  import { TASK_TEMPLATE_NAMES } from "./taskTemplates.js";
@@ -52,10 +58,12 @@ import { buildToolCatalogSnapshot, getLastToolCatalogSnapshot, resolveToolProfil
52
58
  export function getToolDefs() {
53
59
  const config = getConfig();
54
60
  const agentNames = Object.keys(config.agents).sort();
61
+ const routableAgentNames = [...agentNames, "auto"];
55
62
  const agentDescription = agentNames.length > 0
56
- ? `Configured local agent name. Available agents: ${agentNames.map((name) => JSON.stringify(name)).join(", ")}`
63
+ ? `Configured local agent name. Available agents: ${agentNames.map((name) => JSON.stringify(name)).join(", ")}. run_task_loop also accepts "auto" for bounded routing.`
57
64
  : "Configured local agent name. No agents are currently configured.";
58
65
  const testCommands = getAllConfiguredTestCommands(config).sort();
66
+ const directCommands = getAllConfiguredDirectCommands(config);
59
67
  const tools = [
60
68
  {
61
69
  name: "save_plan",
@@ -165,6 +173,173 @@ export function getToolDefs() {
165
173
  required: [],
166
174
  },
167
175
  },
176
+ {
177
+ name: "run_task_loop",
178
+ description: "Run a guarded PatchWarden task loop by composing create_task, wait_for_task, safe summaries, and audit_task. It does not bypass the watcher, command allow-list, workspace confinement, or confirmation boundaries. Returns only bounded structured lineage and final status.",
179
+ inputSchema: {
180
+ type: "object",
181
+ properties: {
182
+ repo_path: {
183
+ type: "string",
184
+ description: "Required repository path inside workspaceRoot. No implicit workspace-root fallback is allowed.",
185
+ },
186
+ goal: { type: "string", description: "Task goal to execute through the guarded loop." },
187
+ verify_commands: {
188
+ type: "array",
189
+ minItems: 1,
190
+ maxItems: 20,
191
+ items: {
192
+ type: "string",
193
+ ...(testCommands.length > 0 ? { enum: testCommands } : {}),
194
+ },
195
+ description: "Exact-match verification commands. The loop reuses create_task validation and will not run commands outside the allow-list.",
196
+ },
197
+ agent: {
198
+ type: "string",
199
+ description: agentDescription,
200
+ ...(agentNames.length > 0 ? { enum: routableAgentNames } : {}),
201
+ },
202
+ template: {
203
+ type: "string",
204
+ enum: ["inspect_only", "feature_small", "release_check"],
205
+ default: "feature_small",
206
+ description: "Initial guarded task template. Follow-up repair tasks use fix_tests automatically when verification fails.",
207
+ },
208
+ max_iterations: {
209
+ type: "integer",
210
+ minimum: 1,
211
+ maximum: 5,
212
+ default: 3,
213
+ description: "Maximum total main/fix task attempts.",
214
+ },
215
+ task_timeout_seconds: {
216
+ type: "integer",
217
+ minimum: 1,
218
+ maximum: config.maxTaskTimeoutSeconds,
219
+ default: config.defaultTaskTimeoutSeconds,
220
+ description: `Per-task wait budget in seconds (default ${config.defaultTaskTimeoutSeconds}, max ${config.maxTaskTimeoutSeconds}).`,
221
+ },
222
+ auto_fix_tests: {
223
+ type: "boolean",
224
+ default: true,
225
+ description: "When true, create a fix_tests follow-up task after failed_verification until max_iterations is reached.",
226
+ },
227
+ auto_cleanup_artifacts: {
228
+ type: "boolean",
229
+ default: true,
230
+ description: "Records cleanup intent in lineage. Low-risk cleanup remains handled by runTask post-task cleanup.",
231
+ },
232
+ stop_on_high_risk: {
233
+ type: "boolean",
234
+ default: true,
235
+ description: "When true, stop immediately on non-policy high-risk audit evidence. Scope, policy, sensitive-path, release/publish, and confirmation boundaries always stop regardless of this flag.",
236
+ },
237
+ direct_verify: {
238
+ type: "boolean",
239
+ default: false,
240
+ description: "When true, run an independent Direct verification session after the guarded task/audit succeeds. Direct never patches files in this loop.",
241
+ },
242
+ direct_verify_commands: {
243
+ type: "array",
244
+ minItems: 1,
245
+ maxItems: 20,
246
+ items: {
247
+ type: "string",
248
+ ...(directCommands.length > 0 ? { enum: directCommands } : {}),
249
+ },
250
+ description: "Optional Direct verification commands. Defaults to verify_commands and still must pass the Direct command allow-list.",
251
+ },
252
+ direct_verify_timeout_seconds: {
253
+ type: "integer",
254
+ minimum: 1,
255
+ maximum: Math.min(config.maxTaskTimeoutSeconds, config.directSessionTtlSeconds),
256
+ default: 120,
257
+ description: "Per-command timeout for Direct verification.",
258
+ },
259
+ scope_files: {
260
+ type: "array",
261
+ maxItems: 50,
262
+ items: { type: "string" },
263
+ description: "Optional bounded file scope used only for agent routing hints.",
264
+ },
265
+ isolation_mode: {
266
+ type: "string",
267
+ enum: ["current_repo", "worktree"],
268
+ default: "current_repo",
269
+ description: "Use current_repo by default. worktree creates an isolated git worktree for the task but never auto-merges it.",
270
+ },
271
+ worktree_base_branch: {
272
+ type: "string",
273
+ description: "Optional base-branch label recorded in lineage. v1.5 does not auto-checkout or merge this branch.",
274
+ },
275
+ worktree_cleanup: {
276
+ type: "string",
277
+ enum: ["keep", "archive", "delete_ignored_only"],
278
+ default: "keep",
279
+ description: "Cleanup intent recorded in lineage. v1.5 keeps worktrees by default and does not auto-delete them.",
280
+ },
281
+ },
282
+ required: ["repo_path", "goal", "verify_commands"],
283
+ },
284
+ },
285
+ {
286
+ name: "recommend_agent_for_task",
287
+ description: "Return a bounded read-only agent routing recommendation for a repo-scoped task. Does not start an agent, create a task, or read logs.",
288
+ inputSchema: {
289
+ type: "object",
290
+ properties: {
291
+ repo_path: { type: "string", description: "Repository path inside workspaceRoot." },
292
+ goal: { type: "string", description: "Task goal used for routing hints." },
293
+ scope_files: {
294
+ type: "array",
295
+ maxItems: 50,
296
+ items: { type: "string" },
297
+ description: "Optional bounded list of files or directories expected to be in scope.",
298
+ },
299
+ template: { type: "string", description: "Optional task template hint." },
300
+ risk_hint: { type: "string", description: "Optional compact risk hint text." },
301
+ },
302
+ required: ["repo_path", "goal"],
303
+ },
304
+ },
305
+ {
306
+ name: "get_task_lineage",
307
+ description: "Read a bounded safe summary for a run_task_loop lineage. Does not return full logs, diffs, stdout, stderr, or markdown artifacts.",
308
+ inputSchema: {
309
+ type: "object",
310
+ properties: {
311
+ lineage_id: { type: "string", description: "Lineage ID returned by run_task_loop." },
312
+ max_items: { type: "integer", minimum: 1, maximum: 50, default: 8, description: "Maximum rounds/tasks/warnings to return." },
313
+ },
314
+ required: ["lineage_id"],
315
+ },
316
+ },
317
+ {
318
+ name: "export_task_evidence_pack",
319
+ description: "Export a bounded evidence pack for a run_task_loop lineage. Writes evidence.json and EVIDENCE.md without stdout, stderr, full logs, full diff, or sensitive file contents.",
320
+ inputSchema: {
321
+ type: "object",
322
+ properties: {
323
+ lineage_id: { type: "string", description: "Lineage ID returned by run_task_loop." },
324
+ max_items: { type: "integer", minimum: 1, maximum: 50, default: 12, description: "Maximum rounds/tasks/warnings to include." },
325
+ },
326
+ required: ["lineage_id"],
327
+ },
328
+ },
329
+ {
330
+ name: "get_project_policy",
331
+ description: "Read the bounded effective .patchwarden/project-policy.json summary for a repository. Missing policy returns safe defaults. Project policy never expands PatchWarden command allow-lists, workspace confinement, sensitive-path blocking, or watcher/audit boundaries.",
332
+ inputSchema: {
333
+ type: "object",
334
+ properties: {
335
+ repo_path: {
336
+ type: "string",
337
+ description: "Repository path inside workspaceRoot.",
338
+ },
339
+ },
340
+ required: ["repo_path"],
341
+ },
342
+ },
168
343
  {
169
344
  name: "get_task_status",
170
345
  description: "Check task status, execution phase, watcher health, pending reason, current command, timeout, and change evidence.",
@@ -721,6 +896,87 @@ export function getToolDefs() {
721
896
  required: ["repo_path", "target_stage"],
722
897
  },
723
898
  },
899
+ {
900
+ name: "release_check",
901
+ description: "v1.3.0: Run a bounded release readiness check by wrapping the existing release gate. Local stages use existing guarded release-gate commands; remote stages are read-only. Does not publish, push, tag, or create a GitHub Release.",
902
+ inputSchema: {
903
+ type: "object",
904
+ properties: {
905
+ repo_path: { type: "string", description: "Repository path inside workspaceRoot." },
906
+ target_stage: {
907
+ type: "string",
908
+ enum: ["local_ready", "packed_ready", "published_verified", "github_release_verified", "ci_verified"],
909
+ default: "local_ready",
910
+ description: "Release gate stage to check. Defaults to local_ready.",
911
+ },
912
+ package_name: { type: "string", description: "npm package name for remote verification." },
913
+ version: { type: "string", description: "Version to verify. Defaults to project policy/package.json when omitted." },
914
+ github_repo: { type: "string", description: "GitHub repo in owner/repo form for release/CI verification." },
915
+ branch: { type: "string", default: "main", description: "Branch for CI verification." },
916
+ },
917
+ required: ["repo_path"],
918
+ },
919
+ },
920
+ {
921
+ name: "release_prepare",
922
+ description: "v1.3.0: Run project-policy release preparation commands only when each command is already accepted by the existing PatchWarden command guard. Returns command status only, never stdout/stderr. Does not publish, push, tag, or create a GitHub Release.",
923
+ inputSchema: {
924
+ type: "object",
925
+ properties: {
926
+ repo_path: { type: "string", description: "Repository path inside workspaceRoot." },
927
+ required_commands: {
928
+ type: "array",
929
+ maxItems: 10,
930
+ items: {
931
+ type: "string",
932
+ ...(testCommands.length > 0 ? { enum: testCommands } : {}),
933
+ },
934
+ description: "Optional exact-match release preparation commands. Defaults to project policy release_mode.required_commands.",
935
+ },
936
+ timeout_seconds: {
937
+ type: "integer",
938
+ minimum: 1,
939
+ maximum: config.maxTaskTimeoutSeconds,
940
+ default: 300,
941
+ description: "Per-command timeout in seconds.",
942
+ },
943
+ },
944
+ required: ["repo_path"],
945
+ },
946
+ },
947
+ {
948
+ name: "release_verify",
949
+ description: "v1.3.0: Verify npm/GitHub/CI release facts with read-only HTTPS requests. Does not run local shell commands and does not publish, push, tag, or create a GitHub Release.",
950
+ inputSchema: {
951
+ type: "object",
952
+ properties: {
953
+ repo_path: { type: "string", description: "Repository path inside workspaceRoot." },
954
+ package_name: { type: "string", description: "npm package name. Defaults to package.json name." },
955
+ version: { type: "string", description: "Version to verify. Defaults to project policy/package.json." },
956
+ github_repo: { type: "string", description: "GitHub repo in owner/repo form. Defaults to package.json repository when available." },
957
+ branch: { type: "string", default: "main", description: "Branch for CI verification." },
958
+ },
959
+ required: ["repo_path"],
960
+ },
961
+ },
962
+ {
963
+ name: "release_cleanup",
964
+ description: "v1.3.0: Clean up release artifacts using project-policy auto_cleanup rules. Defaults to dry_run=true. Non-dry-run cleanup only removes low-risk ignored/untracked artifacts under repo_path and writes an audit summary.",
965
+ inputSchema: {
966
+ type: "object",
967
+ properties: {
968
+ repo_path: { type: "string", description: "Repository path inside workspaceRoot." },
969
+ dry_run: { type: "boolean", default: true, description: "Preview cleanup by default. Set false to remove eligible artifacts." },
970
+ patterns: {
971
+ type: "array",
972
+ maxItems: 20,
973
+ items: { type: "string" },
974
+ description: "Optional cleanup patterns. Defaults to project policy auto_cleanup.patterns.",
975
+ },
976
+ },
977
+ required: ["repo_path"],
978
+ },
979
+ },
724
980
  {
725
981
  name: "merge_worktree",
726
982
  description: "v1.0.0: Merge an isolated git worktree's changes back into the main workspace. Use after a subgoal task (created with isolate_worktree=true) is accepted. Updates worktree_status.json to status='merged'. Merge failures do NOT delete the worktree (preserved for manual inspection).",
@@ -747,7 +1003,6 @@ export function getToolDefs() {
747
1003
  },
748
1004
  ];
749
1005
  // Direct session tools
750
- const directCommands = getAllConfiguredDirectCommands(config);
751
1006
  tools.push({
752
1007
  name: "create_direct_session",
753
1008
  description: "Create a Direct editing session for ChatGPT to apply patches directly. Requires enableDirectProfile: true in config.",
@@ -829,6 +1084,28 @@ export function getToolDefs() {
829
1084
  required: ["session_id", "command"],
830
1085
  },
831
1086
  });
1087
+ tools.push({
1088
+ name: "run_direct_verification_bundle",
1089
+ description: "Run multiple allowlisted Direct verification commands sequentially and return only bounded structured status. Omits stdout/stderr tails and log content.",
1090
+ inputSchema: {
1091
+ type: "object",
1092
+ properties: {
1093
+ session_id: { type: "string", description: "Direct session ID" },
1094
+ commands: {
1095
+ type: "array",
1096
+ minItems: 1,
1097
+ maxItems: 20,
1098
+ items: {
1099
+ type: "string",
1100
+ ...(directCommands.length > 0 ? { enum: directCommands } : {}),
1101
+ },
1102
+ description: "Verification commands to run in order. Each command must be accepted by the existing Direct command guard.",
1103
+ },
1104
+ timeout_seconds: { type: "integer", minimum: 1, maximum: Math.min(config.maxTaskTimeoutSeconds, config.directSessionTtlSeconds), default: 120 },
1105
+ },
1106
+ required: ["session_id", "commands"],
1107
+ },
1108
+ });
832
1109
  tools.push({
833
1110
  name: "finalize_direct_session",
834
1111
  description: "Finalize a Direct session: capture after snapshot, generate diff/summary/change artifacts, mark session as finalized. Must be called before audit_session.",
@@ -980,6 +1257,60 @@ async function handleToolCallInternal(name, args) {
980
1257
  assessment_id: args?.assessment_id ? String(args.assessment_id) : undefined,
981
1258
  }));
982
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
+ }
983
1314
  case "get_task_status": {
984
1315
  return toResult(getTaskStatus(String(args?.task_id ?? "")));
985
1316
  }
@@ -1171,6 +1502,14 @@ async function handleToolCallInternal(name, args) {
1171
1502
  timeout_seconds: args?.timeout_seconds ? Number(args.timeout_seconds) : undefined,
1172
1503
  }));
1173
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
+ }
1174
1513
  case "finalize_direct_session": {
1175
1514
  guardDirectProfileEnabled();
1176
1515
  return toResult(finalizeDirectSession({
@@ -1267,6 +1606,41 @@ async function handleToolCallInternal(name, args) {
1267
1606
  branch: args?.branch ? String(args.branch) : undefined,
1268
1607
  }));
1269
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
+ }
1270
1644
  case "merge_worktree": {
1271
1645
  return toResult(mergeWorktreeTool({
1272
1646
  worktree_id: String(args?.worktree_id ?? ""),
@@ -0,0 +1,50 @@
1
+ import { type ReleaseStage } from "../release/releaseGate.js";
2
+ import { type ProjectPolicySummary } from "../policy/projectPolicy.js";
3
+ export interface ReleaseCheckInput {
4
+ repo_path: string;
5
+ target_stage?: ReleaseStage;
6
+ package_name?: string;
7
+ version?: string;
8
+ github_repo?: string;
9
+ branch?: string;
10
+ }
11
+ export interface ReleasePrepareInput {
12
+ repo_path: string;
13
+ required_commands?: string[];
14
+ timeout_seconds?: number;
15
+ }
16
+ export interface ReleaseVerifyInput {
17
+ repo_path: string;
18
+ package_name?: string;
19
+ version?: string;
20
+ github_repo?: string;
21
+ branch?: string;
22
+ }
23
+ export interface ReleaseCleanupInput {
24
+ repo_path: string;
25
+ dry_run?: boolean;
26
+ patterns?: string[];
27
+ }
28
+ export interface ReleaseCommandResult {
29
+ command: string;
30
+ status: "passed" | "failed" | "blocked";
31
+ exit_code: number | null;
32
+ reason: string | null;
33
+ }
34
+ export interface ReleaseModeResult {
35
+ ok: boolean;
36
+ mode: "release_check" | "release_prepare" | "release_verify" | "release_cleanup";
37
+ repo_path: string;
38
+ resolved_repo_path: string;
39
+ policy: {
40
+ valid: boolean;
41
+ issue_count: number;
42
+ issues: ProjectPolicySummary["issues"];
43
+ };
44
+ summary: Record<string, unknown>;
45
+ }
46
+ export declare function getProjectPolicyTool(repoPath: string): ProjectPolicySummary;
47
+ export declare function releaseCheck(input: ReleaseCheckInput): Promise<ReleaseModeResult>;
48
+ export declare function releasePrepare(input: ReleasePrepareInput): ReleaseModeResult;
49
+ export declare function releaseVerify(input: ReleaseVerifyInput): Promise<ReleaseModeResult>;
50
+ export declare function releaseCleanup(input: ReleaseCleanupInput): ReleaseModeResult;