patchwarden 1.1.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +41 -7
- package/README.md +36 -9
- package/dist/controlCenter.js +197 -1
- package/dist/direct/directSessionStore.d.ts +2 -0
- package/dist/direct/directVerification.js +7 -0
- package/dist/doctor.js +1 -1
- package/dist/policy/projectPolicy.d.ts +55 -0
- package/dist/policy/projectPolicy.js +286 -0
- package/dist/smoke-test.js +8 -8
- 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/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/tools/evidencePack.d.ts +39 -0
- package/dist/tools/evidencePack.js +168 -0
- package/dist/tools/recommendAgentForTask.d.ts +19 -0
- package/dist/tools/recommendAgentForTask.js +56 -0
- package/dist/tools/registry.js +376 -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/safeViews.d.ts +6 -0
- package/dist/tools/safeViews.js +2 -0
- 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 +110 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/docs/chatgpt-usage.md +31 -0
- package/docs/control-center/README.md +9 -0
- package/package.json +2 -2
- package/scripts/checks/control-center-smoke.js +87 -0
- package/scripts/checks/control-smoke.js +2 -2
- package/scripts/checks/mcp-manifest-check.js +12 -0
- package/scripts/checks/mcp-smoke.js +31 -7
- package/scripts/checks/watcher-supervisor-smoke.js +1 -1
- package/src/controlCenter.ts +198 -1
- package/src/direct/directSessionStore.ts +2 -0
- package/src/direct/directVerification.ts +7 -0
- package/src/doctor.ts +1 -1
- package/src/policy/projectPolicy.ts +344 -0
- package/src/smoke-test.ts +5 -5
- package/src/test/unit/evidence-pack.test.ts +142 -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/tools/evidencePack.ts +205 -0
- package/src/tools/listWorkspace.ts +71 -71
- package/src/tools/recommendAgentForTask.ts +79 -0
- package/src/tools/registry.ts +405 -2
- 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/safeViews.ts +2 -0
- package/src/tools/taskLineage.ts +300 -0
- package/src/tools/toolCatalog.ts +6 -0
- package/src/tools/toolRegistry.ts +110 -0
- package/src/version.ts +2 -2
- package/ui/pages/dashboard.html +143 -2
|
@@ -118,11 +118,14 @@ try {
|
|
|
118
118
|
"discover_tools",
|
|
119
119
|
"explain_tool",
|
|
120
120
|
"export_handoff",
|
|
121
|
+
"export_task_evidence_pack",
|
|
121
122
|
"finalize_direct_session",
|
|
122
123
|
"get_diff",
|
|
123
124
|
"get_plan",
|
|
125
|
+
"get_project_policy",
|
|
124
126
|
"get_result",
|
|
125
127
|
"get_result_json",
|
|
128
|
+
"get_task_lineage",
|
|
126
129
|
"get_task_log_tail",
|
|
127
130
|
"get_task_progress",
|
|
128
131
|
"get_task_status",
|
|
@@ -139,9 +142,16 @@ try {
|
|
|
139
142
|
"merge_worktree",
|
|
140
143
|
"read_goal",
|
|
141
144
|
"read_workspace_file",
|
|
145
|
+
"recommend_agent_for_task",
|
|
142
146
|
"reconcile_tasks",
|
|
143
147
|
"reject_subgoal",
|
|
148
|
+
"release_check",
|
|
149
|
+
"release_cleanup",
|
|
150
|
+
"release_prepare",
|
|
151
|
+
"release_verify",
|
|
144
152
|
"retry_task",
|
|
153
|
+
"run_direct_verification_bundle",
|
|
154
|
+
"run_task_loop",
|
|
145
155
|
"run_verification",
|
|
146
156
|
"safe_audit",
|
|
147
157
|
"safe_audit_direct_session",
|
|
@@ -167,7 +177,7 @@ try {
|
|
|
167
177
|
if (!tools._meta || typeof tools._meta.tool_manifest_sha256 !== "string" || tools._meta.tool_manifest_sha256.length !== 64) {
|
|
168
178
|
throw new Error(`tools/list _meta missing manifest hash: ${JSON.stringify(tools._meta || null)}`);
|
|
169
179
|
}
|
|
170
|
-
if (tools._meta.tool_profile !== "full" || tools._meta.tool_count !==
|
|
180
|
+
if (tools._meta.tool_profile !== "full" || tools._meta.tool_count !== 64) {
|
|
171
181
|
throw new Error(`tools/list _meta profile/count mismatch: ${JSON.stringify(tools._meta)}`);
|
|
172
182
|
}
|
|
173
183
|
if (typeof tools._meta.schema_epoch !== "string" || typeof tools._meta.server_version !== "string") {
|
|
@@ -417,7 +427,7 @@ try {
|
|
|
417
427
|
await disabledClient.close();
|
|
418
428
|
ok("chatgpt_direct disabled exposes only health_check with diagnostic");
|
|
419
429
|
|
|
420
|
-
// 2. chatgpt_direct enabled:
|
|
430
|
+
// 2. chatgpt_direct enabled: 14 tools + minimal create_direct_session
|
|
421
431
|
const enabledConfigPath = join(tempRoot, "direct-enabled.json");
|
|
422
432
|
const directRepo = join(workspaceRoot, "direct-fixture");
|
|
423
433
|
mkdirSync(join(directRepo, "src"), { recursive: true });
|
|
@@ -473,6 +483,7 @@ try {
|
|
|
473
483
|
"health_check",
|
|
474
484
|
"list_workspace",
|
|
475
485
|
"read_workspace_file",
|
|
486
|
+
"run_direct_verification_bundle",
|
|
476
487
|
"run_verification",
|
|
477
488
|
"safe_audit_direct_session",
|
|
478
489
|
"safe_direct_summary",
|
|
@@ -483,8 +494,8 @@ try {
|
|
|
483
494
|
if (JSON.stringify(enabledNames) !== JSON.stringify(expectedDirect)) {
|
|
484
495
|
throw new Error(`chatgpt_direct enabled tools mismatch: ${enabledNames.join(", ")}`);
|
|
485
496
|
}
|
|
486
|
-
if (enabledTools._meta.tool_count !==
|
|
487
|
-
throw new Error(`chatgpt_direct enabled tool_count should be
|
|
497
|
+
if (enabledTools._meta.tool_count !== 14) {
|
|
498
|
+
throw new Error(`chatgpt_direct enabled tool_count should be 14, got ${enabledTools._meta.tool_count}`);
|
|
488
499
|
}
|
|
489
500
|
|
|
490
501
|
// Minimal create_direct_session
|
|
@@ -497,6 +508,19 @@ try {
|
|
|
497
508
|
if (!sessionResult.session_id || !sessionResult.session_id.startsWith("direct_")) {
|
|
498
509
|
throw new Error(`create_direct_session failed: ${JSON.stringify(sessionResult)}`);
|
|
499
510
|
}
|
|
511
|
+
const bundleResult = JSON.parse(
|
|
512
|
+
(await enabledClient.callTool({
|
|
513
|
+
name: "run_direct_verification_bundle",
|
|
514
|
+
arguments: { session_id: sessionResult.session_id, commands: ["npm test"], timeout_seconds: 30 },
|
|
515
|
+
})).content?.[0]?.text || "{}"
|
|
516
|
+
);
|
|
517
|
+
if (bundleResult.status !== "passed" || bundleResult.command_count !== 1 || bundleResult.passed_commands !== 1) {
|
|
518
|
+
throw new Error(`run_direct_verification_bundle failed: ${JSON.stringify(bundleResult)}`);
|
|
519
|
+
}
|
|
520
|
+
const bundleText = JSON.stringify(bundleResult).toLowerCase();
|
|
521
|
+
if (bundleText.includes("stdout_tail") || bundleText.includes("stderr_tail") || bundleText.includes("verification.log")) {
|
|
522
|
+
throw new Error("run_direct_verification_bundle leaked log tail fields");
|
|
523
|
+
}
|
|
500
524
|
|
|
501
525
|
const enabledHealth = JSON.parse(
|
|
502
526
|
(await enabledClient.callTool({ name: "health_check", arguments: {} })).content?.[0]?.text || "{}"
|
|
@@ -504,12 +528,12 @@ try {
|
|
|
504
528
|
if (enabledHealth.direct_profile_enabled !== true) {
|
|
505
529
|
throw new Error(`direct_profile_enabled should be true, got ${enabledHealth.direct_profile_enabled}`);
|
|
506
530
|
}
|
|
507
|
-
if (enabledHealth.direct_tool_count !==
|
|
508
|
-
throw new Error(`direct_tool_count should be
|
|
531
|
+
if (enabledHealth.direct_tool_count !== 14) {
|
|
532
|
+
throw new Error(`direct_tool_count should be 14, got ${enabledHealth.direct_tool_count}`);
|
|
509
533
|
}
|
|
510
534
|
|
|
511
535
|
await enabledClient.close();
|
|
512
|
-
ok("chatgpt_direct enabled exposes
|
|
536
|
+
ok("chatgpt_direct enabled exposes 14 tools and create_direct_session works");
|
|
513
537
|
} catch (error) {
|
|
514
538
|
fail("MCP smoke test", error);
|
|
515
539
|
} finally {
|
package/src/controlCenter.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { createServer, get as httpGet, type IncomingMessage, type ServerResponse } from "node:http";
|
|
16
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
17
17
|
import { delimiter, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
19
|
import { randomUUID } from "node:crypto";
|
|
@@ -33,6 +33,10 @@ import { readWatcherStatus, type WatcherStatusSnapshot } from "./watcherStatus.j
|
|
|
33
33
|
import { redactSensitiveContent } from "./security/contentRedaction.js";
|
|
34
34
|
import { guardWorkspacePath } from "./security/pathGuard.js";
|
|
35
35
|
import { auditTask } from "./tools/auditTask.js";
|
|
36
|
+
import { getProjectPolicySummary } from "./policy/projectPolicy.js";
|
|
37
|
+
import { safeDirectSummary } from "./tools/safeViews.js";
|
|
38
|
+
import { toSafeTaskLineage, type TaskLineageRecord } from "./tools/taskLineage.js";
|
|
39
|
+
import { listEvidencePacks, readEvidencePack } from "./tools/evidencePack.js";
|
|
36
40
|
import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "./version.js";
|
|
37
41
|
|
|
38
42
|
// ── Paths ─────────────────────────────────────────────────────────
|
|
@@ -250,6 +254,25 @@ function readJsonFileSafe<T = unknown>(filePath: string): T | null {
|
|
|
250
254
|
}
|
|
251
255
|
}
|
|
252
256
|
|
|
257
|
+
function isPathInside(root: string, target: string): boolean {
|
|
258
|
+
const rel = relative(root, target);
|
|
259
|
+
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function readJsonFileSafeUnder<T = unknown>(root: string, relPath: string): T | null {
|
|
263
|
+
const base = resolve(root);
|
|
264
|
+
const target = resolve(base, relPath);
|
|
265
|
+
if (!isPathInside(base, target) || !existsSync(target)) return null;
|
|
266
|
+
try {
|
|
267
|
+
const realBase = realpathSync(base);
|
|
268
|
+
const realTarget = realpathSync(target);
|
|
269
|
+
if (!isPathInside(realBase, realTarget)) return null;
|
|
270
|
+
return readJsonFileSafe<T>(realTarget);
|
|
271
|
+
} catch {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
253
276
|
function readTextFileSafe(filePath: string): string | null {
|
|
254
277
|
if (!existsSync(filePath)) return null;
|
|
255
278
|
try {
|
|
@@ -1020,6 +1043,108 @@ function handleStaleTasks(res: ServerResponse): void {
|
|
|
1020
1043
|
}
|
|
1021
1044
|
}
|
|
1022
1045
|
|
|
1046
|
+
function handleLineages(res: ServerResponse): void {
|
|
1047
|
+
try {
|
|
1048
|
+
const root = join(config.workspaceRoot, ".patchwarden", "lineages");
|
|
1049
|
+
if (!existsSync(root)) {
|
|
1050
|
+
sendJson(res, 200, { lineages: [], total: 0, reason: null });
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
const lineages = readdirSync(root, { withFileTypes: true })
|
|
1054
|
+
.filter((entry) => entry.isDirectory())
|
|
1055
|
+
.map((entry) => readJsonFileSafeUnder<TaskLineageRecord>(root, join(entry.name, "lineage.json")))
|
|
1056
|
+
.filter((entry): entry is TaskLineageRecord => entry !== null)
|
|
1057
|
+
.map((entry) => toSafeTaskLineage(entry, 6))
|
|
1058
|
+
.sort((a, b) => b.updated_at.localeCompare(a.updated_at))
|
|
1059
|
+
.slice(0, 50);
|
|
1060
|
+
sendJson(res, 200, { lineages, total: lineages.length, reason: null });
|
|
1061
|
+
} catch (err) {
|
|
1062
|
+
sendJson(res, 200, { lineages: [], total: 0, reason: errorMessage(err) });
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
function handleLineageDetail(res: ServerResponse, lineageId: string): void {
|
|
1067
|
+
try {
|
|
1068
|
+
if (!/^[A-Za-z0-9_-]+$/.test(lineageId)) {
|
|
1069
|
+
sendJson(res, 400, { error: "Invalid lineage id" });
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
const data = readJsonFileSafeUnder<TaskLineageRecord>(
|
|
1073
|
+
join(config.workspaceRoot, ".patchwarden", "lineages"),
|
|
1074
|
+
join(lineageId, "lineage.json")
|
|
1075
|
+
);
|
|
1076
|
+
if (!data) {
|
|
1077
|
+
sendJson(res, 404, { error: "Lineage not found" });
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
sendJson(res, 200, toSafeTaskLineage(data, 20));
|
|
1081
|
+
} catch (err) {
|
|
1082
|
+
sendJson(res, 200, { lineage_id: lineageId, error: errorMessage(err) });
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
function handleProjectPolicy(res: ServerResponse, repoPath: string): void {
|
|
1087
|
+
try {
|
|
1088
|
+
sendJson(res, 200, getProjectPolicySummary(repoPath || "."));
|
|
1089
|
+
} catch (err) {
|
|
1090
|
+
sendJson(res, 200, {
|
|
1091
|
+
repo_path: repoPath || ".",
|
|
1092
|
+
valid: false,
|
|
1093
|
+
issues: [{ code: "policy_unavailable", severity: "error", field: "repo_path", message: errorMessage(err) }],
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function handleReleaseStatus(res: ServerResponse, repoPath: string): void {
|
|
1099
|
+
try {
|
|
1100
|
+
const policy = getProjectPolicySummary(repoPath || ".");
|
|
1101
|
+
sendJson(res, 200, {
|
|
1102
|
+
repo_path: repoPath || ".",
|
|
1103
|
+
resolved_repo_path: policy.resolved_repo_path,
|
|
1104
|
+
policy_valid: policy.valid,
|
|
1105
|
+
policy_issue_count: policy.issues.length,
|
|
1106
|
+
policy_issues: policy.issues.slice(0, 10),
|
|
1107
|
+
release_readiness: policy.release_readiness,
|
|
1108
|
+
next_action: policy.valid && policy.release_readiness.version_consistent !== false
|
|
1109
|
+
? "Run release_check or release_prepare from the full MCP profile when ready."
|
|
1110
|
+
: "Fix project-policy or version consistency issues before release preparation.",
|
|
1111
|
+
remote_write_performed: false,
|
|
1112
|
+
});
|
|
1113
|
+
} catch (err) {
|
|
1114
|
+
sendJson(res, 200, {
|
|
1115
|
+
repo_path: repoPath || ".",
|
|
1116
|
+
policy_valid: false,
|
|
1117
|
+
error: errorMessage(err),
|
|
1118
|
+
remote_write_performed: false,
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
function handleEvidencePacks(res: ServerResponse): void {
|
|
1124
|
+
try {
|
|
1125
|
+
sendJson(res, 200, listEvidencePacks({ max_items: 50 }));
|
|
1126
|
+
} catch (err) {
|
|
1127
|
+
sendJson(res, 200, { evidence_packs: [], total: 0, reason: errorMessage(err) });
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function handleEvidencePackDetail(res: ServerResponse, lineageId: string): void {
|
|
1132
|
+
try {
|
|
1133
|
+
if (!/^[A-Za-z0-9_-]+$/.test(lineageId)) {
|
|
1134
|
+
sendJson(res, 400, { error: "Invalid lineage id" });
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
const pack = readEvidencePack(lineageId);
|
|
1138
|
+
if (!pack) {
|
|
1139
|
+
sendJson(res, 404, { error: "Evidence pack not found" });
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
sendJson(res, 200, pack);
|
|
1143
|
+
} catch (err) {
|
|
1144
|
+
sendJson(res, 200, { lineage_id: lineageId, error: errorMessage(err), bounded: true });
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1023
1148
|
/**
|
|
1024
1149
|
* Reconcile a stale task. Does NOT delete the task. Reads the task files,
|
|
1025
1150
|
* decides whether it is safe to mark the task as stale/archived, writes a
|
|
@@ -1655,6 +1780,29 @@ function handleDirectSessionDetail(res: ServerResponse, sessionId: string): void
|
|
|
1655
1780
|
}
|
|
1656
1781
|
}
|
|
1657
1782
|
|
|
1783
|
+
function handleDirectSessionSafeSummary(res: ServerResponse, sessionId: string): void {
|
|
1784
|
+
try {
|
|
1785
|
+
if (
|
|
1786
|
+
sessionId === "." ||
|
|
1787
|
+
sessionId === ".." ||
|
|
1788
|
+
sessionId.includes("/") ||
|
|
1789
|
+
sessionId.includes("\\") ||
|
|
1790
|
+
sessionId.includes("\0")
|
|
1791
|
+
) {
|
|
1792
|
+
sendJson(res, 400, { error: "Invalid session id" });
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
sendJson(res, 200, safeDirectSummary(sessionId, { max_items: 12 }));
|
|
1796
|
+
} catch (err) {
|
|
1797
|
+
sendJson(res, 200, {
|
|
1798
|
+
session_id: sessionId,
|
|
1799
|
+
error: errorMessage(err),
|
|
1800
|
+
large_logs_omitted: true,
|
|
1801
|
+
diff_omitted: true,
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1658
1806
|
function parseReviewVerdict(content: string): string | null {
|
|
1659
1807
|
// independent-review.md format: "**Verdict**: PASS" (case-insensitive)
|
|
1660
1808
|
const m = content.match(/\*\*Verdict\*\*\s*:\s*([A-Za-z]+)/);
|
|
@@ -1952,6 +2100,44 @@ async function handleRequest(req: IncomingMessage, res: ServerResponse): Promise
|
|
|
1952
2100
|
handleStaleTasks(res);
|
|
1953
2101
|
return;
|
|
1954
2102
|
}
|
|
2103
|
+
if (method === "GET" && pathname === "/api/lineages") {
|
|
2104
|
+
handleLineages(res);
|
|
2105
|
+
return;
|
|
2106
|
+
}
|
|
2107
|
+
const lineageMatch = pathname.match(/^\/api\/lineages\/([^/]+)$/);
|
|
2108
|
+
if (method === "GET" && lineageMatch) {
|
|
2109
|
+
let lineageId: string;
|
|
2110
|
+
try {
|
|
2111
|
+
lineageId = decodeURIComponent(lineageMatch[1]);
|
|
2112
|
+
} catch {
|
|
2113
|
+
lineageId = lineageMatch[1];
|
|
2114
|
+
}
|
|
2115
|
+
handleLineageDetail(res, lineageId);
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
if (method === "GET" && pathname === "/api/project-policy") {
|
|
2119
|
+
handleProjectPolicy(res, parsedUrl.searchParams.get("repo_path") || ".");
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
if (method === "GET" && pathname === "/api/release/status") {
|
|
2123
|
+
handleReleaseStatus(res, parsedUrl.searchParams.get("repo_path") || ".");
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
if (method === "GET" && pathname === "/api/evidence-packs") {
|
|
2127
|
+
handleEvidencePacks(res);
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
const evidencePackMatch = pathname.match(/^\/api\/evidence-packs\/([^/]+)$/);
|
|
2131
|
+
if (method === "GET" && evidencePackMatch) {
|
|
2132
|
+
let lineageId: string;
|
|
2133
|
+
try {
|
|
2134
|
+
lineageId = decodeURIComponent(evidencePackMatch[1]);
|
|
2135
|
+
} catch {
|
|
2136
|
+
lineageId = evidencePackMatch[1];
|
|
2137
|
+
}
|
|
2138
|
+
handleEvidencePackDetail(res, lineageId);
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
1955
2141
|
const taskMatch = pathname.match(/^\/api\/tasks\/([^/]+)$/);
|
|
1956
2142
|
if (method === "GET" && taskMatch) {
|
|
1957
2143
|
let taskId: string;
|
|
@@ -2010,6 +2196,17 @@ async function handleRequest(req: IncomingMessage, res: ServerResponse): Promise
|
|
|
2010
2196
|
handleDirectSessions(res);
|
|
2011
2197
|
return;
|
|
2012
2198
|
}
|
|
2199
|
+
const directSessionSummaryMatch = pathname.match(/^\/api\/direct-sessions\/([^/]+)\/summary$/);
|
|
2200
|
+
if (method === "GET" && directSessionSummaryMatch) {
|
|
2201
|
+
let sessionId: string;
|
|
2202
|
+
try {
|
|
2203
|
+
sessionId = decodeURIComponent(directSessionSummaryMatch[1]);
|
|
2204
|
+
} catch {
|
|
2205
|
+
sessionId = directSessionSummaryMatch[1];
|
|
2206
|
+
}
|
|
2207
|
+
handleDirectSessionSafeSummary(res, sessionId);
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
2013
2210
|
const directSessionMatch = pathname.match(/^\/api\/direct-sessions\/([^/]+)$/);
|
|
2014
2211
|
if (method === "GET" && directSessionMatch) {
|
|
2015
2212
|
let sessionId: string;
|
|
@@ -69,6 +69,13 @@ export async function runDirectVerification(
|
|
|
69
69
|
exit_code: result.exitCode,
|
|
70
70
|
passed,
|
|
71
71
|
timed_out: result.timedOut,
|
|
72
|
+
redacted: stdoutRedacted.redacted || stderrRedacted.redacted,
|
|
73
|
+
redaction_categories: [
|
|
74
|
+
...new Set([
|
|
75
|
+
...stdoutRedacted.redaction_categories,
|
|
76
|
+
...stderrRedacted.redaction_categories,
|
|
77
|
+
]),
|
|
78
|
+
],
|
|
72
79
|
stdout_tail: stdoutTail,
|
|
73
80
|
stderr_tail: stderrTail,
|
|
74
81
|
started_at: startedAt,
|
package/src/doctor.ts
CHANGED
|
@@ -248,7 +248,7 @@ try {
|
|
|
248
248
|
const coreTools = selectToolsForProfile(fullTools, "chatgpt_core", config?.enableDirectProfile);
|
|
249
249
|
const createSchema = coreTools.find((tool) => tool.name === "create_task")?.inputSchema as any;
|
|
250
250
|
const waitSchema = coreTools.find((tool) => tool.name === "wait_for_task")?.inputSchema as any;
|
|
251
|
-
check("Full tool profile exposes
|
|
251
|
+
check("Full tool profile exposes 64 tools", fullTools.length === 64, `${fullTools.length} tools`);
|
|
252
252
|
check(
|
|
253
253
|
`chatgpt_core profile exposes the exact ${CHATGPT_CORE_TOOL_NAMES.length}-tool manifest`,
|
|
254
254
|
JSON.stringify(coreTools.map((tool) => tool.name)) === JSON.stringify(CHATGPT_CORE_TOOL_NAMES),
|