patchwarden 0.4.0 → 0.6.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.
- package/PatchWarden.cmd +51 -0
- package/README.en.md +1022 -0
- package/README.md +897 -358
- package/dist/assessments/agentAssessor.d.ts +15 -0
- package/dist/assessments/agentAssessor.js +293 -0
- package/dist/assessments/assessmentStore.d.ts +133 -0
- package/dist/assessments/assessmentStore.js +238 -0
- package/dist/assessments/confirmCli.d.ts +9 -0
- package/dist/assessments/confirmCli.js +26 -0
- package/dist/config.d.ts +22 -1
- package/dist/config.js +156 -3
- package/dist/direct/directAudit.d.ts +23 -0
- package/dist/direct/directAudit.js +309 -0
- package/dist/direct/directGuards.d.ts +20 -0
- package/dist/direct/directGuards.js +137 -0
- package/dist/direct/directPatch.d.ts +17 -0
- package/dist/direct/directPatch.js +113 -0
- package/dist/direct/directSessionStore.d.ts +63 -0
- package/dist/direct/directSessionStore.js +192 -0
- package/dist/direct/directVerification.d.ts +12 -0
- package/dist/direct/directVerification.js +96 -0
- package/dist/doctor.js +73 -5
- package/dist/logging.d.ts +52 -0
- package/dist/logging.js +123 -0
- package/dist/runner/agentInvocation.d.ts +23 -0
- package/dist/runner/agentInvocation.js +106 -0
- package/dist/runner/changeCapture.d.ts +70 -0
- package/dist/runner/changeCapture.js +278 -6
- package/dist/runner/runTask.js +271 -65
- package/dist/runner/simpleProcess.d.ts +21 -0
- package/dist/runner/simpleProcess.js +206 -0
- package/dist/runner/watch.js +17 -2
- package/dist/security/commandGuard.d.ts +2 -1
- package/dist/security/commandGuard.js +25 -3
- package/dist/security/riskEngine.d.ts +27 -0
- package/dist/security/riskEngine.js +118 -0
- package/dist/security/runtimeGuard.d.ts +6 -0
- package/dist/security/runtimeGuard.js +28 -0
- package/dist/smoke-test.js +1353 -7
- package/dist/test/unit/android-doctor.test.d.ts +1 -0
- package/dist/test/unit/android-doctor.test.js +118 -0
- package/dist/test/unit/chinese-path.test.d.ts +1 -0
- package/dist/test/unit/chinese-path.test.js +91 -0
- package/dist/test/unit/command-guard.test.d.ts +1 -0
- package/dist/test/unit/command-guard.test.js +160 -0
- package/dist/test/unit/direct-guards.test.d.ts +1 -0
- package/dist/test/unit/direct-guards.test.js +213 -0
- package/dist/test/unit/logging.test.d.ts +1 -0
- package/dist/test/unit/logging.test.js +275 -0
- package/dist/test/unit/path-guard.test.d.ts +1 -0
- package/dist/test/unit/path-guard.test.js +109 -0
- package/dist/test/unit/safe-status.test.d.ts +1 -0
- package/dist/test/unit/safe-status.test.js +165 -0
- package/dist/test/unit/sensitive-guard.test.d.ts +1 -0
- package/dist/test/unit/sensitive-guard.test.js +104 -0
- package/dist/test/unit/sync-file.test.d.ts +1 -0
- package/dist/test/unit/sync-file.test.js +154 -0
- package/dist/test/unit/watcher-status.test.d.ts +1 -0
- package/dist/test/unit/watcher-status.test.js +169 -0
- package/dist/tools/androidDoctor.d.ts +38 -0
- package/dist/tools/androidDoctor.js +391 -0
- package/dist/tools/applyPatch.d.ts +16 -0
- package/dist/tools/applyPatch.js +41 -0
- package/dist/tools/auditSession.d.ts +5 -0
- package/dist/tools/auditSession.js +12 -0
- package/dist/tools/auditTask.d.ts +7 -0
- package/dist/tools/auditTask.js +105 -7
- package/dist/tools/createDirectSession.d.ts +14 -0
- package/dist/tools/createDirectSession.js +54 -0
- package/dist/tools/createTask.d.ts +48 -1
- package/dist/tools/createTask.js +298 -47
- package/dist/tools/finalizeDirectSession.d.ts +19 -0
- package/dist/tools/finalizeDirectSession.js +84 -0
- package/dist/tools/getTaskSummary.d.ts +50 -0
- package/dist/tools/getTaskSummary.js +79 -2
- package/dist/tools/healthCheck.d.ts +27 -12
- package/dist/tools/healthCheck.js +44 -6
- package/dist/tools/readWorkspaceFile.d.ts +7 -1
- package/dist/tools/readWorkspaceFile.js +48 -2
- package/dist/tools/registry.js +270 -19
- package/dist/tools/runVerification.d.ts +16 -0
- package/dist/tools/runVerification.js +32 -0
- package/dist/tools/safeStatus.d.ts +19 -0
- package/dist/tools/safeStatus.js +72 -0
- package/dist/tools/savePlan.d.ts +1 -0
- package/dist/tools/savePlan.js +38 -7
- package/dist/tools/searchWorkspace.d.ts +19 -0
- package/dist/tools/searchWorkspace.js +205 -0
- package/dist/tools/syncFile.d.ts +18 -0
- package/dist/tools/syncFile.js +65 -0
- package/dist/tools/taskOutputs.d.ts +2 -2
- package/dist/tools/taskTemplates.js +2 -1
- package/dist/tools/toolCatalog.d.ts +4 -3
- package/dist/tools/toolCatalog.js +34 -5
- package/dist/tools/waitForTask.d.ts +2 -2
- package/dist/tools/waitForTask.js +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/watcherStatus.d.ts +1 -0
- package/dist/watcherStatus.js +96 -4
- package/docs/chatgpt-usage.md +101 -0
- package/docs/performance-notes.md +55 -0
- package/docs/release-checklist.md +14 -0
- package/docs/release-v0.6.0.md +71 -0
- package/docs/release-v0.6.1.md +75 -0
- package/examples/config.example.json +5 -0
- package/examples/openai-tunnel/README.md +11 -4
- package/examples/openai-tunnel/chatgpt-test-prompt.md +18 -14
- package/examples/openai-tunnel/tunnel-client.example.yaml +8 -0
- package/package.json +13 -8
- package/scripts/brand-check.js +58 -12
- package/scripts/control-smoke.js +206 -0
- package/scripts/http-mcp-smoke.js +10 -2
- package/scripts/launchers/Check-PatchWarden-Health.cmd +6 -0
- package/scripts/launchers/Reset-PatchWarden-Tunnel-Key.cmd +6 -0
- package/scripts/launchers/Restart-PatchWarden.cmd +6 -0
- package/scripts/launchers/Start-PatchWarden-Direct-Tunnel.cmd +7 -0
- package/scripts/launchers/Start-PatchWarden-Tunnel.cmd +7 -0
- package/scripts/lifecycle-smoke.js +402 -8
- package/scripts/manage-patchwarden.ps1 +639 -0
- package/scripts/mcp-manifest-check.js +123 -58
- package/scripts/mcp-smoke.js +160 -2
- package/scripts/pack-clean.js +160 -5
- package/scripts/package-manifest-check.js +78 -0
- package/scripts/patchwarden-mcp-direct.cmd +7 -0
- package/scripts/patchwarden-mcp-stdio.cmd +1 -1
- package/scripts/restart-patchwarden.ps1 +5 -4
- package/scripts/start-patchwarden-tunnel.ps1 +261 -30
- package/scripts/tunnel-supervisor-smoke.js +36 -4
- package/scripts/unit-tests.js +36 -0
- package/scripts/watcher-supervisor-smoke.js +10 -6
- package/src/assessments/agentAssessor.ts +324 -0
- package/src/assessments/assessmentStore.ts +426 -0
- package/src/assessments/confirmCli.ts +29 -0
- package/src/config.ts +178 -4
- package/src/direct/directAudit.ts +400 -0
- package/src/direct/directGuards.ts +279 -0
- package/src/direct/directPatch.ts +258 -0
- package/src/direct/directSessionStore.ts +345 -0
- package/src/direct/directVerification.ts +138 -0
- package/src/doctor.ts +103 -7
- package/src/logging.ts +152 -0
- package/src/runner/agentInvocation.ts +125 -0
- package/src/runner/changeCapture.ts +352 -6
- package/src/runner/runTask.ts +279 -63
- package/src/runner/simpleProcess.ts +223 -0
- package/src/runner/watch.ts +18 -2
- package/src/security/commandGuard.ts +46 -4
- package/src/security/riskEngine.ts +160 -0
- package/src/security/runtimeGuard.ts +41 -0
- package/src/smoke-test.ts +1291 -4
- package/src/test/unit/android-doctor.test.ts +158 -0
- package/src/test/unit/chinese-path.test.ts +106 -0
- package/src/test/unit/command-guard.test.ts +221 -0
- package/src/test/unit/direct-guards.test.ts +297 -0
- package/src/test/unit/logging.test.ts +325 -0
- package/src/test/unit/path-guard.test.ts +150 -0
- package/src/test/unit/safe-status.test.ts +187 -0
- package/src/test/unit/sensitive-guard.test.ts +124 -0
- package/src/test/unit/sync-file.test.ts +231 -0
- package/src/test/unit/watcher-status.test.ts +190 -0
- package/src/tools/androidDoctor.ts +424 -0
- package/src/tools/applyPatch.ts +86 -0
- package/src/tools/auditSession.ts +28 -0
- package/src/tools/auditTask.ts +111 -7
- package/src/tools/createDirectSession.ts +113 -0
- package/src/tools/createTask.ts +405 -55
- package/src/tools/finalizeDirectSession.ts +144 -0
- package/src/tools/getTaskSummary.ts +133 -2
- package/src/tools/healthCheck.ts +45 -6
- package/src/tools/readWorkspaceFile.ts +85 -2
- package/src/tools/registry.ts +305 -19
- package/src/tools/runVerification.ts +58 -0
- package/src/tools/safeStatus.ts +96 -0
- package/src/tools/savePlan.ts +57 -7
- package/src/tools/searchWorkspace.ts +275 -0
- package/src/tools/syncFile.ts +122 -0
- package/src/tools/taskTemplates.ts +2 -1
- package/src/tools/toolCatalog.ts +37 -6
- package/src/tools/waitForTask.ts +3 -3
- package/src/version.ts +2 -2
- package/src/watcherStatus.ts +101 -4
- package/tsconfig.json +18 -17
- package/Check-PatchWarden-Health.cmd +0 -6
- package/Reset-PatchWarden-Tunnel-Key.cmd +0 -6
- package/Restart-PatchWarden.cmd +0 -19
- package/Start-PatchWarden-Tunnel.cmd +0 -7
- package/docs/release-v0.3.0.md +0 -43
- package/docs/release-v0.4.0.md +0 -74
package/dist/tools/auditTask.js
CHANGED
|
@@ -55,6 +55,17 @@ export function auditTask(taskId) {
|
|
|
55
55
|
const checks = [];
|
|
56
56
|
const risks = [];
|
|
57
57
|
const actions = [];
|
|
58
|
+
const possibleFalsePositives = [];
|
|
59
|
+
const manualVerificationItems = [];
|
|
60
|
+
const addManualVerification = (item) => {
|
|
61
|
+
if (!manualVerificationItems.includes(item))
|
|
62
|
+
manualVerificationItems.push(item);
|
|
63
|
+
};
|
|
64
|
+
const addPossibleFalsePositive = (check, reason) => {
|
|
65
|
+
if (!possibleFalsePositives.some((item) => item.check === check && item.reason === reason)) {
|
|
66
|
+
possibleFalsePositives.push({ check, reason });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
58
69
|
// ── 1. Task status ──
|
|
59
70
|
const taskStatus = statusData.status || "unknown";
|
|
60
71
|
const failedStatuses = new Set(["failed", "failed_verification", "failed_scope_violation", "failed_policy_violation", "canceled"]);
|
|
@@ -63,6 +74,9 @@ export function auditTask(taskId) {
|
|
|
63
74
|
result: taskStatus === "done" ? "pass" : failedStatuses.has(taskStatus) ? "fail" : "warn",
|
|
64
75
|
detail: `Task status is "${taskStatus}".`,
|
|
65
76
|
});
|
|
77
|
+
if (taskStatus !== "done" && !failedStatuses.has(taskStatus)) {
|
|
78
|
+
addManualVerification(`Task status is "${taskStatus}"; audit evidence may be incomplete until terminal state.`);
|
|
79
|
+
}
|
|
66
80
|
// ── 2. result.md ──
|
|
67
81
|
const resultFile = join(taskDir, "result.md");
|
|
68
82
|
const hasResult = existsSync(resultFile);
|
|
@@ -85,6 +99,9 @@ export function auditTask(taskId) {
|
|
|
85
99
|
result: existsSync(verifyJsonFile) ? "pass" : "warn",
|
|
86
100
|
detail: existsSync(verifyJsonFile) ? "verify.json found." : "verify.json is missing.",
|
|
87
101
|
});
|
|
102
|
+
if (!existsSync(verifyJsonFile)) {
|
|
103
|
+
addManualVerification("verify.json is missing; determine whether independent verification was expected.");
|
|
104
|
+
}
|
|
88
105
|
if (existsSync(verifyJsonFile)) {
|
|
89
106
|
try {
|
|
90
107
|
const verify = JSON.parse(readFileSync(verifyJsonFile, "utf-8"));
|
|
@@ -98,14 +115,57 @@ export function auditTask(taskId) {
|
|
|
98
115
|
checks.push({ name: "verify_status", result: "fail", detail: "verify.json is invalid JSON." });
|
|
99
116
|
}
|
|
100
117
|
}
|
|
101
|
-
|
|
118
|
+
// Phase 4: Use new_out_of_scope_changes (task-caused) instead of out_of_scope_changes (all)
|
|
119
|
+
// Pre-existing external dirty files that didn't change during the task should NOT fail audit.
|
|
120
|
+
const newOutOfScope = Array.isArray(statusData.new_out_of_scope_changes)
|
|
121
|
+
? statusData.new_out_of_scope_changes
|
|
122
|
+
: Array.isArray(statusData.out_of_scope_changes)
|
|
123
|
+
? statusData.out_of_scope_changes
|
|
124
|
+
: [];
|
|
102
125
|
checks.push({
|
|
103
126
|
name: "scope_changes",
|
|
104
|
-
result:
|
|
105
|
-
detail:
|
|
106
|
-
? `${
|
|
107
|
-
: "No out-of-scope changes recorded.",
|
|
127
|
+
result: newOutOfScope.length > 0 ? "fail" : "pass",
|
|
128
|
+
detail: newOutOfScope.length > 0
|
|
129
|
+
? `${newOutOfScope.length} new out-of-scope change(s) detected during task execution.`
|
|
130
|
+
: "No new out-of-scope changes recorded.",
|
|
108
131
|
});
|
|
132
|
+
const changedFilesFile = join(taskDir, "changed-files.json");
|
|
133
|
+
if (existsSync(changedFilesFile)) {
|
|
134
|
+
try {
|
|
135
|
+
const changeEvidence = JSON.parse(readFileSync(changedFilesFile, "utf-8"));
|
|
136
|
+
const hygiene = changeEvidence.artifact_hygiene;
|
|
137
|
+
if (hygiene?.counts) {
|
|
138
|
+
const trackedArtifacts = Number(hygiene.counts.tracked_build_artifacts || 0);
|
|
139
|
+
const ignoredArtifacts = Number(hygiene.counts.ignored_untracked_artifacts || 0);
|
|
140
|
+
const runtimeFiles = Number(hygiene.counts.runtime_generated_files || 0);
|
|
141
|
+
const suspicious = Number(hygiene.counts.suspicious_changes || 0);
|
|
142
|
+
checks.push({
|
|
143
|
+
name: "artifact_hygiene",
|
|
144
|
+
result: suspicious > 0 ? "warn" : "pass",
|
|
145
|
+
detail: suspicious > 0
|
|
146
|
+
? `${suspicious} generated or artifact-like change(s) are tracked or not ignored and require review.`
|
|
147
|
+
: `${ignoredArtifacts} ignored artifact change(s) and ${runtimeFiles} runtime-generated change(s) are classified separately from source risk.`,
|
|
148
|
+
});
|
|
149
|
+
if (trackedArtifacts > 0) {
|
|
150
|
+
risks.push({ severity: "medium", description: `${trackedArtifacts} tracked build artifact change(s) require intentional source-control review.` });
|
|
151
|
+
addPossibleFalsePositive("artifact_hygiene", "Tracked build outputs may be intentional release assets rather than accidental source changes.");
|
|
152
|
+
}
|
|
153
|
+
if (suspicious > 0) {
|
|
154
|
+
actions.push("Review artifact_hygiene.suspicious_changes before accepting the task; add generated paths to Git ignore rules when appropriate.");
|
|
155
|
+
addPossibleFalsePositive("artifact_hygiene", "Artifact-like path classification is heuristic and may include intentionally maintained files.");
|
|
156
|
+
addManualVerification("Review artifact_hygiene.suspicious_changes and decide whether each path is intentional.");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
checks.push({ name: "artifact_hygiene", result: "warn", detail: "Change evidence uses the legacy format without artifact classification." });
|
|
161
|
+
addManualVerification("Legacy changed-files evidence has no artifact classification; inspect changed paths manually.");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
checks.push({ name: "artifact_hygiene", result: "warn", detail: "changed-files.json could not be parsed for artifact classification." });
|
|
166
|
+
addManualVerification("changed-files.json could not be parsed; inspect repository changes directly.");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
109
169
|
// ── 3. test.log ──
|
|
110
170
|
const testLogFile = join(taskDir, "test.log");
|
|
111
171
|
const hasTestLog = existsSync(testLogFile);
|
|
@@ -114,6 +174,8 @@ export function auditTask(taskId) {
|
|
|
114
174
|
result: hasTestLog ? "pass" : "warn",
|
|
115
175
|
detail: hasTestLog ? "test.log found." : "test.log is missing.",
|
|
116
176
|
});
|
|
177
|
+
if (!hasTestLog)
|
|
178
|
+
addManualVerification("test.log is missing; confirm whether the task required an agent-side test run.");
|
|
117
179
|
// ── 4. git.diff ──
|
|
118
180
|
const diffFile = join(taskDir, "git.diff");
|
|
119
181
|
checks.push({
|
|
@@ -121,6 +183,8 @@ export function auditTask(taskId) {
|
|
|
121
183
|
result: existsSync(diffFile) ? "pass" : "warn",
|
|
122
184
|
detail: existsSync(diffFile) ? "git.diff found." : "git.diff is missing.",
|
|
123
185
|
});
|
|
186
|
+
if (!existsSync(diffFile))
|
|
187
|
+
addManualVerification("git.diff is missing; inspect repository state before accepting code changes.");
|
|
124
188
|
// ── 5. repo_path consistency — use resolved_repo_path, NOT resolve() ──
|
|
125
189
|
let repoPathSafe = "";
|
|
126
190
|
let repoConsistent = false;
|
|
@@ -167,6 +231,7 @@ export function auditTask(taskId) {
|
|
|
167
231
|
}
|
|
168
232
|
catch {
|
|
169
233
|
checks.push({ name: "package_json_scripts", result: "warn", detail: "package.json exists but could not be read (may be sensitive)." });
|
|
234
|
+
addManualVerification("package.json could not be read, so documented commands were not fully cross-checked.");
|
|
170
235
|
}
|
|
171
236
|
}
|
|
172
237
|
// ── 7. Scan all docs (result.md, README.md, docs/**/*.md) ──
|
|
@@ -220,6 +285,8 @@ export function auditTask(taskId) {
|
|
|
220
285
|
description: `Command "npm run ${scriptName}" referenced in docs but not found in package.json scripts.`,
|
|
221
286
|
});
|
|
222
287
|
actions.push(`Verify whether "npm run ${scriptName}" should exist or if the agent fabricated it.`);
|
|
288
|
+
addPossibleFalsePositive(`npm_script_${scriptName}`, "Documentation may describe another package, historical version, or example command rather than the current package.json.");
|
|
289
|
+
addManualVerification(`Check whether documented command "npm run ${scriptName}" belongs to another package or should be added here.`);
|
|
223
290
|
}
|
|
224
291
|
}
|
|
225
292
|
if (allNpmRunRefs.size > 0 && pkgScripts.length > 0) {
|
|
@@ -244,6 +311,7 @@ export function auditTask(taskId) {
|
|
|
244
311
|
description: `${allReleaseClaims.length} remote publish/release/deploy claim(s) found in docs. PatchWarden cannot independently verify npm/GitHub actions. Manual confirmation required.`,
|
|
245
312
|
});
|
|
246
313
|
actions.push("Manually verify all npm publish / GitHub release / git tag claims before accepting the task as complete.");
|
|
314
|
+
addManualVerification("Verify remote npm, GitHub Release, and Git tag claims against authoritative remote services.");
|
|
247
315
|
}
|
|
248
316
|
// ── 9. test.log Exit code check ──
|
|
249
317
|
if (hasTestLog) {
|
|
@@ -254,6 +322,7 @@ export function auditTask(taskId) {
|
|
|
254
322
|
}
|
|
255
323
|
else if (statusData.test_command) {
|
|
256
324
|
checks.push({ name: "test_command_in_log", result: "warn", detail: `test.log does not clearly show "${statusData.test_command}".` });
|
|
325
|
+
addManualVerification("Confirm that test.log belongs to the configured test command.");
|
|
257
326
|
}
|
|
258
327
|
// Extract Exit code
|
|
259
328
|
const exitMatch = testLogContent.match(/Exit\s*code:\s*(\d+)/i);
|
|
@@ -271,6 +340,7 @@ export function auditTask(taskId) {
|
|
|
271
340
|
else if (statusData.test_command) {
|
|
272
341
|
checks.push({ name: "test_exit_code", result: "warn", detail: "test.log does not contain 'Exit code:' line — cannot verify test result." });
|
|
273
342
|
risks.push({ severity: "medium", description: "Test command was configured but test.log has no exit code." });
|
|
343
|
+
addManualVerification("The configured test command has no recorded exit code; rerun or inspect verification evidence.");
|
|
274
344
|
}
|
|
275
345
|
}
|
|
276
346
|
// ── 10. Summarize ──
|
|
@@ -278,8 +348,10 @@ export function auditTask(taskId) {
|
|
|
278
348
|
const warnCount = checks.filter((c) => c.result === "warn").length;
|
|
279
349
|
const passCount = checks.filter((c) => c.result === "pass").length;
|
|
280
350
|
const verdict = failCount > 0 ? "fail" : warnCount > 0 ? "warn" : "pass";
|
|
351
|
+
const confirmedFailures = checks.filter((check) => check.result === "fail");
|
|
281
352
|
let summary = `Audit complete: ${passCount} pass, ${warnCount} warn, ${failCount} fail across ${checks.length} checks. `;
|
|
282
|
-
summary += risks.length > 0 ? `${risks.length} risk(s) identified
|
|
353
|
+
summary += risks.length > 0 ? `${risks.length} risk(s) identified. ` : "No risks identified. ";
|
|
354
|
+
summary += `${confirmedFailures.length} confirmed failure(s), ${possibleFalsePositives.length} possible false-positive warning(s), and ${manualVerificationItems.length} manual verification item(s).`;
|
|
283
355
|
if (actions.length === 0) {
|
|
284
356
|
actions.push("No specific actions recommended.");
|
|
285
357
|
}
|
|
@@ -299,9 +371,35 @@ export function auditTask(taskId) {
|
|
|
299
371
|
"## Risks",
|
|
300
372
|
...risks.map((r) => `- [${r.severity}] ${r.description}`),
|
|
301
373
|
"",
|
|
374
|
+
"## Confirmed Failures",
|
|
375
|
+
...(confirmedFailures.length > 0
|
|
376
|
+
? confirmedFailures.map((check) => `- **${check.name}**: ${check.detail}`)
|
|
377
|
+
: ["- None."]),
|
|
378
|
+
"",
|
|
379
|
+
"## Possible False Positives",
|
|
380
|
+
...(possibleFalsePositives.length > 0
|
|
381
|
+
? possibleFalsePositives.map((item) => `- **${item.check}**: ${item.reason}`)
|
|
382
|
+
: ["- None identified."]),
|
|
383
|
+
"",
|
|
384
|
+
"## Manual Verification Required",
|
|
385
|
+
...(manualVerificationItems.length > 0
|
|
386
|
+
? manualVerificationItems.map((item) => `- ${item}`)
|
|
387
|
+
: ["- No additional manual verification identified by this audit."]),
|
|
388
|
+
"",
|
|
302
389
|
"## Recommended Actions",
|
|
303
390
|
...actions.map((a) => `- ${a}`),
|
|
304
391
|
].join("\n");
|
|
305
392
|
writeFileSync(join(taskDir, "independent-review.md"), reviewMd, "utf-8");
|
|
306
|
-
return {
|
|
393
|
+
return {
|
|
394
|
+
task_id: taskId,
|
|
395
|
+
verdict,
|
|
396
|
+
summary,
|
|
397
|
+
checks,
|
|
398
|
+
risks,
|
|
399
|
+
confirmed_failures: confirmedFailures,
|
|
400
|
+
possible_false_positives: possibleFalsePositives,
|
|
401
|
+
manual_verification_required: manualVerificationItems.length > 0,
|
|
402
|
+
manual_verification_items: manualVerificationItems,
|
|
403
|
+
recommended_next_actions: actions,
|
|
404
|
+
};
|
|
307
405
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface CreateDirectSessionInput {
|
|
2
|
+
repo_path: string;
|
|
3
|
+
title?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface CreateDirectSessionOutput {
|
|
6
|
+
session_id: string;
|
|
7
|
+
repo_path: string;
|
|
8
|
+
resolved_repo_path: string;
|
|
9
|
+
workspace_clean: boolean;
|
|
10
|
+
allowed_commands: string[];
|
|
11
|
+
expires_at: string;
|
|
12
|
+
next_action: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function createDirectSession(input: CreateDirectSessionInput): CreateDirectSessionOutput;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
import { getConfig } from "../config.js";
|
|
3
|
+
import { PatchWardenError } from "../errors.js";
|
|
4
|
+
import { guardWorkspacePath } from "../security/pathGuard.js";
|
|
5
|
+
import { guardRuntimeSelfModification } from "../security/runtimeGuard.js";
|
|
6
|
+
import { captureRepoSnapshot } from "../runner/changeCapture.js";
|
|
7
|
+
import { createDirectSession as createDirectSessionRecord, } from "../direct/directSessionStore.js";
|
|
8
|
+
// ── Tool implementation ────────────────────────────────────────────
|
|
9
|
+
export function createDirectSession(input) {
|
|
10
|
+
const config = getConfig();
|
|
11
|
+
// ── Validate repo_path ───────────────────────────────────────────
|
|
12
|
+
if (!input.repo_path || input.repo_path.trim() === "") {
|
|
13
|
+
throw new PatchWardenError("invalid_input", "repo_path is required and must be a non-empty string.", "Provide a repository path inside the configured workspaceRoot.", true, { operation: "create_direct_session" });
|
|
14
|
+
}
|
|
15
|
+
const resolvedRepoPath = guardWorkspacePath(input.repo_path, config.workspaceRoot);
|
|
16
|
+
// ── Verify existence ─────────────────────────────────────────────
|
|
17
|
+
if (!existsSync(resolvedRepoPath)) {
|
|
18
|
+
throw new PatchWardenError("repo_not_found", `repo_path "${input.repo_path}" does not exist (resolved: "${resolvedRepoPath}").`, "Provide a valid repository path inside the configured workspaceRoot.", true, {
|
|
19
|
+
repo_path: input.repo_path,
|
|
20
|
+
resolved_repo_path: resolvedRepoPath,
|
|
21
|
+
operation: "create_direct_session",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
// ── Verify it is a directory ─────────────────────────────────────
|
|
25
|
+
const stat = statSync(resolvedRepoPath);
|
|
26
|
+
if (!stat.isDirectory()) {
|
|
27
|
+
throw new PatchWardenError("repo_not_directory", `repo_path "${input.repo_path}" is not a directory.`, "Provide a directory path, not a file path.", true, {
|
|
28
|
+
repo_path: input.repo_path,
|
|
29
|
+
resolved_repo_path: resolvedRepoPath,
|
|
30
|
+
operation: "create_direct_session",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// ── Runtime self-modification protection ─────────────────────────
|
|
34
|
+
guardRuntimeSelfModification(resolvedRepoPath);
|
|
35
|
+
// ── Capture repo snapshot ────────────────────────────────────────
|
|
36
|
+
const snapshot = captureRepoSnapshot(resolvedRepoPath);
|
|
37
|
+
// ── Create session record ────────────────────────────────────────
|
|
38
|
+
const session = createDirectSessionRecord({
|
|
39
|
+
repo_path: input.repo_path,
|
|
40
|
+
resolved_repo_path: resolvedRepoPath,
|
|
41
|
+
title: input.title,
|
|
42
|
+
snapshot,
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
session_id: session.session_id,
|
|
46
|
+
repo_path: session.repo_path,
|
|
47
|
+
resolved_repo_path: session.resolved_repo_path,
|
|
48
|
+
workspace_clean: !snapshot.workspace_dirty,
|
|
49
|
+
allowed_commands: session.allowed_commands,
|
|
50
|
+
expires_at: session.expires_at,
|
|
51
|
+
next_action: "Use search_workspace/read_workspace_file, then apply_patch to make file changes within this session. " +
|
|
52
|
+
"After editing, call run_verification, finalize_direct_session, and audit_session.",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -9,12 +9,56 @@ export interface CreateTaskInput {
|
|
|
9
9
|
template?: TaskTemplateName;
|
|
10
10
|
goal?: string;
|
|
11
11
|
source_task_id?: string;
|
|
12
|
-
agent
|
|
12
|
+
agent?: string;
|
|
13
13
|
repo_path?: string;
|
|
14
14
|
test_command?: string;
|
|
15
15
|
verify_commands?: string[];
|
|
16
16
|
timeout_seconds?: number;
|
|
17
|
+
execution_mode?: "assess_only" | "execute";
|
|
18
|
+
assessment_id?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface AssessOnlyOutput {
|
|
21
|
+
assessment_id: string;
|
|
22
|
+
assessment_short_id: string;
|
|
23
|
+
decision: "allow" | "needs_confirm" | "blocked";
|
|
24
|
+
risk_level: "low" | "medium" | "high";
|
|
25
|
+
risk_hints: string[];
|
|
26
|
+
hard_rule_hits: string[];
|
|
27
|
+
reason_codes: string[];
|
|
28
|
+
expires_at: string;
|
|
29
|
+
requires_confirm: boolean;
|
|
30
|
+
workspace_snapshot_summary: {
|
|
31
|
+
head: string | null;
|
|
32
|
+
file_count: number;
|
|
33
|
+
workspace_dirty: boolean;
|
|
34
|
+
snapshot_truncated: boolean;
|
|
35
|
+
};
|
|
36
|
+
next_action: string;
|
|
37
|
+
next_tool_call?: {
|
|
38
|
+
name: "create_task";
|
|
39
|
+
arguments: {
|
|
40
|
+
execution_mode: "execute";
|
|
41
|
+
assessment_id: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
local_confirmation: {
|
|
45
|
+
required: boolean;
|
|
46
|
+
command: "patchwarden-confirm" | null;
|
|
47
|
+
arguments: string[];
|
|
48
|
+
};
|
|
49
|
+
suggestion?: string;
|
|
50
|
+
agent_assessment?: {
|
|
51
|
+
status: string;
|
|
52
|
+
merged_risk: string;
|
|
53
|
+
merged_decision: string;
|
|
54
|
+
confidence: number | null;
|
|
55
|
+
notes: string | null;
|
|
56
|
+
read_only_violation: boolean;
|
|
57
|
+
stdout_truncated: boolean;
|
|
58
|
+
stderr_truncated: boolean;
|
|
59
|
+
} | null;
|
|
17
60
|
}
|
|
61
|
+
export type CreateTaskResult = CreateTaskOutput | AssessOnlyOutput;
|
|
18
62
|
export interface CreateTaskOutput {
|
|
19
63
|
task_id: string;
|
|
20
64
|
plan_id: string;
|
|
@@ -39,4 +83,7 @@ export interface CreateTaskOutput {
|
|
|
39
83
|
arguments: Record<string, unknown>;
|
|
40
84
|
};
|
|
41
85
|
}
|
|
86
|
+
export declare function createTask(input: CreateTaskInput & {
|
|
87
|
+
execution_mode: "assess_only";
|
|
88
|
+
}): AssessOnlyOutput;
|
|
42
89
|
export declare function createTask(input: CreateTaskInput): CreateTaskOutput;
|