patchwarden 1.1.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +83 -8
- package/README.md +78 -10
- package/dist/assessments/agentAssessor.d.ts +1 -1
- package/dist/assessments/agentAssessor.js +3 -3
- package/dist/assessments/assessmentStore.d.ts +1 -1
- package/dist/assessments/assessmentStore.js +2 -2
- package/dist/assessments/confirmCli.js +5 -4
- package/dist/config.d.ts +7 -0
- package/dist/config.js +34 -0
- package/dist/control/middleware/auth.d.ts +10 -0
- package/dist/control/middleware/auth.js +8 -0
- package/dist/control/middleware/static.d.ts +3 -0
- package/dist/control/middleware/static.js +65 -0
- package/dist/control/routes/audit.d.ts +15 -0
- package/dist/control/routes/audit.js +277 -0
- package/dist/control/routes/evidence.d.ts +4 -0
- package/dist/control/routes/evidence.js +96 -0
- package/dist/control/routes/lineage.d.ts +3 -0
- package/dist/control/routes/lineage.js +71 -0
- package/dist/control/routes/policy.d.ts +3 -0
- package/dist/control/routes/policy.js +81 -0
- package/dist/control/routes/process.d.ts +5 -0
- package/dist/control/routes/process.js +200 -0
- package/dist/control/routes/sessions.d.ts +22 -0
- package/dist/control/routes/sessions.js +224 -0
- package/dist/control/routes/status.d.ts +6 -0
- package/dist/control/routes/status.js +250 -0
- package/dist/control/routes/taskActions.d.ts +21 -0
- package/dist/control/routes/taskActions.js +233 -0
- package/dist/control/routes/tasks.d.ts +20 -0
- package/dist/control/routes/tasks.js +310 -0
- package/dist/control/routes/workspace.d.ts +15 -0
- package/dist/control/routes/workspace.js +193 -0
- package/dist/control/runtime.d.ts +91 -0
- package/dist/control/runtime.js +392 -0
- package/dist/control/server.d.ts +13 -0
- package/dist/control/server.js +479 -0
- package/dist/control/shared.d.ts +35 -0
- package/dist/control/shared.js +288 -0
- package/dist/controlCenter.d.ts +6 -0
- package/dist/controlCenter.js +7 -2001
- package/dist/direct/directGuards.js +30 -8
- package/dist/direct/directSessionStore.d.ts +2 -0
- package/dist/direct/directVerification.js +7 -0
- package/dist/doctor.d.ts +18 -1
- package/dist/doctor.js +579 -348
- package/dist/goal/goalReport.d.ts +54 -0
- package/dist/goal/goalReport.js +204 -0
- package/dist/goal/goalStatus.d.ts +6 -0
- package/dist/goal/specKitImport.d.ts +63 -0
- package/dist/goal/specKitImport.js +220 -0
- package/dist/goal/subgoalSync.js +2 -1
- package/dist/httpServer.js +15 -12
- package/dist/index.js +7 -4
- package/dist/logging.d.ts +7 -1
- package/dist/logging.js +8 -0
- package/dist/policy/projectPolicy.d.ts +55 -0
- package/dist/policy/projectPolicy.js +286 -0
- package/dist/runner/changeCapture.d.ts +3 -3
- package/dist/runner/changeCapture.js +63 -39
- package/dist/runner/cli.js +7 -6
- package/dist/runner/postTaskCleanup.js +26 -2
- package/dist/runner/runTask.js +245 -221
- package/dist/runner/simpleProcess.js +4 -4
- package/dist/runner/watch.js +17 -14
- package/dist/security/contentRedaction.d.ts +6 -0
- package/dist/security/contentRedaction.js +22 -0
- package/dist/smoke-test.js +261 -255
- package/dist/test/unit/apply-patch.test.d.ts +1 -0
- package/dist/test/unit/apply-patch.test.js +225 -0
- package/dist/test/unit/create-task.test.d.ts +1 -0
- package/dist/test/unit/create-task.test.js +197 -0
- package/dist/test/unit/direct-guards.test.js +124 -9
- package/dist/test/unit/evidence-pack.test.d.ts +1 -0
- package/dist/test/unit/evidence-pack.test.js +224 -0
- package/dist/test/unit/get-task-status.test.d.ts +1 -0
- package/dist/test/unit/get-task-status.test.js +174 -0
- package/dist/test/unit/get-task-summary.test.d.ts +1 -0
- package/dist/test/unit/get-task-summary.test.js +146 -0
- package/dist/test/unit/goal-report.test.d.ts +1 -0
- package/dist/test/unit/goal-report.test.js +159 -0
- package/dist/test/unit/goal-subgoal-task.test.js +6 -6
- package/dist/test/unit/goal-tools-registry.test.js +6 -4
- package/dist/test/unit/path-guard.test.js +24 -0
- package/dist/test/unit/project-policy-release-mode.test.d.ts +1 -0
- package/dist/test/unit/project-policy-release-mode.test.js +125 -0
- package/dist/test/unit/run-task-loop.test.d.ts +1 -0
- package/dist/test/unit/run-task-loop.test.js +380 -0
- package/dist/test/unit/schema-drift-check.test.js +10 -9
- package/dist/test/unit/spec-kit-import.test.d.ts +1 -0
- package/dist/test/unit/spec-kit-import.test.js +341 -0
- package/dist/test/unit/wait-for-task.test.d.ts +1 -0
- package/dist/test/unit/wait-for-task.test.js +144 -0
- package/dist/tools/auditTask.d.ts +8 -63
- package/dist/tools/auditTask.js +12 -8
- package/dist/tools/createDirectSession.d.ts +1 -1
- package/dist/tools/createDirectSession.js +2 -2
- package/dist/tools/createTask.d.ts +2 -2
- package/dist/tools/createTask.js +4 -4
- package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
- package/dist/tools/dispatch/coreDispatch.js +282 -0
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +14 -0
- package/dist/tools/dispatch/diagnosticDispatch.js +78 -0
- package/dist/tools/dispatch/directDispatch.d.ts +8 -0
- package/dist/tools/dispatch/directDispatch.js +115 -0
- package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
- package/dist/tools/dispatch/goalDispatch.js +91 -0
- package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
- package/dist/tools/dispatch/releaseDispatch.js +45 -0
- package/dist/tools/dispatch/types.d.ts +23 -0
- package/dist/tools/dispatch/types.js +15 -0
- package/dist/tools/evidencePack.d.ts +45 -0
- package/dist/tools/evidencePack.js +375 -0
- package/dist/tools/finalizeDirectSession.d.ts +1 -1
- package/dist/tools/finalizeDirectSession.js +3 -3
- package/dist/tools/goalSubgoalTask.d.ts +1 -1
- package/dist/tools/goalSubgoalTask.js +2 -2
- package/dist/tools/healthCheck.js +3 -3
- package/dist/tools/recommendAgentForTask.d.ts +19 -0
- package/dist/tools/recommendAgentForTask.js +56 -0
- package/dist/tools/registry.d.ts +3 -3
- package/dist/tools/registry.js +333 -402
- package/dist/tools/releaseMode.d.ts +50 -0
- package/dist/tools/releaseMode.js +370 -0
- package/dist/tools/retryTask.d.ts +2 -2
- package/dist/tools/retryTask.js +2 -2
- package/dist/tools/runDirectVerificationBundle.d.ts +26 -0
- package/dist/tools/runDirectVerificationBundle.js +64 -0
- package/dist/tools/runTaskLoop.d.ts +57 -0
- package/dist/tools/runTaskLoop.js +417 -0
- package/dist/tools/runVerification.d.ts +4 -0
- package/dist/tools/runVerification.js +4 -0
- package/dist/tools/safeViews.d.ts +8 -2
- package/dist/tools/safeViews.js +4 -2
- package/dist/tools/taskLineage.d.ts +91 -0
- package/dist/tools/taskLineage.js +175 -0
- package/dist/tools/toolCatalog.d.ts +2 -2
- package/dist/tools/toolCatalog.js +6 -0
- package/dist/tools/toolRegistry.js +132 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/docs/CODE_WIKI.md +983 -0
- package/docs/agentseal-integration.md +150 -0
- package/docs/architecture.md +63 -0
- package/docs/assets/patchwarden-oss-demo.gif +0 -0
- package/docs/chatgpt-usage.md +31 -0
- package/docs/control-center/README.md +9 -0
- package/docs/dashboard-overview.md +86 -0
- package/docs/demo.md +8 -0
- package/docs/direct-session-workflow.md +98 -0
- package/docs/evidence-pack-schema.md +215 -0
- package/docs/execution-plan-2026-07-09.md +315 -0
- package/docs/lineage-evidence-pack-workflow.md +127 -0
- package/docs/mcp-inspector-testing.md +200 -0
- package/docs/open-source-application.md +162 -0
- package/docs/opencode-worker.md +151 -0
- package/docs/openhands-worker.md +181 -0
- package/docs/release-evidence.md +72 -0
- package/docs/roadmap-execution-and-acceptance.md +365 -0
- package/docs/spec-kit-integration.md +131 -0
- package/docs/task-safe-review-workflow.md +98 -0
- package/docs/threat-model.md +79 -0
- package/docs/user-feedback.md +40 -0
- package/docs/why-patchwarden.md +110 -0
- package/package.json +3 -3
- package/scripts/checks/control-center-smoke.js +443 -0
- package/scripts/checks/control-smoke.js +2 -2
- package/scripts/checks/lifecycle-smoke.js +29 -23
- package/scripts/checks/mcp-manifest-check.js +12 -0
- package/scripts/checks/mcp-smoke.js +33 -7
- package/scripts/checks/watcher-supervisor-smoke.js +1 -1
- package/scripts/generate-demo-gif.py +320 -0
- package/src/assessments/agentAssessor.ts +3 -3
- package/src/assessments/assessmentStore.ts +2 -2
- package/src/assessments/confirmCli.ts +5 -4
- package/src/config.ts +37 -0
- package/src/control/middleware/auth.ts +17 -0
- package/src/control/middleware/static.ts +71 -0
- package/src/control/routes/audit.ts +321 -0
- package/src/control/routes/evidence.ts +107 -0
- package/src/control/routes/lineage.ts +92 -0
- package/src/control/routes/policy.ts +81 -0
- package/src/control/routes/process.ts +204 -0
- package/src/control/routes/sessions.ts +251 -0
- package/src/control/routes/status.ts +325 -0
- package/src/control/routes/taskActions.ts +248 -0
- package/src/control/routes/tasks.ts +323 -0
- package/src/control/routes/workspace.ts +203 -0
- package/src/control/runtime.ts +472 -0
- package/src/control/server.ts +471 -0
- package/src/control/shared.ts +294 -0
- package/src/controlCenter.ts +7 -2150
- package/src/direct/directGuards.ts +28 -7
- package/src/direct/directSessionStore.ts +2 -0
- package/src/direct/directVerification.ts +7 -0
- package/src/doctor.ts +741 -481
- package/src/goal/goalReport.ts +271 -0
- package/src/goal/goalStatus.ts +6 -0
- package/src/goal/specKitImport.ts +355 -0
- package/src/goal/subgoalSync.ts +4 -2
- package/src/httpServer.ts +17 -14
- package/src/index.ts +7 -4
- package/src/logging.ts +10 -1
- package/src/policy/projectPolicy.ts +344 -0
- package/src/runner/changeCapture.ts +70 -42
- package/src/runner/cli.ts +7 -6
- package/src/runner/postTaskCleanup.ts +26 -2
- package/src/runner/runTask.ts +325 -223
- package/src/runner/simpleProcess.ts +4 -4
- package/src/runner/watch.ts +17 -14
- package/src/security/contentRedaction.ts +29 -0
- package/src/smoke-test.ts +254 -252
- package/src/test/unit/apply-patch.test.ts +293 -0
- package/src/test/unit/create-task.test.ts +255 -0
- package/src/test/unit/direct-guards.test.ts +178 -8
- package/src/test/unit/evidence-pack.test.ts +251 -0
- package/src/test/unit/get-task-status.test.ts +203 -0
- package/src/test/unit/get-task-summary.test.ts +173 -0
- package/src/test/unit/goal-report.test.ts +189 -0
- package/src/test/unit/goal-subgoal-task.test.ts +6 -6
- package/src/test/unit/goal-tools-registry.test.ts +7 -5
- package/src/test/unit/path-guard.test.ts +30 -0
- package/src/test/unit/project-policy-release-mode.test.ts +156 -0
- package/src/test/unit/run-task-loop.test.ts +425 -0
- package/src/test/unit/schema-drift-check.test.ts +11 -9
- package/src/test/unit/spec-kit-import.test.ts +429 -0
- package/src/test/unit/wait-for-task.test.ts +176 -0
- package/src/tools/auditTask.ts +99 -59
- package/src/tools/createDirectSession.ts +3 -3
- package/src/tools/createTask.ts +7 -7
- package/src/tools/dispatch/coreDispatch.ts +374 -0
- package/src/tools/dispatch/diagnosticDispatch.ts +101 -0
- package/src/tools/dispatch/directDispatch.ts +167 -0
- package/src/tools/dispatch/goalDispatch.ts +127 -0
- package/src/tools/dispatch/releaseDispatch.ts +65 -0
- package/src/tools/dispatch/types.ts +24 -0
- package/src/tools/evidencePack.ts +490 -0
- package/src/tools/finalizeDirectSession.ts +4 -4
- package/src/tools/goalSubgoalTask.ts +2 -2
- package/src/tools/healthCheck.ts +3 -3
- package/src/tools/listWorkspace.ts +71 -71
- package/src/tools/recommendAgentForTask.ts +79 -0
- package/src/tools/registry.ts +352 -509
- package/src/tools/releaseMode.ts +450 -0
- package/src/tools/retryTask.ts +2 -2
- package/src/tools/runDirectVerificationBundle.ts +98 -0
- package/src/tools/runTaskLoop.ts +526 -0
- package/src/tools/runVerification.ts +8 -0
- package/src/tools/safeViews.ts +4 -2
- package/src/tools/taskLineage.ts +300 -0
- package/src/tools/toolCatalog.ts +6 -0
- package/src/tools/toolRegistry.ts +132 -0
- package/src/version.ts +2 -2
- package/ui/pages/audit.html +192 -3
- package/ui/pages/dashboard.html +1070 -14
- package/ui/pages/direct-sessions.html +505 -53
- package/ui/pages/task-detail.html +456 -438
- package/ui/pages/tasks.html +598 -61
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { getConfig } from "../config.js";
|
|
5
|
+
import { guardReadPath } from "../security/pathGuard.js";
|
|
6
|
+
import { redactSensitiveValue } from "../security/contentRedaction.js";
|
|
7
|
+
import { PatchWardenError } from "../errors.js";
|
|
8
|
+
|
|
9
|
+
export type TaskLoopStopReason =
|
|
10
|
+
| "success"
|
|
11
|
+
| "max_iterations_reached"
|
|
12
|
+
| "verification_failed"
|
|
13
|
+
| "high_risk_blocked"
|
|
14
|
+
| "user_confirmation_required"
|
|
15
|
+
| "agent_timeout"
|
|
16
|
+
| "policy_blocked"
|
|
17
|
+
| "watcher_blocked"
|
|
18
|
+
| "direct_profile_disabled"
|
|
19
|
+
| "direct_verification_failed"
|
|
20
|
+
| "direct_audit_failed";
|
|
21
|
+
|
|
22
|
+
export interface TaskLineageDirectSession {
|
|
23
|
+
session_id: string;
|
|
24
|
+
status?: "passed" | "failed" | "skipped";
|
|
25
|
+
command_count?: number;
|
|
26
|
+
passed_commands?: number;
|
|
27
|
+
failed_commands?: number;
|
|
28
|
+
timed_out_commands?: number;
|
|
29
|
+
audit_decision?: "pass" | "warn" | "fail" | "not_run";
|
|
30
|
+
changed_files_total?: number;
|
|
31
|
+
next_action?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TaskLineageWorktree {
|
|
35
|
+
isolation_mode: "current_repo" | "worktree";
|
|
36
|
+
worktree_id?: string;
|
|
37
|
+
worktree_path?: string;
|
|
38
|
+
branch?: string;
|
|
39
|
+
requested_base_branch?: string;
|
|
40
|
+
cleanup: "keep" | "archive" | "delete_ignored_only";
|
|
41
|
+
status: "not_used" | "active" | "failed";
|
|
42
|
+
next_action: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface TaskLineageAgentRouting {
|
|
46
|
+
requested_agent: string | null;
|
|
47
|
+
selected_agent: string;
|
|
48
|
+
reason: string;
|
|
49
|
+
fallback: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface TaskLineageRound {
|
|
53
|
+
iteration: number;
|
|
54
|
+
task_id: string;
|
|
55
|
+
role: "main" | "fix_tests" | "cleanup";
|
|
56
|
+
status: string;
|
|
57
|
+
terminal: boolean;
|
|
58
|
+
verification_status: string;
|
|
59
|
+
audit_verdict: string;
|
|
60
|
+
fail_checks: string[];
|
|
61
|
+
warn_checks: string[];
|
|
62
|
+
next_action: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface TaskLineageRecord {
|
|
66
|
+
lineage_id: string;
|
|
67
|
+
goal: string;
|
|
68
|
+
repo_path: string;
|
|
69
|
+
created_at: string;
|
|
70
|
+
updated_at: string;
|
|
71
|
+
final_status: "accepted" | "needs_fix" | "blocked" | "failed";
|
|
72
|
+
stop_reason: TaskLoopStopReason;
|
|
73
|
+
next_action: string;
|
|
74
|
+
main_task: string | null;
|
|
75
|
+
fix_tasks: string[];
|
|
76
|
+
cleanup_tasks: string[];
|
|
77
|
+
direct_sessions: Array<string | TaskLineageDirectSession>;
|
|
78
|
+
rounds: TaskLineageRound[];
|
|
79
|
+
warnings: string[];
|
|
80
|
+
errors: string[];
|
|
81
|
+
worktree?: TaskLineageWorktree;
|
|
82
|
+
agent_routing?: TaskLineageAgentRouting;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface SafeTaskLineage {
|
|
86
|
+
lineage_id: string;
|
|
87
|
+
goal: string;
|
|
88
|
+
repo_path: string;
|
|
89
|
+
created_at: string;
|
|
90
|
+
updated_at: string;
|
|
91
|
+
final_status: TaskLineageRecord["final_status"];
|
|
92
|
+
stop_reason: TaskLoopStopReason;
|
|
93
|
+
next_action: string;
|
|
94
|
+
tasks: {
|
|
95
|
+
main: string | null;
|
|
96
|
+
fix: string[];
|
|
97
|
+
cleanup: string[];
|
|
98
|
+
direct_sessions: TaskLineageDirectSession[];
|
|
99
|
+
};
|
|
100
|
+
worktree: TaskLineageWorktree;
|
|
101
|
+
agent_routing: TaskLineageAgentRouting | null;
|
|
102
|
+
verification: {
|
|
103
|
+
latest_status: string;
|
|
104
|
+
passed: boolean;
|
|
105
|
+
};
|
|
106
|
+
rounds: TaskLineageRound[];
|
|
107
|
+
warnings: string[];
|
|
108
|
+
errors: string[];
|
|
109
|
+
truncated: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function createLineageId(now = new Date()): string {
|
|
113
|
+
const stamp = now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
114
|
+
return `lineage_${stamp}_${randomBytes(4).toString("hex")}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function writeTaskLineage(record: TaskLineageRecord): SafeTaskLineage {
|
|
118
|
+
const config = getConfig();
|
|
119
|
+
const lineageDir = resolve(config.workspaceRoot, ".patchwarden", "lineages", record.lineage_id);
|
|
120
|
+
mkdirSync(lineageDir, { recursive: true });
|
|
121
|
+
const safeRecord = redactSensitiveValue(record).value as TaskLineageRecord;
|
|
122
|
+
writeFileSync(join(lineageDir, "lineage.json"), JSON.stringify(safeRecord, null, 2) + "\n", "utf-8");
|
|
123
|
+
writeFileSync(join(lineageDir, "SUMMARY.md"), buildSummaryMarkdown(safeRecord), "utf-8");
|
|
124
|
+
return toSafeTaskLineage(safeRecord);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function getTaskLineage(lineageId: string, options: { max_items?: number } = {}): SafeTaskLineage {
|
|
128
|
+
const maxItems = normalizeMaxItems(options.max_items);
|
|
129
|
+
if (!/^[A-Za-z0-9_-]+$/.test(lineageId)) {
|
|
130
|
+
throw new PatchWardenError(
|
|
131
|
+
"invalid_lineage_id",
|
|
132
|
+
"lineage_id may contain only letters, numbers, underscores, and hyphens.",
|
|
133
|
+
"Pass a lineage_id returned by run_task_loop.",
|
|
134
|
+
true,
|
|
135
|
+
{ lineage_id: lineageId }
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const config = getConfig();
|
|
139
|
+
const lineageFile = resolve(config.workspaceRoot, ".patchwarden", "lineages", lineageId, "lineage.json");
|
|
140
|
+
guardReadPath(lineageFile, config.workspaceRoot, ".patchwarden/lineages");
|
|
141
|
+
if (!existsSync(lineageFile)) {
|
|
142
|
+
throw new PatchWardenError(
|
|
143
|
+
"lineage_not_found",
|
|
144
|
+
`Task lineage not found: "${lineageId}".`,
|
|
145
|
+
"Pass a lineage_id returned by run_task_loop.",
|
|
146
|
+
true,
|
|
147
|
+
{ lineage_id: lineageId }
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const raw = readFileSync(lineageFile, "utf-8").replace(/^\uFEFF/, "");
|
|
151
|
+
const record = JSON.parse(raw) as TaskLineageRecord;
|
|
152
|
+
return toSafeTaskLineage(redactSensitiveValue(record).value as TaskLineageRecord, maxItems);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function toSafeTaskLineage(record: TaskLineageRecord, maxItems = 8): SafeTaskLineage {
|
|
156
|
+
const rounds = record.rounds.slice(0, maxItems);
|
|
157
|
+
const latest = record.rounds[record.rounds.length - 1];
|
|
158
|
+
const directSessions = normalizeDirectSessions(record.direct_sessions);
|
|
159
|
+
return {
|
|
160
|
+
lineage_id: record.lineage_id,
|
|
161
|
+
goal: record.goal,
|
|
162
|
+
repo_path: record.repo_path,
|
|
163
|
+
created_at: record.created_at,
|
|
164
|
+
updated_at: record.updated_at,
|
|
165
|
+
final_status: record.final_status,
|
|
166
|
+
stop_reason: record.stop_reason,
|
|
167
|
+
next_action: record.next_action,
|
|
168
|
+
tasks: {
|
|
169
|
+
main: record.main_task,
|
|
170
|
+
fix: record.fix_tasks.slice(0, maxItems),
|
|
171
|
+
cleanup: record.cleanup_tasks.slice(0, maxItems),
|
|
172
|
+
direct_sessions: directSessions.slice(0, maxItems),
|
|
173
|
+
},
|
|
174
|
+
worktree: normalizeWorktree(record.worktree),
|
|
175
|
+
agent_routing: record.agent_routing ? {
|
|
176
|
+
requested_agent: record.agent_routing.requested_agent,
|
|
177
|
+
selected_agent: truncate(String(record.agent_routing.selected_agent), 120),
|
|
178
|
+
reason: truncate(String(record.agent_routing.reason), 240),
|
|
179
|
+
fallback: Boolean(record.agent_routing.fallback),
|
|
180
|
+
} : null,
|
|
181
|
+
verification: {
|
|
182
|
+
latest_status: latest?.verification_status || "not_available",
|
|
183
|
+
passed: latest?.verification_status === "passed",
|
|
184
|
+
},
|
|
185
|
+
rounds,
|
|
186
|
+
warnings: record.warnings.slice(0, maxItems).map((value) => truncate(value, 240)),
|
|
187
|
+
errors: record.errors.slice(0, maxItems).map((value) => truncate(value, 240)),
|
|
188
|
+
truncated:
|
|
189
|
+
record.rounds.length > maxItems ||
|
|
190
|
+
record.fix_tasks.length > maxItems ||
|
|
191
|
+
record.cleanup_tasks.length > maxItems ||
|
|
192
|
+
directSessions.length > maxItems ||
|
|
193
|
+
record.warnings.length > maxItems ||
|
|
194
|
+
record.errors.length > maxItems,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function buildSummaryMarkdown(record: TaskLineageRecord): string {
|
|
199
|
+
const rounds = record.rounds.map((round) =>
|
|
200
|
+
`- ${round.role} ${round.task_id}: ${round.status}, verification=${round.verification_status}, audit=${round.audit_verdict}`
|
|
201
|
+
);
|
|
202
|
+
return [
|
|
203
|
+
"# PatchWarden Task Lineage",
|
|
204
|
+
"",
|
|
205
|
+
`- Lineage: ${record.lineage_id}`,
|
|
206
|
+
`- Goal: ${record.goal}`,
|
|
207
|
+
`- Repo: ${record.repo_path}`,
|
|
208
|
+
`- Final status: ${record.final_status}`,
|
|
209
|
+
`- Stop reason: ${record.stop_reason}`,
|
|
210
|
+
`- Next action: ${record.next_action}`,
|
|
211
|
+
`- Isolation: ${normalizeWorktree(record.worktree).isolation_mode}`,
|
|
212
|
+
`- Worktree: ${formatWorktree(record.worktree)}`,
|
|
213
|
+
`- Agent routing: ${formatAgentRouting(record.agent_routing)}`,
|
|
214
|
+
"",
|
|
215
|
+
"## Tasks",
|
|
216
|
+
`- Main: ${record.main_task || "none"}`,
|
|
217
|
+
`- Fix tasks: ${record.fix_tasks.length > 0 ? record.fix_tasks.join(", ") : "none"}`,
|
|
218
|
+
`- Cleanup tasks: ${record.cleanup_tasks.length > 0 ? record.cleanup_tasks.join(", ") : "none"}`,
|
|
219
|
+
`- Direct sessions: ${formatDirectSessions(record.direct_sessions)}`,
|
|
220
|
+
"",
|
|
221
|
+
"## Rounds",
|
|
222
|
+
...(rounds.length > 0 ? rounds : ["- None."]),
|
|
223
|
+
"",
|
|
224
|
+
].join("\n");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function normalizeWorktree(value: TaskLineageWorktree | undefined): TaskLineageWorktree {
|
|
228
|
+
if (!value) {
|
|
229
|
+
return {
|
|
230
|
+
isolation_mode: "current_repo",
|
|
231
|
+
cleanup: "keep",
|
|
232
|
+
status: "not_used",
|
|
233
|
+
next_action: "none",
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
isolation_mode: value.isolation_mode === "worktree" ? "worktree" : "current_repo",
|
|
238
|
+
worktree_id: value.worktree_id ? truncate(String(value.worktree_id), 120) : undefined,
|
|
239
|
+
worktree_path: value.worktree_path ? truncate(String(value.worktree_path), 260) : undefined,
|
|
240
|
+
branch: value.branch ? truncate(String(value.branch), 160) : undefined,
|
|
241
|
+
requested_base_branch: value.requested_base_branch ? truncate(String(value.requested_base_branch), 160) : undefined,
|
|
242
|
+
cleanup: value.cleanup,
|
|
243
|
+
status: value.status,
|
|
244
|
+
next_action: truncate(String(value.next_action || "review_worktree"), 240),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function formatWorktree(value: TaskLineageWorktree | undefined): string {
|
|
249
|
+
const worktree = normalizeWorktree(value);
|
|
250
|
+
if (worktree.isolation_mode !== "worktree") return "not used";
|
|
251
|
+
const id = worktree.worktree_id || "unknown";
|
|
252
|
+
const status = worktree.status || "unknown";
|
|
253
|
+
const branch = worktree.branch ? ` branch=${worktree.branch}` : "";
|
|
254
|
+
return `${id} status=${status}${branch}`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function formatAgentRouting(value: TaskLineageAgentRouting | undefined): string {
|
|
258
|
+
if (!value) return "not recorded";
|
|
259
|
+
const requested = value.requested_agent ? ` requested=${value.requested_agent}` : "";
|
|
260
|
+
return `${value.selected_agent}${requested} reason=${truncate(value.reason, 160)}`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function normalizeDirectSessions(value: Array<string | TaskLineageDirectSession>): TaskLineageDirectSession[] {
|
|
264
|
+
return value.map((entry) => {
|
|
265
|
+
if (typeof entry === "string") return { session_id: entry };
|
|
266
|
+
return {
|
|
267
|
+
session_id: String(entry.session_id || ""),
|
|
268
|
+
status: entry.status,
|
|
269
|
+
command_count: entry.command_count,
|
|
270
|
+
passed_commands: entry.passed_commands,
|
|
271
|
+
failed_commands: entry.failed_commands,
|
|
272
|
+
timed_out_commands: entry.timed_out_commands,
|
|
273
|
+
audit_decision: entry.audit_decision,
|
|
274
|
+
changed_files_total: entry.changed_files_total,
|
|
275
|
+
next_action: entry.next_action ? truncate(String(entry.next_action), 240) : undefined,
|
|
276
|
+
};
|
|
277
|
+
}).filter((entry) => entry.session_id !== "");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function formatDirectSessions(value: Array<string | TaskLineageDirectSession>): string {
|
|
281
|
+
const sessions = normalizeDirectSessions(value);
|
|
282
|
+
if (sessions.length === 0) return "none";
|
|
283
|
+
return sessions.map((entry) => {
|
|
284
|
+
const status = entry.status ? ` status=${entry.status}` : "";
|
|
285
|
+
const audit = entry.audit_decision ? ` audit=${entry.audit_decision}` : "";
|
|
286
|
+
return `${entry.session_id}${status}${audit}`;
|
|
287
|
+
}).join(", ");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function normalizeMaxItems(value: number | undefined): number {
|
|
291
|
+
if (value === undefined) return 8;
|
|
292
|
+
if (!Number.isInteger(value) || value < 1 || value > 50) {
|
|
293
|
+
throw new Error("max_items must be an integer from 1 to 50.");
|
|
294
|
+
}
|
|
295
|
+
return value;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function truncate(value: string, maxChars: number): string {
|
|
299
|
+
return value.length <= maxChars ? value : `${value.slice(0, maxChars)}...`;
|
|
300
|
+
}
|
package/src/tools/toolCatalog.ts
CHANGED
|
@@ -25,6 +25,11 @@ export const CHATGPT_CORE_TOOL_NAMES = [
|
|
|
25
25
|
"read_workspace_file",
|
|
26
26
|
"save_plan",
|
|
27
27
|
"create_task",
|
|
28
|
+
"run_task_loop",
|
|
29
|
+
"recommend_agent_for_task",
|
|
30
|
+
"get_task_lineage",
|
|
31
|
+
"export_task_evidence_pack",
|
|
32
|
+
"get_project_policy",
|
|
28
33
|
"wait_for_task",
|
|
29
34
|
"get_task_summary",
|
|
30
35
|
"get_diff",
|
|
@@ -50,6 +55,7 @@ export const CHATGPT_DIRECT_TOOL_NAMES = [
|
|
|
50
55
|
"read_workspace_file",
|
|
51
56
|
"apply_patch",
|
|
52
57
|
"run_verification",
|
|
58
|
+
"run_direct_verification_bundle",
|
|
53
59
|
"finalize_direct_session",
|
|
54
60
|
"audit_session",
|
|
55
61
|
"safe_direct_summary",
|
|
@@ -405,6 +405,61 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
|
|
|
405
405
|
requiresConfirmation: false,
|
|
406
406
|
relatedTools: ["save_plan", "wait_for_task", "get_task_status"],
|
|
407
407
|
},
|
|
408
|
+
run_task_loop: {
|
|
409
|
+
title: "Run Task Loop",
|
|
410
|
+
summary: "运行受保护的任务循环:创建任务、等待、验证、审计,并在低风险测试失败时创建 fix_tests 后续任务",
|
|
411
|
+
profiles: ["full", "chatgpt_core"],
|
|
412
|
+
modes: ["delegate", "audit"],
|
|
413
|
+
tags: ["loop", "task", "lineage", "verify", "audit", "fix"],
|
|
414
|
+
aliases: ["task_loop", "run_loop", "guarded_loop"],
|
|
415
|
+
risk: "workspace_write",
|
|
416
|
+
requiresConfirmation: false,
|
|
417
|
+
relatedTools: ["create_task", "wait_for_task", "safe_result", "safe_audit", "get_task_lineage"],
|
|
418
|
+
},
|
|
419
|
+
recommend_agent_for_task: {
|
|
420
|
+
title: "Recommend Agent For Task",
|
|
421
|
+
summary: "Return bounded agent routing guidance without starting an agent or creating a task",
|
|
422
|
+
profiles: ["full", "chatgpt_core"],
|
|
423
|
+
modes: ["delegate", "diagnostic"],
|
|
424
|
+
tags: ["agent", "routing", "recommend", "task", "safe"],
|
|
425
|
+
aliases: ["recommend_agent", "route_agent", "agent_routing"],
|
|
426
|
+
risk: "readonly",
|
|
427
|
+
requiresConfirmation: false,
|
|
428
|
+
relatedTools: ["list_agents", "create_task", "run_task_loop"],
|
|
429
|
+
},
|
|
430
|
+
get_task_lineage: {
|
|
431
|
+
title: "Get Task Lineage",
|
|
432
|
+
summary: "读取 run_task_loop 生成的任务链路安全摘要,不返回完整日志或 diff",
|
|
433
|
+
profiles: ["full", "chatgpt_core"],
|
|
434
|
+
modes: ["delegate", "audit", "diagnostic"],
|
|
435
|
+
tags: ["lineage", "loop", "summary", "task", "safe"],
|
|
436
|
+
aliases: ["lineage", "task_lineage"],
|
|
437
|
+
risk: "readonly",
|
|
438
|
+
requiresConfirmation: false,
|
|
439
|
+
relatedTools: ["run_task_loop", "safe_result", "safe_audit"],
|
|
440
|
+
},
|
|
441
|
+
export_task_evidence_pack: {
|
|
442
|
+
title: "Export Task Evidence Pack",
|
|
443
|
+
summary: "Export bounded lineage, verification, Direct, policy, and catalog evidence without logs or diffs",
|
|
444
|
+
profiles: ["full", "chatgpt_core"],
|
|
445
|
+
modes: ["delegate", "audit", "diagnostic"],
|
|
446
|
+
tags: ["evidence", "lineage", "summary", "release", "safe"],
|
|
447
|
+
aliases: ["evidence_pack", "export_evidence", "task_evidence"],
|
|
448
|
+
risk: "workspace_write",
|
|
449
|
+
requiresConfirmation: false,
|
|
450
|
+
relatedTools: ["get_task_lineage", "run_task_loop", "get_project_policy"],
|
|
451
|
+
},
|
|
452
|
+
get_project_policy: {
|
|
453
|
+
title: "Get Project Policy",
|
|
454
|
+
summary: "Read bounded project policy and release readiness without exposing secrets or expanding command permissions",
|
|
455
|
+
profiles: ["full", "chatgpt_core"],
|
|
456
|
+
modes: ["diagnostic", "release"],
|
|
457
|
+
tags: ["policy", "project", "release", "readiness", "safe"],
|
|
458
|
+
aliases: ["project_policy", "policy", "get_policy"],
|
|
459
|
+
risk: "readonly",
|
|
460
|
+
requiresConfirmation: false,
|
|
461
|
+
relatedTools: ["release_check", "release_prepare", "release_cleanup"],
|
|
462
|
+
},
|
|
408
463
|
sync_file: {
|
|
409
464
|
title: "Sync File",
|
|
410
465
|
summary: "在 Direct 会话中同步文件(带 sha256 校验)",
|
|
@@ -517,6 +572,17 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
|
|
|
517
572
|
requiresConfirmation: false,
|
|
518
573
|
relatedTools: ["finalize_direct_session", "audit_session"],
|
|
519
574
|
},
|
|
575
|
+
run_direct_verification_bundle: {
|
|
576
|
+
title: "Run Direct Verification Bundle",
|
|
577
|
+
summary: "Run multiple Direct allowlisted verification commands and return bounded status without stdout or stderr tails",
|
|
578
|
+
profiles: ["full", "chatgpt_direct"],
|
|
579
|
+
modes: ["direct", "audit"],
|
|
580
|
+
tags: ["direct", "verify", "verification", "bundle", "safe"],
|
|
581
|
+
aliases: ["direct_verify_bundle", "verification_bundle"],
|
|
582
|
+
risk: "command",
|
|
583
|
+
requiresConfirmation: false,
|
|
584
|
+
relatedTools: ["run_verification", "safe_finalize_direct_session", "safe_audit_direct_session"],
|
|
585
|
+
},
|
|
520
586
|
safe_direct_summary: {
|
|
521
587
|
title: "Safe Direct Summary",
|
|
522
588
|
summary: "Direct 会话安全摘要(不含完整 diff 或验证 stdout/stderr)",
|
|
@@ -662,6 +728,28 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
|
|
|
662
728
|
requiresConfirmation: false,
|
|
663
729
|
relatedTools: ["summarize_goal_progress", "read_goal"],
|
|
664
730
|
},
|
|
731
|
+
export_goal_report: {
|
|
732
|
+
title: "Export Goal Report",
|
|
733
|
+
summary: "Export a structured final report for a Goal session",
|
|
734
|
+
profiles: ["full"],
|
|
735
|
+
modes: ["delegate", "audit"],
|
|
736
|
+
tags: ["goal", "report", "export", "summary", "final"],
|
|
737
|
+
aliases: ["goal_report", "report"],
|
|
738
|
+
risk: "workspace_write",
|
|
739
|
+
requiresConfirmation: false,
|
|
740
|
+
relatedTools: ["summarize_goal_progress", "export_handoff"],
|
|
741
|
+
},
|
|
742
|
+
import_speckit_tasks: {
|
|
743
|
+
title: "Import Spec Kit Tasks",
|
|
744
|
+
summary: "Import Spec Kit tasks into a Goal session as subgoals",
|
|
745
|
+
profiles: ["full"],
|
|
746
|
+
modes: ["delegate", "audit"],
|
|
747
|
+
tags: ["goal", "speckit", "import", "tasks", "subgoal"],
|
|
748
|
+
aliases: ["speckit_import", "import_tasks"],
|
|
749
|
+
risk: "workspace_write",
|
|
750
|
+
requiresConfirmation: false,
|
|
751
|
+
relatedTools: ["create_subgoal_task", "export_goal_report"],
|
|
752
|
+
},
|
|
665
753
|
invoke_discovered_tool: {
|
|
666
754
|
title: "Invoke Discovered Tool",
|
|
667
755
|
summary: "v0.8.1: 通过 token 调用 discover_tools 发现的动态工具",
|
|
@@ -696,6 +784,50 @@ const STATIC_TOOL_META: Record<string, StaticToolMeta> = {
|
|
|
696
784
|
requiresConfirmation: true,
|
|
697
785
|
relatedTools: ["audit_task", "safe_status"],
|
|
698
786
|
},
|
|
787
|
+
release_check: {
|
|
788
|
+
title: "Release Check",
|
|
789
|
+
summary: "Run bounded release readiness checks through the existing release gate without remote writes",
|
|
790
|
+
profiles: ["full"],
|
|
791
|
+
modes: ["release", "diagnostic"],
|
|
792
|
+
tags: ["release", "check", "gate", "readiness", "policy"],
|
|
793
|
+
aliases: ["release_check", "check_release"],
|
|
794
|
+
risk: "release",
|
|
795
|
+
requiresConfirmation: true,
|
|
796
|
+
relatedTools: ["check_release_gate", "get_project_policy", "release_prepare", "release_verify"],
|
|
797
|
+
},
|
|
798
|
+
release_prepare: {
|
|
799
|
+
title: "Release Prepare",
|
|
800
|
+
summary: "Run policy-approved local release preparation commands through the existing command guard",
|
|
801
|
+
profiles: ["full"],
|
|
802
|
+
modes: ["release"],
|
|
803
|
+
tags: ["release", "prepare", "build", "test", "policy"],
|
|
804
|
+
aliases: ["prepare_release", "release_ready"],
|
|
805
|
+
risk: "release",
|
|
806
|
+
requiresConfirmation: true,
|
|
807
|
+
relatedTools: ["get_project_policy", "release_check", "release_verify"],
|
|
808
|
+
},
|
|
809
|
+
release_verify: {
|
|
810
|
+
title: "Release Verify",
|
|
811
|
+
summary: "Verify npm, GitHub release, and CI facts with read-only remote checks",
|
|
812
|
+
profiles: ["full"],
|
|
813
|
+
modes: ["release", "diagnostic"],
|
|
814
|
+
tags: ["release", "verify", "npm", "github", "ci"],
|
|
815
|
+
aliases: ["verify_release", "release_remote_verify"],
|
|
816
|
+
risk: "release",
|
|
817
|
+
requiresConfirmation: true,
|
|
818
|
+
relatedTools: ["release_check", "check_release_gate"],
|
|
819
|
+
},
|
|
820
|
+
release_cleanup: {
|
|
821
|
+
title: "Release Cleanup",
|
|
822
|
+
summary: "Dry-run-first cleanup for low-risk release artifacts under project policy",
|
|
823
|
+
profiles: ["full"],
|
|
824
|
+
modes: ["release"],
|
|
825
|
+
tags: ["release", "cleanup", "artifacts", "policy"],
|
|
826
|
+
aliases: ["cleanup_release", "release_artifact_cleanup"],
|
|
827
|
+
risk: "workspace_write",
|
|
828
|
+
requiresConfirmation: true,
|
|
829
|
+
relatedTools: ["get_project_policy", "release_prepare"],
|
|
830
|
+
},
|
|
699
831
|
merge_worktree: {
|
|
700
832
|
title: "Merge Worktree",
|
|
701
833
|
summary: "v1.0.0: 合并隔离 worktree 变更回主工作区",
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PATCHWARDEN_VERSION = "1.1
|
|
2
|
-
export const TOOL_SCHEMA_EPOCH = "2026-
|
|
1
|
+
export const PATCHWARDEN_VERSION = "1.5.1";
|
|
2
|
+
export const TOOL_SCHEMA_EPOCH = "2026-07-05-v13";
|