patchwarden 0.4.0 → 0.6.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.
- 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/runner/agentInvocation.d.ts +23 -0
- package/dist/runner/agentInvocation.js +106 -0
- package/dist/runner/changeCapture.d.ts +29 -0
- package/dist/runner/changeCapture.js +107 -5
- package/dist/runner/runTask.js +70 -44
- 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/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 +94 -2
- 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 +47 -0
- package/dist/tools/getTaskSummary.js +64 -1
- package/dist/tools/healthCheck.d.ts +22 -12
- package/dist/tools/healthCheck.js +23 -6
- package/dist/tools/readWorkspaceFile.d.ts +7 -1
- package/dist/tools/readWorkspaceFile.js +48 -2
- package/dist/tools/registry.js +217 -19
- package/dist/tools/runVerification.d.ts +16 -0
- package/dist/tools/runVerification.js +32 -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/taskTemplates.js +2 -1
- package/dist/tools/toolCatalog.d.ts +3 -2
- package/dist/tools/toolCatalog.js +32 -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/docs/chatgpt-usage.md +101 -0
- package/docs/release-checklist.md +14 -0
- package/docs/release-v0.6.0.md +71 -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 +12 -8
- package/scripts/brand-check.js +58 -12
- package/scripts/control-smoke.js +206 -0
- 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 +66 -6
- package/scripts/manage-patchwarden.ps1 +639 -0
- package/scripts/mcp-manifest-check.js +100 -58
- package/scripts/mcp-smoke.js +157 -2
- package/scripts/pack-clean.js +3 -4
- 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/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/runner/agentInvocation.ts +125 -0
- package/src/runner/changeCapture.ts +140 -5
- package/src/runner/runTask.ts +61 -43
- 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/tools/applyPatch.ts +86 -0
- package/src/tools/auditSession.ts +28 -0
- package/src/tools/auditTask.ts +100 -2
- 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 +111 -1
- package/src/tools/healthCheck.ts +23 -6
- package/src/tools/readWorkspaceFile.ts +85 -2
- package/src/tools/registry.ts +242 -19
- package/src/tools/runVerification.ts +58 -0
- package/src/tools/savePlan.ts +57 -7
- package/src/tools/searchWorkspace.ts +275 -0
- package/src/tools/taskTemplates.ts +2 -1
- package/src/tools/toolCatalog.ts +35 -6
- package/src/tools/waitForTask.ts +3 -3
- package/src/version.ts +2 -2
- 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
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
|
|
2
|
+
import { relative, join, extname } from "node:path";
|
|
3
|
+
import { getConfig } from "../config.js";
|
|
4
|
+
import { PatchWardenError } from "../errors.js";
|
|
5
|
+
import { isSensitivePath } from "../security/sensitiveGuard.js";
|
|
6
|
+
import {
|
|
7
|
+
readDirectSession,
|
|
8
|
+
validateDirectSessionFreshness,
|
|
9
|
+
} from "../direct/directSessionStore.js";
|
|
10
|
+
import { guardDirectSessionActive } from "../direct/directGuards.js";
|
|
11
|
+
|
|
12
|
+
// ── Types ──────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
export interface SearchWorkspaceInput {
|
|
15
|
+
session_id: string;
|
|
16
|
+
query: string;
|
|
17
|
+
max_results?: number;
|
|
18
|
+
case_sensitive?: boolean;
|
|
19
|
+
max_preview_chars?: number;
|
|
20
|
+
include_globs?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SearchMatch {
|
|
24
|
+
path: string;
|
|
25
|
+
line: number;
|
|
26
|
+
preview: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SearchWorkspaceOutput {
|
|
30
|
+
results: SearchMatch[];
|
|
31
|
+
total_matches: number;
|
|
32
|
+
truncated: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ── Constants ──────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
const SKIP_DIRECTORIES = new Set([
|
|
38
|
+
".git",
|
|
39
|
+
".patchwarden",
|
|
40
|
+
"node_modules",
|
|
41
|
+
"dist",
|
|
42
|
+
"release",
|
|
43
|
+
"coverage",
|
|
44
|
+
".next",
|
|
45
|
+
".turbo",
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
const BINARY_EXTENSIONS = new Set([
|
|
49
|
+
".exe",
|
|
50
|
+
".dll",
|
|
51
|
+
".so",
|
|
52
|
+
".png",
|
|
53
|
+
".jpg",
|
|
54
|
+
".jpeg",
|
|
55
|
+
".gif",
|
|
56
|
+
".bmp",
|
|
57
|
+
".ico",
|
|
58
|
+
".webp",
|
|
59
|
+
".pdf",
|
|
60
|
+
".mp3",
|
|
61
|
+
".mp4",
|
|
62
|
+
".zip",
|
|
63
|
+
".gz",
|
|
64
|
+
".tar",
|
|
65
|
+
".bin",
|
|
66
|
+
".dat",
|
|
67
|
+
".class",
|
|
68
|
+
".jar",
|
|
69
|
+
".wasm",
|
|
70
|
+
".node",
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
// Safety valve: stop scanning after this many total matches to prevent
|
|
74
|
+
// runaway searches in large repositories with very common terms.
|
|
75
|
+
const MAX_TOTAL_MATCHES = 10_000;
|
|
76
|
+
|
|
77
|
+
// ── Tool implementation ────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
export function searchWorkspace(
|
|
80
|
+
input: SearchWorkspaceInput
|
|
81
|
+
): SearchWorkspaceOutput {
|
|
82
|
+
const config = getConfig();
|
|
83
|
+
|
|
84
|
+
// ── Validate session freshness ───────────────────────────────────
|
|
85
|
+
|
|
86
|
+
const validation = validateDirectSessionFreshness(input.session_id);
|
|
87
|
+
if (!validation.valid || !validation.session) {
|
|
88
|
+
throw new PatchWardenError(
|
|
89
|
+
validation.failure_reason || "session_invalid",
|
|
90
|
+
`Direct session "${input.session_id}" is not valid: ${validation.failure_reason}.`,
|
|
91
|
+
"Create a new direct session with create_direct_session.",
|
|
92
|
+
true,
|
|
93
|
+
{
|
|
94
|
+
session_id: input.session_id,
|
|
95
|
+
failure_reason: validation.failure_reason,
|
|
96
|
+
operation: "search_workspace",
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const session = validation.session;
|
|
102
|
+
guardDirectSessionActive(session);
|
|
103
|
+
|
|
104
|
+
// ── Validate query ───────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
if (!input.query || input.query.trim() === "") {
|
|
107
|
+
throw new PatchWardenError(
|
|
108
|
+
"invalid_input",
|
|
109
|
+
"query is required and must be a non-empty string.",
|
|
110
|
+
"Provide a search query to look for in file contents.",
|
|
111
|
+
true,
|
|
112
|
+
{ operation: "search_workspace", session_id: input.session_id }
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Resolve parameters ───────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
const maxResults = input.max_results ?? 20;
|
|
119
|
+
const caseSensitive = input.case_sensitive ?? false;
|
|
120
|
+
const maxPreviewChars = input.max_preview_chars ?? 200;
|
|
121
|
+
const includeGlobs = input.include_globs;
|
|
122
|
+
const query = caseSensitive ? input.query : input.query.toLowerCase();
|
|
123
|
+
|
|
124
|
+
const repoPath = session.resolved_repo_path;
|
|
125
|
+
|
|
126
|
+
if (!existsSync(repoPath)) {
|
|
127
|
+
throw new PatchWardenError(
|
|
128
|
+
"repo_not_found",
|
|
129
|
+
`Session repo path "${repoPath}" no longer exists.`,
|
|
130
|
+
"Create a new direct session with create_direct_session.",
|
|
131
|
+
true,
|
|
132
|
+
{
|
|
133
|
+
session_id: input.session_id,
|
|
134
|
+
resolved_repo_path: repoPath,
|
|
135
|
+
operation: "search_workspace",
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── Recursive search ─────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
const results: SearchMatch[] = [];
|
|
143
|
+
let totalMatches = 0;
|
|
144
|
+
let truncated = false;
|
|
145
|
+
|
|
146
|
+
const visit = (directory: string): void => {
|
|
147
|
+
if (truncated && totalMatches >= MAX_TOTAL_MATCHES) return;
|
|
148
|
+
|
|
149
|
+
let entries;
|
|
150
|
+
try {
|
|
151
|
+
entries = readdirSync(directory, { withFileTypes: true });
|
|
152
|
+
} catch {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (const entry of entries) {
|
|
157
|
+
if (totalMatches >= MAX_TOTAL_MATCHES) {
|
|
158
|
+
truncated = true;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Skip blacklisted directories
|
|
163
|
+
if (entry.isDirectory() && SKIP_DIRECTORIES.has(entry.name)) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const fullPath = join(directory, entry.name);
|
|
168
|
+
const relPath = relative(repoPath, fullPath).replace(/\\/g, "/");
|
|
169
|
+
|
|
170
|
+
if (entry.isDirectory()) {
|
|
171
|
+
visit(fullPath);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!entry.isFile()) continue;
|
|
176
|
+
|
|
177
|
+
// Skip sensitive files
|
|
178
|
+
if (isSensitivePath(relPath)) continue;
|
|
179
|
+
|
|
180
|
+
// Skip binary files by extension
|
|
181
|
+
const ext = extname(entry.name).toLowerCase();
|
|
182
|
+
if (BINARY_EXTENSIONS.has(ext)) continue;
|
|
183
|
+
|
|
184
|
+
// Apply include_globs filter
|
|
185
|
+
if (includeGlobs && includeGlobs.length > 0) {
|
|
186
|
+
if (!matchesAnyGlob(relPath, includeGlobs)) continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Read file content and search line by line
|
|
190
|
+
let content: string;
|
|
191
|
+
try {
|
|
192
|
+
const fileStat = statSync(fullPath);
|
|
193
|
+
if (fileStat.size > config.directMaxFileBytes) continue;
|
|
194
|
+
content = readFileSync(fullPath, "utf-8");
|
|
195
|
+
} catch {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const lines = content.split(/\r?\n/);
|
|
200
|
+
for (let i = 0; i < lines.length; i++) {
|
|
201
|
+
if (totalMatches >= MAX_TOTAL_MATCHES) {
|
|
202
|
+
truncated = true;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const line = lines[i];
|
|
207
|
+
const haystack = caseSensitive ? line : line.toLowerCase();
|
|
208
|
+
if (haystack.includes(query)) {
|
|
209
|
+
totalMatches++;
|
|
210
|
+
if (results.length < maxResults) {
|
|
211
|
+
const preview =
|
|
212
|
+
line.length > maxPreviewChars
|
|
213
|
+
? line.slice(0, maxPreviewChars)
|
|
214
|
+
: line;
|
|
215
|
+
results.push({
|
|
216
|
+
path: relPath,
|
|
217
|
+
line: i + 1,
|
|
218
|
+
preview,
|
|
219
|
+
});
|
|
220
|
+
} else {
|
|
221
|
+
truncated = true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
visit(repoPath);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
results,
|
|
232
|
+
total_matches: totalMatches,
|
|
233
|
+
truncated,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ── Glob matching ──────────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
function matchesAnyGlob(filePath: string, globs: string[]): boolean {
|
|
240
|
+
for (const pattern of globs) {
|
|
241
|
+
if (matchGlob(filePath, pattern)) return true;
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Simple glob matching: "*" matches any sequence of characters (including
|
|
248
|
+
* path separators), "?" matches a single character.
|
|
249
|
+
*
|
|
250
|
+
* Examples:
|
|
251
|
+
* "*.ts" matches "foo.ts", "src/bar.ts"
|
|
252
|
+
* "src/*.ts" matches "src/foo.ts", "src/sub/bar.ts"
|
|
253
|
+
* "*.test.ts" matches "foo.test.ts", "src/bar.test.ts"
|
|
254
|
+
*/
|
|
255
|
+
function matchGlob(filePath: string, pattern: string): boolean {
|
|
256
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
257
|
+
const regex = globToRegex(normalizedPattern);
|
|
258
|
+
return regex.test(filePath);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function globToRegex(pattern: string): RegExp {
|
|
262
|
+
let regexStr = "";
|
|
263
|
+
for (const char of pattern) {
|
|
264
|
+
if (char === "*") {
|
|
265
|
+
regexStr += ".*";
|
|
266
|
+
} else if (char === "?") {
|
|
267
|
+
regexStr += ".";
|
|
268
|
+
} else if (/[.+^${}()|[\]\\]/.test(char)) {
|
|
269
|
+
regexStr += "\\" + char;
|
|
270
|
+
} else {
|
|
271
|
+
regexStr += char;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return new RegExp("^" + regexStr + "$");
|
|
275
|
+
}
|
|
@@ -42,7 +42,8 @@ export function expandTaskTemplate(input: TaskTemplateInput): ExpandedTaskTempla
|
|
|
42
42
|
`## Goal\n${goal}`,
|
|
43
43
|
"## Safety boundaries",
|
|
44
44
|
"- Work only inside the resolved repository path.",
|
|
45
|
-
"-
|
|
45
|
+
"- Leave repository changes uncommitted for review; remote operations are outside this task.",
|
|
46
|
+
"- Keep credentials private and do not modify unrelated files.",
|
|
46
47
|
"- Report exact files inspected or changed and any remaining uncertainty.",
|
|
47
48
|
];
|
|
48
49
|
|
package/src/tools/toolCatalog.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../version.js";
|
|
3
3
|
|
|
4
|
-
export type ToolProfile = "full" | "chatgpt_core";
|
|
4
|
+
export type ToolProfile = "full" | "chatgpt_core" | "chatgpt_direct";
|
|
5
5
|
|
|
6
6
|
export interface CatalogTool {
|
|
7
7
|
name: string;
|
|
@@ -37,22 +37,51 @@ export const CHATGPT_CORE_TOOL_NAMES = [
|
|
|
37
37
|
"audit_task",
|
|
38
38
|
] as const;
|
|
39
39
|
|
|
40
|
+
export const CHATGPT_DIRECT_TOOL_NAMES = [
|
|
41
|
+
"health_check",
|
|
42
|
+
"list_workspace",
|
|
43
|
+
"create_direct_session",
|
|
44
|
+
"search_workspace",
|
|
45
|
+
"read_workspace_file",
|
|
46
|
+
"apply_patch",
|
|
47
|
+
"run_verification",
|
|
48
|
+
"finalize_direct_session",
|
|
49
|
+
"audit_session",
|
|
50
|
+
] as const;
|
|
51
|
+
|
|
40
52
|
let lastSnapshot: ToolCatalogSnapshot | null = null;
|
|
41
53
|
|
|
42
54
|
export function resolveToolProfile(configProfile?: string): ToolProfile {
|
|
43
55
|
const raw = (process.env.PATCHWARDEN_TOOL_PROFILE || configProfile || "full").trim();
|
|
44
|
-
if (raw !== "full" && raw !== "chatgpt_core") {
|
|
45
|
-
throw new Error(`Invalid tool profile "${raw}". Expected "full" or "
|
|
56
|
+
if (raw !== "full" && raw !== "chatgpt_core" && raw !== "chatgpt_direct") {
|
|
57
|
+
throw new Error(`Invalid tool profile "${raw}". Expected "full", "chatgpt_core", or "chatgpt_direct".`);
|
|
46
58
|
}
|
|
47
59
|
return raw;
|
|
48
60
|
}
|
|
49
61
|
|
|
50
|
-
export function selectToolsForProfile<T extends CatalogTool>(tools: T[], profile: ToolProfile): T[] {
|
|
62
|
+
export function selectToolsForProfile<T extends CatalogTool>(tools: T[], profile: ToolProfile, enableDirectProfile?: boolean): T[] {
|
|
51
63
|
if (profile === "full") return tools;
|
|
52
64
|
const byName = new Map(tools.map((tool) => [tool.name, tool]));
|
|
53
|
-
|
|
65
|
+
|
|
66
|
+
if (profile === "chatgpt_core") {
|
|
67
|
+
return CHATGPT_CORE_TOOL_NAMES.map((name) => {
|
|
68
|
+
const tool = byName.get(name);
|
|
69
|
+
if (!tool) throw new Error(`chatgpt_core tool profile requires missing tool "${name}".`);
|
|
70
|
+
return tool;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// chatgpt_direct
|
|
75
|
+
if (!enableDirectProfile) {
|
|
76
|
+
// Degraded mode: only expose health_check for diagnostics
|
|
77
|
+
const tool = byName.get("health_check");
|
|
78
|
+
if (!tool) throw new Error(`chatgpt_direct degraded mode requires missing tool "health_check".`);
|
|
79
|
+
return [tool];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return CHATGPT_DIRECT_TOOL_NAMES.map((name) => {
|
|
54
83
|
const tool = byName.get(name);
|
|
55
|
-
if (!tool) throw new Error(`
|
|
84
|
+
if (!tool) throw new Error(`chatgpt_direct tool profile requires missing tool "${name}".`);
|
|
56
85
|
return tool;
|
|
57
86
|
});
|
|
58
87
|
}
|
package/src/tools/waitForTask.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
2
|
import { getTaskStatus } from "./getTaskStatus.js";
|
|
3
|
-
import { getTaskSummary, type
|
|
3
|
+
import { getTaskSummary, type TaskSummaryResult } from "./getTaskSummary.js";
|
|
4
4
|
|
|
5
5
|
const TERMINAL_STATUSES = new Set([
|
|
6
6
|
"done",
|
|
@@ -32,7 +32,7 @@ export interface WaitForTaskOutput {
|
|
|
32
32
|
name: "wait_for_task" | "audit_task" | "health_check";
|
|
33
33
|
arguments: Record<string, unknown>;
|
|
34
34
|
};
|
|
35
|
-
summary?:
|
|
35
|
+
summary?: TaskSummaryResult;
|
|
36
36
|
progress_summary?: WaitForTaskProgressSummary;
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -85,6 +85,6 @@ export async function waitForTask(taskId: string, waitSeconds = 25): Promise<Wai
|
|
|
85
85
|
: executionBlocked
|
|
86
86
|
? { name: "health_check", arguments: {} }
|
|
87
87
|
: { name: "wait_for_task", arguments: { task_id: taskId, timeout_seconds: waitSeconds } },
|
|
88
|
-
...(terminal ? { summary: getTaskSummary(taskId) } : { progress_summary: progressSummary }),
|
|
88
|
+
...(terminal ? { summary: getTaskSummary(taskId, { view: "compact" }) } : { progress_summary: progressSummary }),
|
|
89
89
|
};
|
|
90
90
|
}
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PATCHWARDEN_VERSION = "0.
|
|
2
|
-
export const TOOL_SCHEMA_EPOCH = "2026-06-
|
|
1
|
+
export const PATCHWARDEN_VERSION = "0.6.0";
|
|
2
|
+
export const TOOL_SCHEMA_EPOCH = "2026-06-22-v6";
|
package/tsconfig.json
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "
|
|
5
|
-
"moduleResolution": "
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"types": ["node"],
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"skipLibCheck": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*.ts"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|
package/Restart-PatchWarden.cmd
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
setlocal
|
|
3
|
-
cd /d "%~dp0"
|
|
4
|
-
echo.
|
|
5
|
-
echo ========================================
|
|
6
|
-
echo PatchWarden One-Click Restart
|
|
7
|
-
echo ========================================
|
|
8
|
-
echo.
|
|
9
|
-
echo This will:
|
|
10
|
-
echo 1. Stop only processes owned by the current PatchWarden launcher
|
|
11
|
-
echo 2. Rebuild the project
|
|
12
|
-
echo 3. Start a fresh tunnel launcher window
|
|
13
|
-
echo.
|
|
14
|
-
echo Close this window to cancel, or
|
|
15
|
-
pause
|
|
16
|
-
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0scripts\restart-patchwarden.ps1"
|
|
17
|
-
echo.
|
|
18
|
-
echo Restart script finished.
|
|
19
|
-
pause
|
package/docs/release-v0.3.0.md
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# Safe-Bifrost v0.3.0
|
|
2
|
-
|
|
3
|
-
Safe-Bifrost v0.3.0 makes ChatGPT-managed local agent work easier to monitor,
|
|
4
|
-
verify, and accept without weakening the workspace and command boundaries.
|
|
5
|
-
|
|
6
|
-
## Highlights
|
|
7
|
-
|
|
8
|
-
- Adds `wait_for_task` so ChatGPT can stay in the tool loop until a task is
|
|
9
|
-
terminal, plus `get_task_summary` for one-call acceptance evidence.
|
|
10
|
-
- Requires an explicit existing repository directory and supports both
|
|
11
|
-
workspace-relative and absolute `repo_path` values.
|
|
12
|
-
- Adds independent multi-command `verify_commands` while keeping
|
|
13
|
-
`test_command` compatibility.
|
|
14
|
-
- Generates structured `result.json`, `verify.json`, readable `verify.log`,
|
|
15
|
-
and a complete `diff.patch` for every terminal task.
|
|
16
|
-
- Adds task-scoped file statistics, clear large/no-diff responses, and avoids
|
|
17
|
-
including unchanged pre-existing workspace files in task diff evidence.
|
|
18
|
-
- Detects changes outside `resolved_repo_path`, marks the task
|
|
19
|
-
`failed_scope_violation`, invalidates verification acceptance, and creates a
|
|
20
|
-
review-only `rollback_scope_violation_plan.md`.
|
|
21
|
-
- Allows ordinary long build/test/release plans while blocking explicit
|
|
22
|
-
credential theft, destructive disk deletion, and malicious persistence.
|
|
23
|
-
- Redacts secret-like values in ordinary task artifacts instead of rejecting
|
|
24
|
-
the entire read.
|
|
25
|
-
- Expands `doctor`, stdio/HTTP MCP smoke tests, lifecycle coverage, packaging
|
|
26
|
-
checks, and ChatGPT workflow documentation.
|
|
27
|
-
|
|
28
|
-
## Compatibility note
|
|
29
|
-
|
|
30
|
-
`create_task` now requires `repo_path`. Existing clients that omitted it must
|
|
31
|
-
pass an existing directory under `workspaceRoot`. `test_command` remains
|
|
32
|
-
supported and is converted into a one-entry verification list.
|
|
33
|
-
|
|
34
|
-
## Verification
|
|
35
|
-
|
|
36
|
-
- `npm test`: 63 security tests, 14 lifecycle tests, and doctor smoke passed
|
|
37
|
-
- `npm run test:mcp`: passed
|
|
38
|
-
- `npm run test:http-mcp`: 11 passed
|
|
39
|
-
- `npm run doctor`: 47 OK, 0 WARN, 0 FAIL
|
|
40
|
-
- `npm run pack:clean`: passed
|
|
41
|
-
- `npm run verify:package`: passed
|
|
42
|
-
|
|
43
|
-
Tracked in [issue #1](https://github.com/jiezeng2004-design/safe-bifrost/issues/1).
|
package/docs/release-v0.4.0.md
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# PatchWarden v0.4.0
|
|
2
|
-
|
|
3
|
-
PatchWarden v0.4.0 hardens the ChatGPT-to-local-agent task loop without
|
|
4
|
-
weakening workspace, command, or sensitive-file boundaries.
|
|
5
|
-
|
|
6
|
-
This release also completes the project rename from Safe-Bifrost to
|
|
7
|
-
PatchWarden. The npm package is now `patchwarden`; the old package is retained
|
|
8
|
-
only to point existing users to the new name.
|
|
9
|
-
|
|
10
|
-
## Highlights
|
|
11
|
-
|
|
12
|
-
- Adds supervised Windows tunnel recovery with local readiness probes,
|
|
13
|
-
structured failure categories, capped retry backoff, and redacted runtime
|
|
14
|
-
state under `%LOCALAPPDATA%\patchwarden\runtime`.
|
|
15
|
-
- Adds `Check-PatchWarden-Health.cmd` for diagnostics when the MCP tunnel itself
|
|
16
|
-
is unreachable.
|
|
17
|
-
- Expands `health_check` with workspace, tasks directory, watcher, agent, tunnel,
|
|
18
|
-
and last-error evidence; local HTTP `/healthz` and `/readyz` are also available.
|
|
19
|
-
- Lets `create_task` use exactly one of a saved `plan_id`, persisted
|
|
20
|
-
`inline_plan`, or a guarded task template.
|
|
21
|
-
- Adds `inspect_only`, `feature_small`, `fix_tests`, `release_check`, and safe
|
|
22
|
-
review-only `rollback_scope_violation` templates.
|
|
23
|
-
- Enforces no-change templates with `failed_policy_violation`.
|
|
24
|
-
- Adds deterministic failure guidance through `failure_reason`,
|
|
25
|
-
`failed_command`, `suggested_next_action`, and `safe_followup_prompt`.
|
|
26
|
-
- Adds standalone `file-stats.json` while preserving complete `diff.patch`,
|
|
27
|
-
`changed-files.json`, and the v0.3.0 acceptance contract.
|
|
28
|
-
- Adds deterministic `full` and `chatgpt_core` tool profiles. Tunnel stdio uses
|
|
29
|
-
an exact 16-tool core manifest while ordinary local launches remain on the
|
|
30
|
-
22-tool full profile.
|
|
31
|
-
- Adds schema-inclusive tool manifest hashing, `schema_epoch`, server version,
|
|
32
|
-
and a real MCP stdio preflight before tunnel startup.
|
|
33
|
-
- Adds structured `create_task.next_tool_call`, preferred
|
|
34
|
-
`wait_for_task(timeout_seconds)`, legacy `wait_seconds` compatibility, and a
|
|
35
|
-
complete terminal summary in the wait response.
|
|
36
|
-
- Adds recursive redaction for structured result/verification summaries and
|
|
37
|
-
concise verification counts/headlines such as `166 passed`.
|
|
38
|
-
- Adds explicit `get_diff.patch_mode` values for textual, no-change, and
|
|
39
|
-
hash-only evidence, including a reason when a textual patch is unavailable.
|
|
40
|
-
- Enhances the local health report with process source/version evidence and
|
|
41
|
-
mixed-version warnings without automatically ending any process.
|
|
42
|
-
- Adds first-class watcher stale/missing evidence, structured pending artifact
|
|
43
|
-
responses, and controlled recovery for launcher-owned watcher processes.
|
|
44
|
-
- Reports stale client catalogs as `tool_catalog_mismatch` with the active
|
|
45
|
-
profile, schema epoch, manifest hash, and Connector refresh guidance.
|
|
46
|
-
- Fixes a plan-guard bypass where generic security wording could suppress a
|
|
47
|
-
later credential-access instruction. Every dangerous occurrence is now
|
|
48
|
-
evaluated independently and only directly negated actions are allowed.
|
|
49
|
-
|
|
50
|
-
## Compatibility
|
|
51
|
-
|
|
52
|
-
The MCP tool names and `save_plan` -> `create_task` workflow remain stable, but
|
|
53
|
-
the product rename is intentionally breaking: old CLI names, `SAFE_BIFROST_*`
|
|
54
|
-
variables, `safe-bifrost.config.json`, `.safe-bifrost/`, and legacy AppData
|
|
55
|
-
paths are not loaded. Follow the migration guide before replacing an existing
|
|
56
|
-
installation. `repo_path`, configured agent validation, and exact
|
|
57
|
-
verification-command allowlists remain mandatory.
|
|
58
|
-
|
|
59
|
-
## Verification
|
|
60
|
-
|
|
61
|
-
Run the following from Windows PowerShell:
|
|
62
|
-
|
|
63
|
-
```powershell
|
|
64
|
-
npm.cmd test
|
|
65
|
-
npm.cmd run test:mcp
|
|
66
|
-
npm.cmd run test:http-mcp
|
|
67
|
-
npm.cmd run doctor
|
|
68
|
-
npm.cmd run check:tool-manifest
|
|
69
|
-
npm.cmd run check:brand
|
|
70
|
-
npm.cmd run test:tunnel-supervisor
|
|
71
|
-
npm.cmd run test:watcher-supervisor
|
|
72
|
-
npm.cmd run pack:clean
|
|
73
|
-
npm.cmd run verify:package
|
|
74
|
-
```
|