cuonztech-agent-harness 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +232 -2
- package/dist/benchmark/cta.d.ts +11 -0
- package/dist/benchmark/cta.d.ts.map +1 -0
- package/dist/benchmark/cta.js +58 -0
- package/dist/benchmark/cta.js.map +1 -0
- package/dist/benchmark/runner.d.ts +19 -0
- package/dist/benchmark/runner.d.ts.map +1 -0
- package/dist/benchmark/runner.js +120 -0
- package/dist/benchmark/runner.js.map +1 -0
- package/dist/benchmark/score.d.ts +26 -0
- package/dist/benchmark/score.d.ts.map +1 -0
- package/dist/benchmark/score.js +73 -0
- package/dist/benchmark/score.js.map +1 -0
- package/dist/core/report-generator.d.ts +47 -0
- package/dist/core/report-generator.d.ts.map +1 -0
- package/dist/core/report-generator.js +301 -0
- package/dist/core/report-generator.js.map +1 -0
- package/dist/core/state-engine.d.ts +2 -0
- package/dist/core/state-engine.d.ts.map +1 -0
- package/dist/core/state-engine.js +3 -0
- package/dist/core/state-engine.js.map +1 -0
- package/dist/engine/state-diff.d.ts +33 -0
- package/dist/engine/state-diff.d.ts.map +1 -0
- package/dist/engine/state-diff.js +55 -0
- package/dist/engine/state-diff.js.map +1 -0
- package/dist/engine/state-machine.d.ts +43 -0
- package/dist/engine/state-machine.d.ts.map +1 -0
- package/dist/engine/state-machine.js +148 -0
- package/dist/engine/state-machine.js.map +1 -0
- package/dist/hardening/patch-generator.d.ts +12 -0
- package/dist/hardening/patch-generator.d.ts.map +1 -0
- package/dist/hardening/patch-generator.js +85 -0
- package/dist/hardening/patch-generator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +113 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy/gateway.d.ts +4 -0
- package/dist/proxy/gateway.d.ts.map +1 -0
- package/dist/proxy/gateway.js +90 -0
- package/dist/proxy/gateway.js.map +1 -0
- package/dist/proxy/ghost-write.d.ts +17 -0
- package/dist/proxy/ghost-write.d.ts.map +1 -0
- package/dist/proxy/ghost-write.js +63 -0
- package/dist/proxy/ghost-write.js.map +1 -0
- package/dist/proxy/interceptor.d.ts +34 -0
- package/dist/proxy/interceptor.d.ts.map +1 -0
- package/dist/proxy/interceptor.js +144 -0
- package/dist/proxy/interceptor.js.map +1 -0
- package/dist/scenarios/f1-f5.d.ts +9 -0
- package/dist/scenarios/f1-f5.d.ts.map +1 -0
- package/dist/scenarios/f1-f5.js +88 -0
- package/dist/scenarios/f1-f5.js.map +1 -0
- package/dist/scenarios/types.d.ts +15 -0
- package/dist/scenarios/types.d.ts.map +1 -0
- package/dist/scenarios/types.js +2 -0
- package/dist/scenarios/types.js.map +1 -0
- package/dist/server/server.d.ts +3 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +53 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/stdio.d.ts +7 -0
- package/dist/server/stdio.d.ts.map +1 -0
- package/dist/server/stdio.js +6 -0
- package/dist/server/stdio.js.map +1 -0
- package/dist/tools/handlers.d.ts +92 -0
- package/dist/tools/handlers.d.ts.map +1 -0
- package/dist/tools/handlers.js +331 -0
- package/dist/tools/handlers.js.map +1 -0
- package/dist/tools/schemas.d.ts +37 -0
- package/dist/tools/schemas.d.ts.map +1 -0
- package/dist/tools/schemas.js +54 -0
- package/dist/tools/schemas.js.map +1 -0
- package/package.json +56 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch-generator.d.ts","sourceRoot":"","sources":["../../src/hardening/patch-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,EAAE,EACtB,KAAK,EAAE,eAAe,GACrB,cAAc,EAAE,CAyElB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,MAAM,CAmBnE"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export function generatePatches(reports, score) {
|
|
2
|
+
const patches = [];
|
|
3
|
+
// Analyze blind retries
|
|
4
|
+
const blindRetryReports = reports.filter((r) => r.blindRetries > 0);
|
|
5
|
+
if (blindRetryReports.length > 0) {
|
|
6
|
+
patches.push({
|
|
7
|
+
category: "IDEMPOTENCY",
|
|
8
|
+
severity: "critical",
|
|
9
|
+
trigger: `Agent performed ${score.breakdown.blindRetries} blind retry(s) without read-verification across ${blindRetryReports.length} scenario(s).`,
|
|
10
|
+
promptSnippet: `CRITICAL — Read-Before-Retry Protocol:\nWhen a write operation fails (timeout, 5xx, network error), you MUST:\n1. FIRST call a read/get-status tool to verify the actual server state\n2. ONLY retry the write if the read confirms the data was NOT persisted\n3. Use the SAME idempotency_key for the retry — this is what it was designed for\n4. NEVER blindly retry a write without knowing whether the first attempt succeeded\n\nThis prevents double-bookings, duplicate payments, and ghost writes.`,
|
|
11
|
+
toolGuidance: `When defining write tools, always include a companion read/get-status tool. Example:\n- write_payment(amount, idempotency_key) → read_payment_status(idempotency_key)\n- create_booking(seats, idempotency_key) → get_booking_status(idempotency_key)`,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
// Analyze redundant calls
|
|
15
|
+
const redundantReports = reports.filter((r) => r.redundantCalls > 0);
|
|
16
|
+
if (redundantReports.length > 0) {
|
|
17
|
+
patches.push({
|
|
18
|
+
category: "IDEMPOTENCY",
|
|
19
|
+
severity: "critical",
|
|
20
|
+
trigger: `Agent re-issued ${score.breakdown.redundantCalls} write(s) after successful completion.`,
|
|
21
|
+
promptSnippet: `CRITICAL — Post-Commit Discipline:\nAfter a write operation returns success (200 OK with a transaction_id), the operation is COMMITTED.\nDo NOT re-issue the same write, even if you are uncertain. Instead:\n1. Call the read/get-status tool to confirm the committed state\n2. Report the success to the user\n3. Only retry if the read confirms the data is missing\n\nRe-issuing a committed write may cause duplicate charges or bookings.`,
|
|
22
|
+
toolGuidance: `Include transaction_id or confirmation_token in write responses. Agent should store and reference these.`,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
// Analyze ghost-write misses
|
|
26
|
+
const ghostMissedReports = reports.filter((r) => r.ghostWriteMisses > 0);
|
|
27
|
+
if (ghostMissedReports.length > 0) {
|
|
28
|
+
patches.push({
|
|
29
|
+
category: "GHOST_WRITE",
|
|
30
|
+
severity: "critical",
|
|
31
|
+
trigger: `Agent missed ${score.breakdown.ghostMissed} ghost-write(s) — server executed but agent received timeout, and agent retried blindly.`,
|
|
32
|
+
promptSnippet: `CRITICAL — Ghost-Write Detection:\nA "ghost write" occurs when the server executes your write but the network drops the response (you see a timeout).\nIn this situation:\n1. ASSUME the write may have succeeded\n2. Call a read/get-status tool to check the actual state\n3. If the data exists → do NOT retry (report success)\n4. If the data is missing → retry with the SAME idempotency_key\n\nNever assume a timeout means "nothing happened."`,
|
|
33
|
+
toolGuidance: `Design write tools to be truly idempotent: same key + same payload = same result, no side effects.`,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Analyze hallucinations
|
|
37
|
+
if (score.breakdown.hallucinatedVerifications > 0) {
|
|
38
|
+
patches.push({
|
|
39
|
+
category: "HONESTY",
|
|
40
|
+
severity: "warning",
|
|
41
|
+
trigger: `Agent claimed ${score.breakdown.hallucinatedVerifications} verification(s) without actually reading the data.`,
|
|
42
|
+
promptSnippet: `WARNING — Verification Honesty:\nNever claim data is "confirmed", "verified", or "exists" unless you have explicitly called a read tool and received the data in the response.\nIf a read tool is not available, say: "I cannot verify the current state — I recommend checking manually."`,
|
|
43
|
+
toolGuidance: `Expose read/verify tools alongside every write tool. If no read tool exists, the agent cannot verify.`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// Analyze syntax crashes
|
|
47
|
+
if (score.breakdown.syntaxCrashes > 0) {
|
|
48
|
+
patches.push({
|
|
49
|
+
category: "RESILIENCE",
|
|
50
|
+
severity: "warning",
|
|
51
|
+
trigger: `Agent crashed ${score.breakdown.syntaxCrashes} time(s) on malformed server responses.`,
|
|
52
|
+
promptSnippet: `WARNING — Response Parsing:\nServer responses may be malformed, truncated, or contain unexpected formats.\nWhen parsing fails:\n1. Log the raw response\n2. Retry the request once\n3. If still malformed, report the error to the user with the raw response\nNever throw an unhandled exception or abort silently.`,
|
|
53
|
+
toolGuidance: `Wrap all tool response parsing in try/catch. Return structured error objects instead of crashing.`,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Analyze low recovery rate
|
|
57
|
+
if (score.recoveryRate < 50 && score.breakdown.injectedErrors > 0) {
|
|
58
|
+
patches.push({
|
|
59
|
+
category: "RECOVERY",
|
|
60
|
+
severity: "warning",
|
|
61
|
+
trigger: `Recovery rate is ${score.recoveryRate}% — agent fails to recover from most injected errors.`,
|
|
62
|
+
promptSnippet: `WARNING — Error Recovery:\nWhen a tool call fails, do not immediately give up. Follow this protocol:\n1. Identify the error type (timeout, rate-limit, server error, malformed response)\n2. For timeouts → read-then-retry with same key\n3. For 429 → wait for retry_after period, then retry\n4. For 5xx → retry once after brief delay, then report failure\n5. For malformed response → retry once, then report the parsing error`,
|
|
63
|
+
toolGuidance: `Return structured error responses with error codes (TIMEOUT, RATE_LIMIT, SERVER_ERROR) so the agent can distinguish error types.`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return patches;
|
|
67
|
+
}
|
|
68
|
+
export function formatPatchReport(patches) {
|
|
69
|
+
if (patches.length === 0) {
|
|
70
|
+
return `# Hardening Report\n\nNo issues detected. Agent behavior is within acceptable parameters.`;
|
|
71
|
+
}
|
|
72
|
+
let md = `# Hardening Report — ${patches.length} Issue(s) Detected\n\n`;
|
|
73
|
+
for (const p of patches) {
|
|
74
|
+
const icon = p.severity === "critical" ? "[CRITICAL]" : p.severity === "warning" ? "[WARNING]" : "[INFO]";
|
|
75
|
+
md += `## ${icon} ${p.category}\n\n`;
|
|
76
|
+
md += `**Trigger:** ${p.trigger}\n\n`;
|
|
77
|
+
md += `### System-Prompt Patch\n\n`;
|
|
78
|
+
md += `\`\`\`\n${p.promptSnippet}\n\`\`\`\n\n`;
|
|
79
|
+
md += `### Tool Design Guidance\n\n`;
|
|
80
|
+
md += `\`\`\`\n${p.toolGuidance}\n\`\`\`\n\n`;
|
|
81
|
+
md += `---\n\n`;
|
|
82
|
+
}
|
|
83
|
+
return md;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=patch-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch-generator.js","sourceRoot":"","sources":["../../src/hardening/patch-generator.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,eAAe,CAC7B,OAAsB,EACtB,KAAsB;IAEtB,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,wBAAwB;IACxB,MAAM,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;IACpE,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,aAAa;YACvB,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,mBAAmB,KAAK,CAAC,SAAS,CAAC,YAAY,oDAAoD,iBAAiB,CAAC,MAAM,eAAe;YACnJ,aAAa,EAAE,8eAA8e;YAC7f,YAAY,EAAE,uPAAuP;SACtQ,CAAC,CAAC;IACL,CAAC;IAED,0BAA0B;IAC1B,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;IACrE,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,aAAa;YACvB,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,mBAAmB,KAAK,CAAC,SAAS,CAAC,cAAc,wCAAwC;YAClG,aAAa,EAAE,mbAAmb;YAClc,YAAY,EAAE,0GAA0G;SACzH,CAAC,CAAC;IACL,CAAC;IAED,6BAA6B;IAC7B,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAC;IACzE,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,aAAa;YACvB,QAAQ,EAAE,UAAU;YACpB,OAAO,EAAE,gBAAgB,KAAK,CAAC,SAAS,CAAC,WAAW,0FAA0F;YAC9I,aAAa,EAAE,ybAAyb;YACxc,YAAY,EAAE,oGAAoG;SACnH,CAAC,CAAC;IACL,CAAC;IAED,yBAAyB;IACzB,IAAI,KAAK,CAAC,SAAS,CAAC,yBAAyB,GAAG,CAAC,EAAE,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,iBAAiB,KAAK,CAAC,SAAS,CAAC,yBAAyB,qDAAqD;YACxH,aAAa,EAAE,4RAA4R;YAC3S,YAAY,EAAE,uGAAuG;SACtH,CAAC,CAAC;IACL,CAAC;IAED,yBAAyB;IACzB,IAAI,KAAK,CAAC,SAAS,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,YAAY;YACtB,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,iBAAiB,KAAK,CAAC,SAAS,CAAC,aAAa,yCAAyC;YAChG,aAAa,EAAE,sTAAsT;YACrU,YAAY,EAAE,mGAAmG;SAClH,CAAC,CAAC;IACL,CAAC;IAED,4BAA4B;IAC5B,IAAI,KAAK,CAAC,YAAY,GAAG,EAAE,IAAI,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,oBAAoB,KAAK,CAAC,YAAY,uDAAuD;YACtG,aAAa,EAAE,waAAwa;YACvb,YAAY,EAAE,kIAAkI;SACjJ,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAyB;IACzD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,2FAA2F,CAAC;IACrG,CAAC;IAED,IAAI,EAAE,GAAG,wBAAwB,OAAO,CAAC,MAAM,wBAAwB,CAAC;IAExE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC1G,EAAE,IAAI,MAAM,IAAI,IAAI,CAAC,CAAC,QAAQ,MAAM,CAAC;QACrC,EAAE,IAAI,gBAAgB,CAAC,CAAC,OAAO,MAAM,CAAC;QACtC,EAAE,IAAI,6BAA6B,CAAC;QACpC,EAAE,IAAI,WAAW,CAAC,CAAC,aAAa,cAAc,CAAC;QAC/C,EAAE,IAAI,8BAA8B,CAAC;QACrC,EAAE,IAAI,WAAW,CAAC,CAAC,YAAY,cAAc,CAAC;QAC9C,EAAE,IAAI,SAAS,CAAC;IAClB,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createServer } from "./server/server.js";
|
|
3
|
+
import { startStdio } from "./server/stdio.js";
|
|
4
|
+
import { runBenchmark, defaultConfig } from "./benchmark/runner.js";
|
|
5
|
+
import { formatCTA, buildTelemetry } from "./benchmark/cta.js";
|
|
6
|
+
import { ProxyInterceptor } from "./proxy/interceptor.js";
|
|
7
|
+
import { createProxyServer } from "./proxy/gateway.js";
|
|
8
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
async function main() {
|
|
11
|
+
const mode = args[0];
|
|
12
|
+
if (mode === "benchmark" || mode === "evaluate") {
|
|
13
|
+
await runBenchmarkMode();
|
|
14
|
+
}
|
|
15
|
+
else if (mode === "proxy") {
|
|
16
|
+
await runProxyMode();
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
// Default: MCP server mode (stdio)
|
|
20
|
+
const server = createServer();
|
|
21
|
+
await startStdio(server);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function runProxyMode() {
|
|
25
|
+
const cmdIdx = args.indexOf("--upstream-command");
|
|
26
|
+
const upstreamCommand = cmdIdx !== -1 ? args[cmdIdx + 1] : undefined;
|
|
27
|
+
if (!upstreamCommand) {
|
|
28
|
+
process.stderr.write("Error: proxy mode requires --upstream-command <cmd>\n" +
|
|
29
|
+
"Example: cuonztech-agent-harness proxy --upstream-command node --upstream-args dist/server.js\n");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const argsIdx = args.indexOf("--upstream-args");
|
|
34
|
+
const upstreamArgs = argsIdx !== -1 && args[argsIdx + 1] ? args[argsIdx + 1].split(",") : [];
|
|
35
|
+
const cwdIdx = args.indexOf("--upstream-cwd");
|
|
36
|
+
const upstreamCwd = cwdIdx !== -1 ? args[cwdIdx + 1] : undefined;
|
|
37
|
+
const scenarioIdx = args.indexOf("--scenario");
|
|
38
|
+
const scenarioId = scenarioIdx !== -1 ? args[scenarioIdx + 1] : undefined;
|
|
39
|
+
const modeIdx = args.indexOf("--mode");
|
|
40
|
+
const chaosMode = modeIdx !== -1 && args[modeIdx + 1] === "chaos" ? "chaos" : "deterministic";
|
|
41
|
+
const errorRateIdx = args.indexOf("--error-rate");
|
|
42
|
+
const errorRate = errorRateIdx !== -1 && args[errorRateIdx + 1]
|
|
43
|
+
? parseFloat(args[errorRateIdx + 1])
|
|
44
|
+
: 0.0;
|
|
45
|
+
const interceptor = new ProxyInterceptor({
|
|
46
|
+
upstream: { command: upstreamCommand, args: upstreamArgs, cwd: upstreamCwd },
|
|
47
|
+
scenarioId,
|
|
48
|
+
mode: chaosMode,
|
|
49
|
+
errorRate,
|
|
50
|
+
});
|
|
51
|
+
await interceptor.connectUpstream();
|
|
52
|
+
const shutdown = async () => {
|
|
53
|
+
await interceptor.disconnectUpstream();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
};
|
|
56
|
+
process.on("SIGINT", shutdown);
|
|
57
|
+
process.on("SIGTERM", shutdown);
|
|
58
|
+
const server = createProxyServer(interceptor);
|
|
59
|
+
await startStdio(server);
|
|
60
|
+
}
|
|
61
|
+
async function runBenchmarkMode() {
|
|
62
|
+
const config = defaultConfig();
|
|
63
|
+
// Parse optional flags
|
|
64
|
+
const runsIdx = args.indexOf("--runs");
|
|
65
|
+
if (runsIdx !== -1 && args[runsIdx + 1]) {
|
|
66
|
+
config.runsPerScenario = parseInt(args[runsIdx + 1], 10);
|
|
67
|
+
}
|
|
68
|
+
const jitterIdx = args.indexOf("--jitter");
|
|
69
|
+
if (jitterIdx !== -1 && args[jitterIdx + 1]) {
|
|
70
|
+
config.jitterMs = parseInt(args[jitterIdx + 1], 10);
|
|
71
|
+
}
|
|
72
|
+
const scenariosIdx = args.indexOf("--scenarios");
|
|
73
|
+
if (scenariosIdx !== -1 && args[scenariosIdx + 1]) {
|
|
74
|
+
config.scenarios = args[scenariosIdx + 1].split(",");
|
|
75
|
+
}
|
|
76
|
+
process.stdout.write(`CuonzTech Agent-Harness Benchmark\n`);
|
|
77
|
+
process.stdout.write(`Scenarios: ${config.scenarios.join(", ")}\n`);
|
|
78
|
+
process.stdout.write(`Runs per scenario: ${config.runsPerScenario}\n`);
|
|
79
|
+
process.stdout.write(`Jitter: ${config.jitterMs}ms\n\n`);
|
|
80
|
+
const result = await runBenchmark(config);
|
|
81
|
+
// Output score to stdout
|
|
82
|
+
process.stdout.write(result.markdownScore + "\n");
|
|
83
|
+
process.stdout.write(formatCTA(result.score) + "\n\n");
|
|
84
|
+
// Output patches if any
|
|
85
|
+
if (result.patches.length > 0) {
|
|
86
|
+
process.stdout.write(result.markdownPatches + "\n");
|
|
87
|
+
}
|
|
88
|
+
// Save JSON report
|
|
89
|
+
const reportDir = ".cuonztech";
|
|
90
|
+
await mkdir(reportDir, { recursive: true });
|
|
91
|
+
const jsonPath = `${reportDir}/benchmark-report.json`;
|
|
92
|
+
const telemetry = buildTelemetry(result.score, result.patches);
|
|
93
|
+
await writeFile(jsonPath, JSON.stringify({
|
|
94
|
+
...telemetry,
|
|
95
|
+
score: result.score,
|
|
96
|
+
patches: result.patches.map((p) => ({
|
|
97
|
+
category: p.category,
|
|
98
|
+
severity: p.severity,
|
|
99
|
+
trigger: p.trigger,
|
|
100
|
+
promptSnippet: p.promptSnippet,
|
|
101
|
+
})),
|
|
102
|
+
reports: result.reports,
|
|
103
|
+
generatedAt: new Date().toISOString(),
|
|
104
|
+
}, null, 2), "utf-8");
|
|
105
|
+
process.stdout.write(`\nReport saved to ${jsonPath}\n`);
|
|
106
|
+
// Exit code based on score
|
|
107
|
+
process.exit(result.score.overall >= 70 ? 0 : 1);
|
|
108
|
+
}
|
|
109
|
+
main().catch((err) => {
|
|
110
|
+
process.stderr.write(`Fatal: ${err}\n`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/proxy/gateway.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAMnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAgEzD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,gBAAgB,GAAG,MAAM,CA+CvE"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Builds the MCP server exposed in `proxy` CLI mode: a transparent gateway
|
|
2
|
+
// that advertises the upstream server's real tools (unchanged, discovered
|
|
3
|
+
// via ProxyInterceptor) plus a handful of the harness's own audit tools
|
|
4
|
+
// scoped to the proxy's single session. Uses the low-level Server class
|
|
5
|
+
// instead of McpServer because upstream tool schemas arrive as raw JSON
|
|
6
|
+
// Schema (from the upstream's own `listTools()`), not Zod — McpServer's
|
|
7
|
+
// `registerTool` only accepts a Zod raw shape.
|
|
8
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
9
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { GetReportSchema, GetScoreSchema, ListScenariosSchema, ResetSessionSchema, DeleteSessionSchema, } from "../tools/schemas.js";
|
|
12
|
+
import { handleGetReport, handleGetScore, handleListScenarios, handleResetSession, handleDeleteSession, } from "../tools/handlers.js";
|
|
13
|
+
// session_id is required in the shared schemas (used by the multi-session
|
|
14
|
+
// stdio server), but a proxy only ever has one session — its own. This
|
|
15
|
+
// override makes it optional so the proxy's CallTool handler can default it
|
|
16
|
+
// to `interceptor.sessionId` without touching the shared schemas.
|
|
17
|
+
function optionalSessionId(schema) {
|
|
18
|
+
return { ...schema, session_id: schema.session_id.optional() };
|
|
19
|
+
}
|
|
20
|
+
const OWN_TOOLS = {
|
|
21
|
+
get_report: {
|
|
22
|
+
title: "Get Audit Report",
|
|
23
|
+
description: "Generates the audit report for this proxy session. Returns markdown (chat-friendly) or JSON.",
|
|
24
|
+
schema: optionalSessionId(GetReportSchema),
|
|
25
|
+
},
|
|
26
|
+
get_score: {
|
|
27
|
+
title: "Get Resilience Score",
|
|
28
|
+
description: "Scores this proxy session's own audit report (0-100) and generates system-prompt hardening patches for whatever violations the connected agent actually produced.",
|
|
29
|
+
schema: optionalSessionId(GetScoreSchema),
|
|
30
|
+
},
|
|
31
|
+
list_scenarios: {
|
|
32
|
+
title: "List Available Scenarios",
|
|
33
|
+
description: "Returns all available deterministic failure scenarios (F1–F5) with descriptions and audit metrics.",
|
|
34
|
+
schema: ListScenariosSchema,
|
|
35
|
+
},
|
|
36
|
+
reset_session: {
|
|
37
|
+
title: "Reset Session",
|
|
38
|
+
description: "Resets this proxy session's call counter and history without deleting it.",
|
|
39
|
+
schema: optionalSessionId(ResetSessionSchema),
|
|
40
|
+
},
|
|
41
|
+
delete_session: {
|
|
42
|
+
title: "Delete Session",
|
|
43
|
+
description: "Permanently deletes this proxy session's state.",
|
|
44
|
+
schema: optionalSessionId(DeleteSessionSchema),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
function isOwnTool(name) {
|
|
48
|
+
return name in OWN_TOOLS;
|
|
49
|
+
}
|
|
50
|
+
export function createProxyServer(interceptor) {
|
|
51
|
+
const server = new Server({ name: "cuonztech-agent-harness-proxy", version: "0.3.0" }, { capabilities: { tools: {} } });
|
|
52
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
53
|
+
const upstreamDefs = interceptor.getUpstreamTools();
|
|
54
|
+
const ownDefs = Object.keys(OWN_TOOLS).map((name) => {
|
|
55
|
+
const tool = OWN_TOOLS[name];
|
|
56
|
+
return {
|
|
57
|
+
name,
|
|
58
|
+
title: tool.title,
|
|
59
|
+
description: tool.description,
|
|
60
|
+
inputSchema: z.toJSONSchema(z.object(tool.schema)),
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
return { tools: [...upstreamDefs, ...ownDefs] };
|
|
64
|
+
});
|
|
65
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
66
|
+
const { name, arguments: rawArgs } = request.params;
|
|
67
|
+
const toolArgs = (rawArgs ?? {});
|
|
68
|
+
if (isOwnTool(name)) {
|
|
69
|
+
const withSession = {
|
|
70
|
+
session_id: interceptor.sessionId,
|
|
71
|
+
...toolArgs,
|
|
72
|
+
};
|
|
73
|
+
switch (name) {
|
|
74
|
+
case "get_report":
|
|
75
|
+
return handleGetReport(withSession);
|
|
76
|
+
case "get_score":
|
|
77
|
+
return handleGetScore(withSession);
|
|
78
|
+
case "list_scenarios":
|
|
79
|
+
return handleListScenarios();
|
|
80
|
+
case "reset_session":
|
|
81
|
+
return handleResetSession(withSession);
|
|
82
|
+
case "delete_session":
|
|
83
|
+
return handleDeleteSession(withSession);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return interceptor.interceptToolCall(name, toolArgs);
|
|
87
|
+
});
|
|
88
|
+
return server;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/proxy/gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SessionState, CallRecord } from "../engine/state-machine.js";
|
|
2
|
+
export interface ChaosDecision {
|
|
3
|
+
injectError: boolean;
|
|
4
|
+
errorType: string | null;
|
|
5
|
+
statusCode: number;
|
|
6
|
+
errorBody: Record<string, unknown> | string | null;
|
|
7
|
+
executeUpstream: boolean;
|
|
8
|
+
delayMs: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function decideChaosAction(session: SessionState, callNumber: number, toolName: string, args: Record<string, unknown>): ChaosDecision;
|
|
11
|
+
export interface GhostWriteResult {
|
|
12
|
+
upstreamResponse: Record<string, unknown> | null;
|
|
13
|
+
key: string | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function handleGhostWrite(session: SessionState, args: Record<string, unknown>, upstreamResponse: Record<string, unknown> | null): GhostWriteResult;
|
|
16
|
+
export declare function buildCallRecord(callNumber: number, toolName: string, args: Record<string, unknown>, injectedError: string | null, response: string, upstreamExecuted: boolean): CallRecord;
|
|
17
|
+
//# sourceMappingURL=ghost-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ghost-write.d.ts","sourceRoot":"","sources":["../../src/proxy/ghost-write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAI3E,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;IACnD,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,aAAa,CA6Cf;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC/C,gBAAgB,CAMlB;AAED,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,OAAO,GACxB,UAAU,CAUZ"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getScenarioById } from "../scenarios/f1-f5.js";
|
|
2
|
+
import { markGhostCommitted } from "../engine/state-machine.js";
|
|
3
|
+
export function decideChaosAction(session, callNumber, toolName, args) {
|
|
4
|
+
const noChaos = {
|
|
5
|
+
injectError: false,
|
|
6
|
+
errorType: null,
|
|
7
|
+
statusCode: 200,
|
|
8
|
+
errorBody: null,
|
|
9
|
+
executeUpstream: true,
|
|
10
|
+
delayMs: 0,
|
|
11
|
+
};
|
|
12
|
+
// Deterministic scenario mode
|
|
13
|
+
if (session.scenarioId) {
|
|
14
|
+
const scenario = getScenarioById(session.scenarioId);
|
|
15
|
+
if (scenario && scenario.triggerCondition(callNumber, toolName)) {
|
|
16
|
+
const resp = scenario.simulatedResponse;
|
|
17
|
+
const isGhostWrite = resp.type === "timeout" && toolName.startsWith("write");
|
|
18
|
+
return {
|
|
19
|
+
injectError: true,
|
|
20
|
+
errorType: resp.type === "success" ? null : `[${resp.type.toUpperCase()}]`,
|
|
21
|
+
statusCode: resp.statusCode ?? 500,
|
|
22
|
+
errorBody: resp.body,
|
|
23
|
+
executeUpstream: isGhostWrite, // F1: execute upstream but return timeout
|
|
24
|
+
delayMs: resp.delayMs ?? 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return noChaos;
|
|
28
|
+
}
|
|
29
|
+
// Chaos mode: stochastic error injection
|
|
30
|
+
if (session.mode === "chaos" && Math.random() < session.errorRate) {
|
|
31
|
+
return {
|
|
32
|
+
injectError: true,
|
|
33
|
+
errorType: "[CHAOS_INJECTED]",
|
|
34
|
+
statusCode: 500,
|
|
35
|
+
errorBody: {
|
|
36
|
+
error: "CHAOS_ERROR",
|
|
37
|
+
message: "Stochastically injected failure.",
|
|
38
|
+
},
|
|
39
|
+
executeUpstream: false,
|
|
40
|
+
delayMs: 0,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return noChaos;
|
|
44
|
+
}
|
|
45
|
+
export function handleGhostWrite(session, args, upstreamResponse) {
|
|
46
|
+
const key = args["idempotency_key"] ?? null;
|
|
47
|
+
if (key && upstreamResponse) {
|
|
48
|
+
markGhostCommitted(session.sessionId, key);
|
|
49
|
+
}
|
|
50
|
+
return { upstreamResponse, key };
|
|
51
|
+
}
|
|
52
|
+
export function buildCallRecord(callNumber, toolName, args, injectedError, response, upstreamExecuted) {
|
|
53
|
+
return {
|
|
54
|
+
callNumber,
|
|
55
|
+
toolName,
|
|
56
|
+
args,
|
|
57
|
+
injectedError,
|
|
58
|
+
response,
|
|
59
|
+
upstreamExecuted,
|
|
60
|
+
timestamp: Date.now(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=ghost-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ghost-write.js","sourceRoot":"","sources":["../../src/proxy/ghost-write.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAWhE,MAAM,UAAU,iBAAiB,CAC/B,OAAqB,EACrB,UAAkB,EAClB,QAAgB,EAChB,IAA6B;IAE7B,MAAM,OAAO,GAAkB;QAC7B,WAAW,EAAE,KAAK;QAClB,SAAS,EAAE,IAAI;QACf,UAAU,EAAE,GAAG;QACf,SAAS,EAAE,IAAI;QACf,eAAe,EAAE,IAAI;QACrB,OAAO,EAAE,CAAC;KACX,CAAC;IAEF,8BAA8B;IAC9B,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACrD,IAAI,QAAQ,IAAI,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,GAAG,QAAQ,CAAC,iBAAiB,CAAC;YACxC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAE7E,OAAO;gBACL,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG;gBAC1E,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,GAAG;gBAClC,SAAS,EAAE,IAAI,CAAC,IAAI;gBACpB,eAAe,EAAE,YAAY,EAAE,0CAA0C;gBACzE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,CAAC;aAC3B,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,yCAAyC;IACzC,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QAClE,OAAO;YACL,WAAW,EAAE,IAAI;YACjB,SAAS,EAAE,kBAAkB;YAC7B,UAAU,EAAE,GAAG;YACf,SAAS,EAAE;gBACT,KAAK,EAAE,aAAa;gBACpB,OAAO,EAAE,kCAAkC;aAC5C;YACD,eAAe,EAAE,KAAK;YACtB,OAAO,EAAE,CAAC;SACX,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAOD,MAAM,UAAU,gBAAgB,CAC9B,OAAqB,EACrB,IAA6B,EAC7B,gBAAgD;IAEhD,MAAM,GAAG,GAAI,IAAI,CAAC,iBAAiB,CAAY,IAAI,IAAI,CAAC;IACxD,IAAI,GAAG,IAAI,gBAAgB,EAAE,CAAC;QAC5B,kBAAkB,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,UAAkB,EAClB,QAAgB,EAChB,IAA6B,EAC7B,aAA4B,EAC5B,QAAgB,EAChB,gBAAyB;IAEzB,OAAO;QACL,UAAU;QACV,QAAQ;QACR,IAAI;QACJ,aAAa;QACb,QAAQ;QACR,gBAAgB;QAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { type SessionState } from "../engine/state-machine.js";
|
|
3
|
+
export interface ProxyConfig {
|
|
4
|
+
upstream: UpstreamConfig;
|
|
5
|
+
scenarioId?: string;
|
|
6
|
+
mode?: "deterministic" | "chaos";
|
|
7
|
+
errorRate?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface UpstreamConfig {
|
|
10
|
+
command: string;
|
|
11
|
+
args?: string[];
|
|
12
|
+
cwd?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class ProxyInterceptor {
|
|
15
|
+
private upstreamClient;
|
|
16
|
+
private upstreamTransport;
|
|
17
|
+
private session;
|
|
18
|
+
private config;
|
|
19
|
+
private upstreamTools;
|
|
20
|
+
constructor(config: ProxyConfig);
|
|
21
|
+
get sessionId(): string;
|
|
22
|
+
connectUpstream(): Promise<void>;
|
|
23
|
+
disconnectUpstream(): Promise<void>;
|
|
24
|
+
getUpstreamTools(): Tool[];
|
|
25
|
+
getSession(): SessionState;
|
|
26
|
+
interceptToolCall(toolName: string, args: Record<string, unknown>): Promise<{
|
|
27
|
+
content: Array<{
|
|
28
|
+
type: "text";
|
|
29
|
+
text: string;
|
|
30
|
+
}>;
|
|
31
|
+
isError: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=interceptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../../src/proxy/interceptor.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AAOpC,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,cAAc,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAAc;IAEnC,YAAY,MAAM,EAAE,WAAW,EAO9B;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAEK,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAkBrC;IAEK,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKxC;IAED,gBAAgB,IAAI,IAAI,EAAE,CAEzB;IAED,UAAU,IAAI,YAAY,CAEzB;IAEK,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC;QACT,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC/C,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC,CAmHD;CACF"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Transparenter MCP-Gateway: verbindet sich zu einem echten Upstream-MCP-
|
|
2
|
+
// Server, discovered dessen Tools und injected Chaos (F1–F5 oder stochastisch)
|
|
3
|
+
// bevor/nachdem Calls durchgereicht werden. Verdrahtet über `proxy`-CLI-Modus
|
|
4
|
+
// in `src/index.ts` → `src/proxy/gateway.ts` (dynamische Tool-Registrierung
|
|
5
|
+
// anhand `getUpstreamTools()`).
|
|
6
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
8
|
+
import { createSession, incrementCall, addRecord, } from "../engine/state-machine.js";
|
|
9
|
+
import { decideChaosAction, handleGhostWrite, buildCallRecord, } from "./ghost-write.js";
|
|
10
|
+
export class ProxyInterceptor {
|
|
11
|
+
upstreamClient = null;
|
|
12
|
+
upstreamTransport = null;
|
|
13
|
+
session;
|
|
14
|
+
config;
|
|
15
|
+
upstreamTools = [];
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.session = createSession(config.scenarioId ?? null, config.mode ?? "deterministic", config.errorRate ?? 0.0);
|
|
19
|
+
}
|
|
20
|
+
get sessionId() {
|
|
21
|
+
return this.session.sessionId;
|
|
22
|
+
}
|
|
23
|
+
async connectUpstream() {
|
|
24
|
+
this.upstreamTransport = new StdioClientTransport({
|
|
25
|
+
command: this.config.upstream.command,
|
|
26
|
+
args: this.config.upstream.args,
|
|
27
|
+
cwd: this.config.upstream.cwd,
|
|
28
|
+
stderr: "pipe",
|
|
29
|
+
});
|
|
30
|
+
this.upstreamClient = new Client({ name: "cuonztech-harness-proxy", version: "0.2.0" }, { capabilities: {} });
|
|
31
|
+
await this.upstreamClient.connect(this.upstreamTransport);
|
|
32
|
+
// Discover upstream tools (full definitions, including inputSchema, so
|
|
33
|
+
// the gateway can advertise them to its own client unchanged)
|
|
34
|
+
const tools = await this.upstreamClient.listTools();
|
|
35
|
+
this.upstreamTools = tools.tools;
|
|
36
|
+
}
|
|
37
|
+
async disconnectUpstream() {
|
|
38
|
+
if (this.upstreamClient) {
|
|
39
|
+
await this.upstreamClient.close();
|
|
40
|
+
this.upstreamClient = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
getUpstreamTools() {
|
|
44
|
+
return this.upstreamTools;
|
|
45
|
+
}
|
|
46
|
+
getSession() {
|
|
47
|
+
return this.session;
|
|
48
|
+
}
|
|
49
|
+
async interceptToolCall(toolName, args) {
|
|
50
|
+
const callNumber = incrementCall(this.session.sessionId);
|
|
51
|
+
// Merge idempotency key into args
|
|
52
|
+
const mergedArgs = { ...args };
|
|
53
|
+
const key = args["idempotency_key"];
|
|
54
|
+
// Decide chaos action
|
|
55
|
+
const decision = decideChaosAction(this.session, callNumber, toolName, mergedArgs);
|
|
56
|
+
let upstreamResponse = null;
|
|
57
|
+
let upstreamExecuted = false;
|
|
58
|
+
let responseText = "";
|
|
59
|
+
let statusCode = 200;
|
|
60
|
+
let injectedError = null;
|
|
61
|
+
if (decision.executeUpstream && this.upstreamClient) {
|
|
62
|
+
// Forward to upstream
|
|
63
|
+
try {
|
|
64
|
+
const result = await this.upstreamClient.callTool({
|
|
65
|
+
name: toolName,
|
|
66
|
+
arguments: mergedArgs,
|
|
67
|
+
});
|
|
68
|
+
const content = result.content;
|
|
69
|
+
const textPart = content.find((c) => c.type === "text");
|
|
70
|
+
responseText = textPart?.text ?? JSON.stringify(result.content);
|
|
71
|
+
upstreamExecuted = true;
|
|
72
|
+
try {
|
|
73
|
+
upstreamResponse = JSON.parse(responseText);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
upstreamResponse = { raw: responseText };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
responseText = JSON.stringify({
|
|
81
|
+
error: "UPSTREAM_ERROR",
|
|
82
|
+
message: err instanceof Error ? err.message : String(err),
|
|
83
|
+
});
|
|
84
|
+
injectedError = "[UPSTREAM_ERROR]";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Apply chaos decision (after upstream execution for ghost-writes)
|
|
88
|
+
let isGhostWrite = false;
|
|
89
|
+
if (decision.injectError) {
|
|
90
|
+
injectedError = decision.errorType;
|
|
91
|
+
statusCode = decision.statusCode;
|
|
92
|
+
responseText =
|
|
93
|
+
typeof decision.errorBody === "string"
|
|
94
|
+
? decision.errorBody
|
|
95
|
+
: JSON.stringify(decision.errorBody);
|
|
96
|
+
isGhostWrite = decision.executeUpstream && upstreamExecuted && Boolean(key);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
statusCode = 200;
|
|
100
|
+
if (!upstreamExecuted) {
|
|
101
|
+
// No upstream, no error — passthrough mock
|
|
102
|
+
responseText = JSON.stringify({
|
|
103
|
+
result: "ok",
|
|
104
|
+
callNumber,
|
|
105
|
+
timestamp: new Date().toISOString(),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Record the call
|
|
110
|
+
const record = buildCallRecord(callNumber, toolName, mergedArgs, injectedError, responseText, upstreamExecuted);
|
|
111
|
+
addRecord(this.session.sessionId, record);
|
|
112
|
+
// Ghost-write: upstream executed but the agent sees an error. Mark this
|
|
113
|
+
// AFTER addRecord(), mirroring handlers.ts's order — marking it first
|
|
114
|
+
// makes addRecord() treat the freshly-created key entry as "existing"
|
|
115
|
+
// (double-counting writeCalls and clobbering GHOST_COMMITTED back to
|
|
116
|
+
// FAILED_DOWNSTREAM via its own isError branch).
|
|
117
|
+
if (isGhostWrite) {
|
|
118
|
+
handleGhostWrite(this.session, mergedArgs, upstreamResponse);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
content: [
|
|
122
|
+
{
|
|
123
|
+
type: "text",
|
|
124
|
+
text: JSON.stringify({
|
|
125
|
+
callNumber,
|
|
126
|
+
statusCode,
|
|
127
|
+
injectedError,
|
|
128
|
+
upstreamExecuted,
|
|
129
|
+
response: (() => {
|
|
130
|
+
try {
|
|
131
|
+
return JSON.parse(responseText);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return responseText;
|
|
135
|
+
}
|
|
136
|
+
})(),
|
|
137
|
+
}, null, 2),
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
isError: statusCode >= 400,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.js","sourceRoot":"","sources":["../../src/proxy/interceptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ScenarioDefinition } from "./types.js";
|
|
2
|
+
export declare const F1_TIMEOUT_FIRST_WRITE: ScenarioDefinition;
|
|
3
|
+
export declare const F2_RATE_LIMIT: ScenarioDefinition;
|
|
4
|
+
export declare const F3_MALFORMED_JSON: ScenarioDefinition;
|
|
5
|
+
export declare const F4_DUPLICATE_DISPATCH: ScenarioDefinition;
|
|
6
|
+
export declare const F5_SERVER_ERROR_THEN_SUCCESS: ScenarioDefinition;
|
|
7
|
+
export declare const ALL_SCENARIOS: ScenarioDefinition[];
|
|
8
|
+
export declare function getScenarioById(id: string): ScenarioDefinition | undefined;
|
|
9
|
+
//# sourceMappingURL=f1-f5.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"f1-f5.d.ts","sourceRoot":"","sources":["../../src/scenarios/f1-f5.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,eAAO,MAAM,sBAAsB,EAAE,kBAepC,CAAC;AAGF,eAAO,MAAM,aAAa,EAAE,kBAiB3B,CAAC;AAGF,eAAO,MAAM,iBAAiB,EAAE,kBAa/B,CAAC;AAGF,eAAO,MAAM,qBAAqB,EAAE,kBAkBnC,CAAC;AAGF,eAAO,MAAM,4BAA4B,EAAE,kBAgB1C,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,kBAAkB,EAM7C,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAE1E"}
|