patchwarden 0.6.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +54 -20
- package/README.md +48 -21
- package/dist/agents/agentRouter.d.ts +40 -0
- package/dist/agents/agentRouter.js +95 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +6 -2
- package/dist/controlCenter.js +212 -3
- package/dist/direct/directSessionStore.d.ts +2 -0
- package/dist/direct/directVerification.js +7 -0
- package/dist/doctor.js +32 -1
- package/dist/goal/acceptanceEngine.d.ts +62 -0
- package/dist/goal/acceptanceEngine.js +103 -0
- package/dist/goal/acceptanceTemplate.d.ts +10 -0
- package/dist/goal/acceptanceTemplate.js +104 -0
- package/dist/goal/goalGraph.d.ts +58 -0
- package/dist/goal/goalGraph.js +189 -0
- package/dist/goal/goalProgress.d.ts +81 -0
- package/dist/goal/goalProgress.js +216 -0
- package/dist/goal/goalStatus.d.ts +60 -0
- package/dist/goal/goalStatus.js +137 -0
- package/dist/goal/goalStore.d.ts +79 -0
- package/dist/goal/goalStore.js +211 -0
- package/dist/goal/handoffExport.d.ts +34 -0
- package/dist/goal/handoffExport.js +183 -0
- package/dist/goal/subgoalSync.d.ts +40 -0
- package/dist/goal/subgoalSync.js +72 -0
- package/dist/goal/worktreeManager.d.ts +96 -0
- package/dist/goal/worktreeManager.js +292 -0
- package/dist/logging.d.ts +44 -0
- package/dist/logging.js +68 -0
- package/dist/policy/projectPolicy.d.ts +55 -0
- package/dist/policy/projectPolicy.js +286 -0
- package/dist/release/releaseGate.d.ts +99 -0
- package/dist/release/releaseGate.js +475 -0
- package/dist/runner/postTaskCleanup.d.ts +13 -0
- package/dist/runner/postTaskCleanup.js +147 -0
- package/dist/runner/runTask.js +50 -10
- package/dist/security/discoveryTokenStore.d.ts +62 -0
- package/dist/security/discoveryTokenStore.js +100 -0
- package/dist/security/toolInvocationGuard.d.ts +35 -0
- package/dist/security/toolInvocationGuard.js +153 -0
- package/dist/smoke-test.js +18 -10
- package/dist/taskRuntime.d.ts +17 -0
- package/dist/test/unit/acceptance-engine.test.d.ts +1 -0
- package/dist/test/unit/acceptance-engine.test.js +228 -0
- package/dist/test/unit/agent-router.test.d.ts +1 -0
- package/dist/test/unit/agent-router.test.js +287 -0
- package/dist/test/unit/audit-checks.test.d.ts +1 -0
- package/dist/test/unit/audit-checks.test.js +350 -0
- package/dist/test/unit/diagnose-task.test.d.ts +1 -0
- package/dist/test/unit/diagnose-task.test.js +457 -0
- package/dist/test/unit/discovery-token-store.test.d.ts +1 -0
- package/dist/test/unit/discovery-token-store.test.js +139 -0
- package/dist/test/unit/evidence-pack.test.d.ts +1 -0
- package/dist/test/unit/evidence-pack.test.js +130 -0
- package/dist/test/unit/goal-graph.test.d.ts +1 -0
- package/dist/test/unit/goal-graph.test.js +298 -0
- package/dist/test/unit/goal-progress.test.d.ts +1 -0
- package/dist/test/unit/goal-progress.test.js +381 -0
- package/dist/test/unit/goal-status.test.d.ts +1 -0
- package/dist/test/unit/goal-status.test.js +215 -0
- package/dist/test/unit/goal-store.test.d.ts +1 -0
- package/dist/test/unit/goal-store.test.js +253 -0
- package/dist/test/unit/goal-subgoal-task.test.d.ts +1 -0
- package/dist/test/unit/goal-subgoal-task.test.js +55 -0
- package/dist/test/unit/goal-tools-registry.test.d.ts +1 -0
- package/dist/test/unit/goal-tools-registry.test.js +190 -0
- package/dist/test/unit/handoff-export.test.d.ts +1 -0
- package/dist/test/unit/handoff-export.test.js +263 -0
- package/dist/test/unit/invoke-discovered-tool.test.d.ts +1 -0
- package/dist/test/unit/invoke-discovered-tool.test.js +167 -0
- package/dist/test/unit/logging.test.js +127 -5
- package/dist/test/unit/post-task-cleanup.test.d.ts +1 -0
- package/dist/test/unit/post-task-cleanup.test.js +48 -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/reconcile-tasks.test.d.ts +1 -0
- package/dist/test/unit/reconcile-tasks.test.js +456 -0
- package/dist/test/unit/release-gate.test.d.ts +1 -0
- package/dist/test/unit/release-gate.test.js +242 -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/safe-views.test.d.ts +1 -0
- package/dist/test/unit/safe-views.test.js +171 -0
- package/dist/test/unit/schema-drift-check.test.d.ts +1 -0
- package/dist/test/unit/schema-drift-check.test.js +176 -0
- package/dist/test/unit/subgoal-sync.test.d.ts +1 -0
- package/dist/test/unit/subgoal-sync.test.js +183 -0
- package/dist/test/unit/tool-invocation-guard.test.d.ts +1 -0
- package/dist/test/unit/tool-invocation-guard.test.js +432 -0
- package/dist/test/unit/tool-usage-stats.test.d.ts +1 -0
- package/dist/test/unit/tool-usage-stats.test.js +300 -0
- package/dist/test/unit/toolSearch.test.d.ts +1 -0
- package/dist/test/unit/toolSearch.test.js +571 -0
- package/dist/test/unit/worktree-manager.test.d.ts +1 -0
- package/dist/test/unit/worktree-manager.test.js +176 -0
- package/dist/tools/auditTask.d.ts +103 -1
- package/dist/tools/auditTask.js +461 -8
- package/dist/tools/cancelTask.js +1 -1
- package/dist/tools/checkReleaseGate.d.ts +21 -0
- package/dist/tools/checkReleaseGate.js +22 -0
- package/dist/tools/createTask.d.ts +18 -2
- package/dist/tools/createTask.js +29 -1
- package/dist/tools/diagnoseTask.d.ts +45 -0
- package/dist/tools/diagnoseTask.js +347 -0
- package/dist/tools/discardWorktree.d.ts +24 -0
- package/dist/tools/discardWorktree.js +27 -0
- package/dist/tools/discoverTools.d.ts +11 -0
- package/dist/tools/discoverTools.js +39 -0
- package/dist/tools/evidencePack.d.ts +39 -0
- package/dist/tools/evidencePack.js +168 -0
- package/dist/tools/explainTool.d.ts +11 -0
- package/dist/tools/explainTool.js +21 -0
- package/dist/tools/getTaskSummary.js +2 -1
- package/dist/tools/goalSubgoalTask.d.ts +51 -0
- package/dist/tools/goalSubgoalTask.js +110 -0
- package/dist/tools/healthCheck.d.ts +1 -0
- package/dist/tools/healthCheck.js +5 -1
- package/dist/tools/invokeDiscoveredTool.d.ts +37 -0
- package/dist/tools/invokeDiscoveredTool.js +112 -0
- package/dist/tools/listTasks.d.ts +3 -1
- package/dist/tools/listTasks.js +14 -1
- package/dist/tools/mergeWorktree.d.ts +24 -0
- package/dist/tools/mergeWorktree.js +27 -0
- package/dist/tools/recommendAgentForTask.d.ts +19 -0
- package/dist/tools/recommendAgentForTask.js +56 -0
- package/dist/tools/reconcileTasks.d.ts +41 -0
- package/dist/tools/reconcileTasks.js +352 -0
- package/dist/tools/registry.js +926 -2
- package/dist/tools/releaseMode.d.ts +50 -0
- package/dist/tools/releaseMode.js +370 -0
- 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/safeStatus.d.ts +31 -1
- package/dist/tools/safeStatus.js +43 -1
- package/dist/tools/safeViews.d.ts +262 -0
- package/dist/tools/safeViews.js +252 -0
- package/dist/tools/schemaDriftCheck.d.ts +46 -0
- package/dist/tools/schemaDriftCheck.js +80 -0
- package/dist/tools/taskLineage.d.ts +91 -0
- package/dist/tools/taskLineage.js +175 -0
- package/dist/tools/toolCatalog.d.ts +4 -3
- package/dist/tools/toolCatalog.js +39 -11
- package/dist/tools/toolRegistry.d.ts +61 -0
- package/dist/tools/toolRegistry.js +834 -0
- package/dist/tools/toolSearch.d.ts +110 -0
- package/dist/tools/toolSearch.js +331 -0
- package/dist/tools/toolUsageStats.d.ts +41 -0
- package/dist/tools/toolUsageStats.js +116 -0
- package/dist/tools/waitForTask.js +1 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/watcherStatus.d.ts +29 -0
- package/dist/watcherStatus.js +92 -1
- package/docs/chatgpt-usage.md +31 -0
- package/docs/control-center/README.md +9 -0
- package/package.json +3 -2
- package/scripts/checks/control-center-smoke.js +87 -0
- package/scripts/checks/control-smoke.js +2 -2
- package/scripts/checks/doctor-smoke.js +1 -0
- package/scripts/checks/lifecycle-smoke.js +13 -9
- package/scripts/checks/mcp-manifest-check.js +65 -6
- package/scripts/checks/mcp-smoke.js +60 -9
- package/scripts/checks/watcher-supervisor-smoke.js +2 -2
- package/src/agents/agentRouter.ts +149 -0
- package/src/config.ts +9 -3
- package/src/controlCenter.ts +215 -4
- package/src/direct/directSessionStore.ts +2 -0
- package/src/direct/directVerification.ts +7 -0
- package/src/doctor.ts +36 -1
- package/src/goal/acceptanceEngine.ts +160 -0
- package/src/goal/acceptanceTemplate.ts +121 -0
- package/src/goal/goalGraph.ts +225 -0
- package/src/goal/goalProgress.ts +301 -0
- package/src/goal/goalStatus.ts +234 -0
- package/src/goal/goalStore.ts +306 -0
- package/src/goal/handoffExport.ts +211 -0
- package/src/goal/subgoalSync.ts +82 -0
- package/src/goal/worktreeManager.ts +404 -0
- package/src/logging.ts +91 -0
- package/src/policy/projectPolicy.ts +344 -0
- package/src/release/releaseGate.ts +567 -0
- package/src/runner/postTaskCleanup.ts +154 -0
- package/src/runner/runTask.ts +49 -10
- package/src/security/discoveryTokenStore.ts +157 -0
- package/src/security/toolInvocationGuard.ts +251 -0
- package/src/smoke-test.ts +15 -7
- package/src/taskRuntime.ts +17 -0
- package/src/test/unit/acceptance-engine.test.ts +261 -0
- package/src/test/unit/agent-router.test.ts +342 -0
- package/src/test/unit/audit-checks.test.ts +567 -0
- package/src/test/unit/diagnose-task.test.ts +544 -0
- package/src/test/unit/discovery-token-store.test.ts +181 -0
- package/src/test/unit/evidence-pack.test.ts +142 -0
- package/src/test/unit/goal-graph.test.ts +347 -0
- package/src/test/unit/goal-progress.test.ts +538 -0
- package/src/test/unit/goal-status.test.ts +270 -0
- package/src/test/unit/goal-store.test.ts +318 -0
- package/src/test/unit/goal-subgoal-task.test.ts +72 -0
- package/src/test/unit/goal-tools-registry.test.ts +243 -0
- package/src/test/unit/handoff-export.test.ts +295 -0
- package/src/test/unit/invoke-discovered-tool.test.ts +216 -0
- package/src/test/unit/logging.test.ts +177 -5
- package/src/test/unit/post-task-cleanup.test.ts +53 -0
- package/src/test/unit/project-policy-release-mode.test.ts +156 -0
- package/src/test/unit/reconcile-tasks.test.ts +551 -0
- package/src/test/unit/release-gate.test.ts +314 -0
- package/src/test/unit/run-task-loop.test.ts +425 -0
- package/src/test/unit/safe-views.test.ts +184 -0
- package/src/test/unit/schema-drift-check.test.ts +260 -0
- package/src/test/unit/subgoal-sync.test.ts +236 -0
- package/src/test/unit/tool-invocation-guard.test.ts +542 -0
- package/src/test/unit/tool-usage-stats.test.ts +384 -0
- package/src/test/unit/toolSearch.test.ts +756 -0
- package/src/test/unit/worktree-manager.test.ts +247 -0
- package/src/tools/auditTask.ts +831 -402
- package/src/tools/cancelTask.ts +1 -1
- package/src/tools/checkReleaseGate.ts +45 -0
- package/src/tools/createTask.ts +64 -6
- package/src/tools/diagnoseTask.ts +460 -0
- package/src/tools/discardWorktree.ts +42 -0
- package/src/tools/discoverTools.ts +51 -0
- package/src/tools/evidencePack.ts +205 -0
- package/src/tools/explainTool.ts +26 -0
- package/src/tools/getTaskSummary.ts +2 -1
- package/src/tools/goalSubgoalTask.ts +170 -0
- package/src/tools/healthCheck.ts +4 -1
- package/src/tools/invokeDiscoveredTool.ts +163 -0
- package/src/tools/listTasks.ts +16 -2
- package/src/tools/listWorkspace.ts +71 -71
- package/src/tools/mergeWorktree.ts +42 -0
- package/src/tools/recommendAgentForTask.ts +79 -0
- package/src/tools/reconcileTasks.ts +464 -0
- package/src/tools/registry.ts +1093 -73
- package/src/tools/releaseMode.ts +450 -0
- package/src/tools/runDirectVerificationBundle.ts +98 -0
- package/src/tools/runTaskLoop.ts +526 -0
- package/src/tools/runVerification.ts +8 -0
- package/src/tools/safeStatus.ts +73 -2
- package/src/tools/safeViews.ts +273 -0
- package/src/tools/schemaDriftCheck.ts +120 -0
- package/src/tools/taskLineage.ts +300 -0
- package/src/tools/toolCatalog.ts +40 -11
- package/src/tools/toolRegistry.ts +896 -0
- package/src/tools/toolSearch.ts +464 -0
- package/src/tools/toolUsageStats.ts +151 -0
- package/src/tools/waitForTask.ts +1 -0
- package/src/version.ts +2 -2
- package/src/watcherStatus.ts +97 -1
- package/ui/pages/dashboard.html +143 -2
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export type TaskLoopStopReason = "success" | "max_iterations_reached" | "verification_failed" | "high_risk_blocked" | "user_confirmation_required" | "agent_timeout" | "policy_blocked" | "watcher_blocked" | "direct_profile_disabled" | "direct_verification_failed" | "direct_audit_failed";
|
|
2
|
+
export interface TaskLineageDirectSession {
|
|
3
|
+
session_id: string;
|
|
4
|
+
status?: "passed" | "failed" | "skipped";
|
|
5
|
+
command_count?: number;
|
|
6
|
+
passed_commands?: number;
|
|
7
|
+
failed_commands?: number;
|
|
8
|
+
timed_out_commands?: number;
|
|
9
|
+
audit_decision?: "pass" | "warn" | "fail" | "not_run";
|
|
10
|
+
changed_files_total?: number;
|
|
11
|
+
next_action?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface TaskLineageWorktree {
|
|
14
|
+
isolation_mode: "current_repo" | "worktree";
|
|
15
|
+
worktree_id?: string;
|
|
16
|
+
worktree_path?: string;
|
|
17
|
+
branch?: string;
|
|
18
|
+
requested_base_branch?: string;
|
|
19
|
+
cleanup: "keep" | "archive" | "delete_ignored_only";
|
|
20
|
+
status: "not_used" | "active" | "failed";
|
|
21
|
+
next_action: string;
|
|
22
|
+
}
|
|
23
|
+
export interface TaskLineageAgentRouting {
|
|
24
|
+
requested_agent: string | null;
|
|
25
|
+
selected_agent: string;
|
|
26
|
+
reason: string;
|
|
27
|
+
fallback: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface TaskLineageRound {
|
|
30
|
+
iteration: number;
|
|
31
|
+
task_id: string;
|
|
32
|
+
role: "main" | "fix_tests" | "cleanup";
|
|
33
|
+
status: string;
|
|
34
|
+
terminal: boolean;
|
|
35
|
+
verification_status: string;
|
|
36
|
+
audit_verdict: string;
|
|
37
|
+
fail_checks: string[];
|
|
38
|
+
warn_checks: string[];
|
|
39
|
+
next_action: string;
|
|
40
|
+
}
|
|
41
|
+
export interface TaskLineageRecord {
|
|
42
|
+
lineage_id: string;
|
|
43
|
+
goal: string;
|
|
44
|
+
repo_path: string;
|
|
45
|
+
created_at: string;
|
|
46
|
+
updated_at: string;
|
|
47
|
+
final_status: "accepted" | "needs_fix" | "blocked" | "failed";
|
|
48
|
+
stop_reason: TaskLoopStopReason;
|
|
49
|
+
next_action: string;
|
|
50
|
+
main_task: string | null;
|
|
51
|
+
fix_tasks: string[];
|
|
52
|
+
cleanup_tasks: string[];
|
|
53
|
+
direct_sessions: Array<string | TaskLineageDirectSession>;
|
|
54
|
+
rounds: TaskLineageRound[];
|
|
55
|
+
warnings: string[];
|
|
56
|
+
errors: string[];
|
|
57
|
+
worktree?: TaskLineageWorktree;
|
|
58
|
+
agent_routing?: TaskLineageAgentRouting;
|
|
59
|
+
}
|
|
60
|
+
export interface SafeTaskLineage {
|
|
61
|
+
lineage_id: string;
|
|
62
|
+
goal: string;
|
|
63
|
+
repo_path: string;
|
|
64
|
+
created_at: string;
|
|
65
|
+
updated_at: string;
|
|
66
|
+
final_status: TaskLineageRecord["final_status"];
|
|
67
|
+
stop_reason: TaskLoopStopReason;
|
|
68
|
+
next_action: string;
|
|
69
|
+
tasks: {
|
|
70
|
+
main: string | null;
|
|
71
|
+
fix: string[];
|
|
72
|
+
cleanup: string[];
|
|
73
|
+
direct_sessions: TaskLineageDirectSession[];
|
|
74
|
+
};
|
|
75
|
+
worktree: TaskLineageWorktree;
|
|
76
|
+
agent_routing: TaskLineageAgentRouting | null;
|
|
77
|
+
verification: {
|
|
78
|
+
latest_status: string;
|
|
79
|
+
passed: boolean;
|
|
80
|
+
};
|
|
81
|
+
rounds: TaskLineageRound[];
|
|
82
|
+
warnings: string[];
|
|
83
|
+
errors: string[];
|
|
84
|
+
truncated: boolean;
|
|
85
|
+
}
|
|
86
|
+
export declare function createLineageId(now?: Date): string;
|
|
87
|
+
export declare function writeTaskLineage(record: TaskLineageRecord): SafeTaskLineage;
|
|
88
|
+
export declare function getTaskLineage(lineageId: string, options?: {
|
|
89
|
+
max_items?: number;
|
|
90
|
+
}): SafeTaskLineage;
|
|
91
|
+
export declare function toSafeTaskLineage(record: TaskLineageRecord, maxItems?: number): SafeTaskLineage;
|
|
@@ -0,0 +1,175 @@
|
|
|
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
|
+
export function createLineageId(now = new Date()) {
|
|
9
|
+
const stamp = now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
10
|
+
return `lineage_${stamp}_${randomBytes(4).toString("hex")}`;
|
|
11
|
+
}
|
|
12
|
+
export function writeTaskLineage(record) {
|
|
13
|
+
const config = getConfig();
|
|
14
|
+
const lineageDir = resolve(config.workspaceRoot, ".patchwarden", "lineages", record.lineage_id);
|
|
15
|
+
mkdirSync(lineageDir, { recursive: true });
|
|
16
|
+
const safeRecord = redactSensitiveValue(record).value;
|
|
17
|
+
writeFileSync(join(lineageDir, "lineage.json"), JSON.stringify(safeRecord, null, 2) + "\n", "utf-8");
|
|
18
|
+
writeFileSync(join(lineageDir, "SUMMARY.md"), buildSummaryMarkdown(safeRecord), "utf-8");
|
|
19
|
+
return toSafeTaskLineage(safeRecord);
|
|
20
|
+
}
|
|
21
|
+
export function getTaskLineage(lineageId, options = {}) {
|
|
22
|
+
const maxItems = normalizeMaxItems(options.max_items);
|
|
23
|
+
if (!/^[A-Za-z0-9_-]+$/.test(lineageId)) {
|
|
24
|
+
throw new PatchWardenError("invalid_lineage_id", "lineage_id may contain only letters, numbers, underscores, and hyphens.", "Pass a lineage_id returned by run_task_loop.", true, { lineage_id: lineageId });
|
|
25
|
+
}
|
|
26
|
+
const config = getConfig();
|
|
27
|
+
const lineageFile = resolve(config.workspaceRoot, ".patchwarden", "lineages", lineageId, "lineage.json");
|
|
28
|
+
guardReadPath(lineageFile, config.workspaceRoot, ".patchwarden/lineages");
|
|
29
|
+
if (!existsSync(lineageFile)) {
|
|
30
|
+
throw new PatchWardenError("lineage_not_found", `Task lineage not found: "${lineageId}".`, "Pass a lineage_id returned by run_task_loop.", true, { lineage_id: lineageId });
|
|
31
|
+
}
|
|
32
|
+
const raw = readFileSync(lineageFile, "utf-8").replace(/^\uFEFF/, "");
|
|
33
|
+
const record = JSON.parse(raw);
|
|
34
|
+
return toSafeTaskLineage(redactSensitiveValue(record).value, maxItems);
|
|
35
|
+
}
|
|
36
|
+
export function toSafeTaskLineage(record, maxItems = 8) {
|
|
37
|
+
const rounds = record.rounds.slice(0, maxItems);
|
|
38
|
+
const latest = record.rounds[record.rounds.length - 1];
|
|
39
|
+
const directSessions = normalizeDirectSessions(record.direct_sessions);
|
|
40
|
+
return {
|
|
41
|
+
lineage_id: record.lineage_id,
|
|
42
|
+
goal: record.goal,
|
|
43
|
+
repo_path: record.repo_path,
|
|
44
|
+
created_at: record.created_at,
|
|
45
|
+
updated_at: record.updated_at,
|
|
46
|
+
final_status: record.final_status,
|
|
47
|
+
stop_reason: record.stop_reason,
|
|
48
|
+
next_action: record.next_action,
|
|
49
|
+
tasks: {
|
|
50
|
+
main: record.main_task,
|
|
51
|
+
fix: record.fix_tasks.slice(0, maxItems),
|
|
52
|
+
cleanup: record.cleanup_tasks.slice(0, maxItems),
|
|
53
|
+
direct_sessions: directSessions.slice(0, maxItems),
|
|
54
|
+
},
|
|
55
|
+
worktree: normalizeWorktree(record.worktree),
|
|
56
|
+
agent_routing: record.agent_routing ? {
|
|
57
|
+
requested_agent: record.agent_routing.requested_agent,
|
|
58
|
+
selected_agent: truncate(String(record.agent_routing.selected_agent), 120),
|
|
59
|
+
reason: truncate(String(record.agent_routing.reason), 240),
|
|
60
|
+
fallback: Boolean(record.agent_routing.fallback),
|
|
61
|
+
} : null,
|
|
62
|
+
verification: {
|
|
63
|
+
latest_status: latest?.verification_status || "not_available",
|
|
64
|
+
passed: latest?.verification_status === "passed",
|
|
65
|
+
},
|
|
66
|
+
rounds,
|
|
67
|
+
warnings: record.warnings.slice(0, maxItems).map((value) => truncate(value, 240)),
|
|
68
|
+
errors: record.errors.slice(0, maxItems).map((value) => truncate(value, 240)),
|
|
69
|
+
truncated: record.rounds.length > maxItems ||
|
|
70
|
+
record.fix_tasks.length > maxItems ||
|
|
71
|
+
record.cleanup_tasks.length > maxItems ||
|
|
72
|
+
directSessions.length > maxItems ||
|
|
73
|
+
record.warnings.length > maxItems ||
|
|
74
|
+
record.errors.length > maxItems,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function buildSummaryMarkdown(record) {
|
|
78
|
+
const rounds = record.rounds.map((round) => `- ${round.role} ${round.task_id}: ${round.status}, verification=${round.verification_status}, audit=${round.audit_verdict}`);
|
|
79
|
+
return [
|
|
80
|
+
"# PatchWarden Task Lineage",
|
|
81
|
+
"",
|
|
82
|
+
`- Lineage: ${record.lineage_id}`,
|
|
83
|
+
`- Goal: ${record.goal}`,
|
|
84
|
+
`- Repo: ${record.repo_path}`,
|
|
85
|
+
`- Final status: ${record.final_status}`,
|
|
86
|
+
`- Stop reason: ${record.stop_reason}`,
|
|
87
|
+
`- Next action: ${record.next_action}`,
|
|
88
|
+
`- Isolation: ${normalizeWorktree(record.worktree).isolation_mode}`,
|
|
89
|
+
`- Worktree: ${formatWorktree(record.worktree)}`,
|
|
90
|
+
`- Agent routing: ${formatAgentRouting(record.agent_routing)}`,
|
|
91
|
+
"",
|
|
92
|
+
"## Tasks",
|
|
93
|
+
`- Main: ${record.main_task || "none"}`,
|
|
94
|
+
`- Fix tasks: ${record.fix_tasks.length > 0 ? record.fix_tasks.join(", ") : "none"}`,
|
|
95
|
+
`- Cleanup tasks: ${record.cleanup_tasks.length > 0 ? record.cleanup_tasks.join(", ") : "none"}`,
|
|
96
|
+
`- Direct sessions: ${formatDirectSessions(record.direct_sessions)}`,
|
|
97
|
+
"",
|
|
98
|
+
"## Rounds",
|
|
99
|
+
...(rounds.length > 0 ? rounds : ["- None."]),
|
|
100
|
+
"",
|
|
101
|
+
].join("\n");
|
|
102
|
+
}
|
|
103
|
+
function normalizeWorktree(value) {
|
|
104
|
+
if (!value) {
|
|
105
|
+
return {
|
|
106
|
+
isolation_mode: "current_repo",
|
|
107
|
+
cleanup: "keep",
|
|
108
|
+
status: "not_used",
|
|
109
|
+
next_action: "none",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
isolation_mode: value.isolation_mode === "worktree" ? "worktree" : "current_repo",
|
|
114
|
+
worktree_id: value.worktree_id ? truncate(String(value.worktree_id), 120) : undefined,
|
|
115
|
+
worktree_path: value.worktree_path ? truncate(String(value.worktree_path), 260) : undefined,
|
|
116
|
+
branch: value.branch ? truncate(String(value.branch), 160) : undefined,
|
|
117
|
+
requested_base_branch: value.requested_base_branch ? truncate(String(value.requested_base_branch), 160) : undefined,
|
|
118
|
+
cleanup: value.cleanup,
|
|
119
|
+
status: value.status,
|
|
120
|
+
next_action: truncate(String(value.next_action || "review_worktree"), 240),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function formatWorktree(value) {
|
|
124
|
+
const worktree = normalizeWorktree(value);
|
|
125
|
+
if (worktree.isolation_mode !== "worktree")
|
|
126
|
+
return "not used";
|
|
127
|
+
const id = worktree.worktree_id || "unknown";
|
|
128
|
+
const status = worktree.status || "unknown";
|
|
129
|
+
const branch = worktree.branch ? ` branch=${worktree.branch}` : "";
|
|
130
|
+
return `${id} status=${status}${branch}`;
|
|
131
|
+
}
|
|
132
|
+
function formatAgentRouting(value) {
|
|
133
|
+
if (!value)
|
|
134
|
+
return "not recorded";
|
|
135
|
+
const requested = value.requested_agent ? ` requested=${value.requested_agent}` : "";
|
|
136
|
+
return `${value.selected_agent}${requested} reason=${truncate(value.reason, 160)}`;
|
|
137
|
+
}
|
|
138
|
+
function normalizeDirectSessions(value) {
|
|
139
|
+
return value.map((entry) => {
|
|
140
|
+
if (typeof entry === "string")
|
|
141
|
+
return { session_id: entry };
|
|
142
|
+
return {
|
|
143
|
+
session_id: String(entry.session_id || ""),
|
|
144
|
+
status: entry.status,
|
|
145
|
+
command_count: entry.command_count,
|
|
146
|
+
passed_commands: entry.passed_commands,
|
|
147
|
+
failed_commands: entry.failed_commands,
|
|
148
|
+
timed_out_commands: entry.timed_out_commands,
|
|
149
|
+
audit_decision: entry.audit_decision,
|
|
150
|
+
changed_files_total: entry.changed_files_total,
|
|
151
|
+
next_action: entry.next_action ? truncate(String(entry.next_action), 240) : undefined,
|
|
152
|
+
};
|
|
153
|
+
}).filter((entry) => entry.session_id !== "");
|
|
154
|
+
}
|
|
155
|
+
function formatDirectSessions(value) {
|
|
156
|
+
const sessions = normalizeDirectSessions(value);
|
|
157
|
+
if (sessions.length === 0)
|
|
158
|
+
return "none";
|
|
159
|
+
return sessions.map((entry) => {
|
|
160
|
+
const status = entry.status ? ` status=${entry.status}` : "";
|
|
161
|
+
const audit = entry.audit_decision ? ` audit=${entry.audit_decision}` : "";
|
|
162
|
+
return `${entry.session_id}${status}${audit}`;
|
|
163
|
+
}).join(", ");
|
|
164
|
+
}
|
|
165
|
+
function normalizeMaxItems(value) {
|
|
166
|
+
if (value === undefined)
|
|
167
|
+
return 8;
|
|
168
|
+
if (!Number.isInteger(value) || value < 1 || value > 50) {
|
|
169
|
+
throw new Error("max_items must be an integer from 1 to 50.");
|
|
170
|
+
}
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
function truncate(value, maxChars) {
|
|
174
|
+
return value.length <= maxChars ? value : `${value.slice(0, maxChars)}...`;
|
|
175
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ToolProfile = "full" | "chatgpt_core" | "chatgpt_direct";
|
|
1
|
+
export type ToolProfile = "full" | "chatgpt_core" | "chatgpt_direct" | "chatgpt_search";
|
|
2
2
|
export interface CatalogTool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
@@ -12,8 +12,9 @@ export interface ToolCatalogSnapshot {
|
|
|
12
12
|
tool_names: string[];
|
|
13
13
|
tool_manifest_sha256: string;
|
|
14
14
|
}
|
|
15
|
-
export declare const CHATGPT_CORE_TOOL_NAMES: readonly ["health_check", "list_agents", "list_workspace", "read_workspace_file", "save_plan", "create_task", "wait_for_task", "get_task_summary", "get_diff", "get_result", "get_result_json", "get_test_log", "get_task_status", "list_tasks", "cancel_task", "audit_task", "safe_status"];
|
|
16
|
-
export declare const CHATGPT_DIRECT_TOOL_NAMES: readonly ["health_check", "list_workspace", "create_direct_session", "search_workspace", "read_workspace_file", "apply_patch", "run_verification", "finalize_direct_session", "audit_session", "sync_file"];
|
|
15
|
+
export declare const CHATGPT_CORE_TOOL_NAMES: readonly ["health_check", "list_agents", "list_workspace", "read_workspace_file", "save_plan", "create_task", "run_task_loop", "recommend_agent_for_task", "get_task_lineage", "export_task_evidence_pack", "get_project_policy", "wait_for_task", "get_task_summary", "get_diff", "get_result", "get_result_json", "get_test_log", "get_task_status", "list_tasks", "cancel_task", "audit_task", "safe_status", "safe_result", "safe_audit", "safe_test_summary", "safe_diff_summary"];
|
|
16
|
+
export declare const CHATGPT_DIRECT_TOOL_NAMES: readonly ["health_check", "list_workspace", "create_direct_session", "search_workspace", "read_workspace_file", "apply_patch", "run_verification", "run_direct_verification_bundle", "finalize_direct_session", "audit_session", "safe_direct_summary", "safe_finalize_direct_session", "safe_audit_direct_session", "sync_file"];
|
|
17
|
+
export declare const CHATGPT_SEARCH_TOOL_NAMES: readonly ["health_check", "discover_tools", "explain_tool", "invoke_discovered_tool", "safe_status"];
|
|
17
18
|
export declare function resolveToolProfile(configProfile?: string): ToolProfile;
|
|
18
19
|
export declare function selectToolsForProfile<T extends CatalogTool>(tools: T[], profile: ToolProfile, enableDirectProfile?: boolean): T[];
|
|
19
20
|
export declare function buildToolCatalogSnapshot(tools: CatalogTool[], profile: ToolProfile): ToolCatalogSnapshot;
|
|
@@ -7,6 +7,11 @@ export const CHATGPT_CORE_TOOL_NAMES = [
|
|
|
7
7
|
"read_workspace_file",
|
|
8
8
|
"save_plan",
|
|
9
9
|
"create_task",
|
|
10
|
+
"run_task_loop",
|
|
11
|
+
"recommend_agent_for_task",
|
|
12
|
+
"get_task_lineage",
|
|
13
|
+
"export_task_evidence_pack",
|
|
14
|
+
"get_project_policy",
|
|
10
15
|
"wait_for_task",
|
|
11
16
|
"get_task_summary",
|
|
12
17
|
"get_diff",
|
|
@@ -18,6 +23,10 @@ export const CHATGPT_CORE_TOOL_NAMES = [
|
|
|
18
23
|
"cancel_task",
|
|
19
24
|
"audit_task",
|
|
20
25
|
"safe_status",
|
|
26
|
+
"safe_result",
|
|
27
|
+
"safe_audit",
|
|
28
|
+
"safe_test_summary",
|
|
29
|
+
"safe_diff_summary",
|
|
21
30
|
];
|
|
22
31
|
export const CHATGPT_DIRECT_TOOL_NAMES = [
|
|
23
32
|
"health_check",
|
|
@@ -27,15 +36,26 @@ export const CHATGPT_DIRECT_TOOL_NAMES = [
|
|
|
27
36
|
"read_workspace_file",
|
|
28
37
|
"apply_patch",
|
|
29
38
|
"run_verification",
|
|
39
|
+
"run_direct_verification_bundle",
|
|
30
40
|
"finalize_direct_session",
|
|
31
41
|
"audit_session",
|
|
42
|
+
"safe_direct_summary",
|
|
43
|
+
"safe_finalize_direct_session",
|
|
44
|
+
"safe_audit_direct_session",
|
|
32
45
|
"sync_file",
|
|
33
46
|
];
|
|
47
|
+
export const CHATGPT_SEARCH_TOOL_NAMES = [
|
|
48
|
+
"health_check",
|
|
49
|
+
"discover_tools",
|
|
50
|
+
"explain_tool",
|
|
51
|
+
"invoke_discovered_tool",
|
|
52
|
+
"safe_status",
|
|
53
|
+
];
|
|
34
54
|
let lastSnapshot = null;
|
|
35
55
|
export function resolveToolProfile(configProfile) {
|
|
36
56
|
const raw = (process.env.PATCHWARDEN_TOOL_PROFILE || configProfile || "full").trim();
|
|
37
|
-
if (raw !== "full" && raw !== "chatgpt_core" && raw !== "chatgpt_direct") {
|
|
38
|
-
throw new Error(`Invalid tool profile "${raw}". Expected "full", "chatgpt_core", or "
|
|
57
|
+
if (raw !== "full" && raw !== "chatgpt_core" && raw !== "chatgpt_direct" && raw !== "chatgpt_search") {
|
|
58
|
+
throw new Error(`Invalid tool profile "${raw}". Expected "full", "chatgpt_core", "chatgpt_direct", or "chatgpt_search".`);
|
|
39
59
|
}
|
|
40
60
|
return raw;
|
|
41
61
|
}
|
|
@@ -51,18 +71,26 @@ export function selectToolsForProfile(tools, profile, enableDirectProfile) {
|
|
|
51
71
|
return tool;
|
|
52
72
|
});
|
|
53
73
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
if (profile === "chatgpt_direct") {
|
|
75
|
+
if (!enableDirectProfile) {
|
|
76
|
+
// Degraded mode: only expose health_check for diagnostics
|
|
77
|
+
const tool = byName.get("health_check");
|
|
78
|
+
if (!tool)
|
|
79
|
+
throw new Error(`chatgpt_direct degraded mode requires missing tool "health_check".`);
|
|
80
|
+
return [tool];
|
|
81
|
+
}
|
|
82
|
+
return CHATGPT_DIRECT_TOOL_NAMES.map((name) => {
|
|
83
|
+
const tool = byName.get(name);
|
|
84
|
+
if (!tool)
|
|
85
|
+
throw new Error(`chatgpt_direct tool profile requires missing tool "${name}".`);
|
|
86
|
+
return tool;
|
|
87
|
+
});
|
|
61
88
|
}
|
|
62
|
-
|
|
89
|
+
// chatgpt_search: compact profile, 5 tools all required, no degraded mode
|
|
90
|
+
return CHATGPT_SEARCH_TOOL_NAMES.map((name) => {
|
|
63
91
|
const tool = byName.get(name);
|
|
64
92
|
if (!tool)
|
|
65
|
-
throw new Error(`
|
|
93
|
+
throw new Error(`chatgpt_search tool profile requires missing tool "${name}".`);
|
|
66
94
|
return tool;
|
|
67
95
|
});
|
|
68
96
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v0.7.1: 统一工具注册表 — SafeToolSearch 只读发现层的基础。
|
|
3
|
+
*
|
|
4
|
+
* 为每个 MCP 工具补全元数据:风险等级、模式、标签、别名、中文意图词。
|
|
5
|
+
* schema digest 使用稳定字段顺序的 canonical JSON → sha256 计算,
|
|
6
|
+
* 用于检测工具 schema 漂移、release check 校验、discover_tools 返回。
|
|
7
|
+
*/
|
|
8
|
+
import type { ToolProfile } from "./toolCatalog.js";
|
|
9
|
+
import type { ToolDef } from "./registry.js";
|
|
10
|
+
export type ToolRisk = "readonly" | "workspace_read_sensitive" | "workspace_write" | "command" | "release" | "credential_sensitive";
|
|
11
|
+
export declare const TOOL_RISK_RANK: Record<ToolRisk, number>;
|
|
12
|
+
export type ToolMode = "delegate" | "direct" | "audit" | "release" | "diagnostic";
|
|
13
|
+
export interface PatchWardenToolMeta {
|
|
14
|
+
name: string;
|
|
15
|
+
title: string;
|
|
16
|
+
summary: string;
|
|
17
|
+
description: string;
|
|
18
|
+
profiles: ToolProfile[];
|
|
19
|
+
modes: ToolMode[];
|
|
20
|
+
tags: string[];
|
|
21
|
+
aliases: string[];
|
|
22
|
+
risk: ToolRisk;
|
|
23
|
+
requiresConfirmation: boolean;
|
|
24
|
+
/** sha256 of canonical-JSON(inputSchema),用于检测 schema 漂移 */
|
|
25
|
+
inputSchemaDigest: string;
|
|
26
|
+
relatedTools?: string[];
|
|
27
|
+
examples?: string[];
|
|
28
|
+
}
|
|
29
|
+
export declare const INTENT_TERMS: Record<string, string[]>;
|
|
30
|
+
interface StaticToolMeta {
|
|
31
|
+
title: string;
|
|
32
|
+
summary: string;
|
|
33
|
+
profiles: ToolProfile[];
|
|
34
|
+
modes: ToolMode[];
|
|
35
|
+
tags: string[];
|
|
36
|
+
aliases: string[];
|
|
37
|
+
risk: ToolRisk;
|
|
38
|
+
requiresConfirmation: boolean;
|
|
39
|
+
relatedTools?: string[];
|
|
40
|
+
examples?: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 稳定 JSON 序列化:按字段名排序,避免 key 顺序导致 digest 漂移。
|
|
44
|
+
*/
|
|
45
|
+
export declare function stableJsonStringify(value: unknown): string;
|
|
46
|
+
/**
|
|
47
|
+
* 计算 inputSchema 的 sha256 digest。
|
|
48
|
+
* 返回格式:"sha256:<hex>"
|
|
49
|
+
*/
|
|
50
|
+
export declare function computeSchemaDigest(inputSchema: unknown): string;
|
|
51
|
+
/**
|
|
52
|
+
* 从 ToolDef[] 构建带元数据的完整工具注册表。
|
|
53
|
+
* 静态元数据来自 STATIC_TOOL_META,schema digest 动态计算。
|
|
54
|
+
* 未在 STATIC_TOOL_META 中注册的工具会被跳过(不报错,保持前向兼容)。
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildToolRegistry(tools: ToolDef[]): PatchWardenToolMeta[];
|
|
57
|
+
/**
|
|
58
|
+
* 获取单个工具的元数据(不含 schema digest,digest 需从 registry 获取)。
|
|
59
|
+
*/
|
|
60
|
+
export declare function getStaticToolMeta(name: string): StaticToolMeta | undefined;
|
|
61
|
+
export {};
|