@martinloop/mcp 0.3.8 → 0.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.md +90 -106
- package/dist/discovery-metadata.d.ts +1 -1
- package/dist/discovery-metadata.js +1 -1
- package/dist/resources.d.ts +1 -2
- package/dist/resources.js +190 -238
- package/dist/server-validation.d.ts +1 -1
- package/dist/server-validation.js +16 -11
- package/dist/server.d.ts +23 -0
- package/dist/server.js +152 -7
- package/dist/tools/run-dossier.d.ts +3 -1
- package/dist/tools/run-dossier.js +40 -2
- package/dist/tools/run-loop.d.ts +1 -4
- package/dist/tools/run-loop.js +6 -15
- package/dist/tools/tool-support.d.ts +2 -1
- package/dist/tools/tool-support.js +1 -12
- package/dist/vendor/adapters/claude-cli.js +38 -35
- package/dist/vendor/adapters/cli-bridge.d.ts +9 -1
- package/dist/vendor/adapters/cli-bridge.js +54 -13
- package/dist/vendor/adapters/codex-launcher.d.ts +61 -1
- package/dist/vendor/adapters/codex-launcher.js +118 -19
- package/dist/vendor/adapters/index.d.ts +1 -3
- package/dist/vendor/adapters/index.js +1 -3
- package/dist/vendor/adapters/openai-compatible.js +57 -18
- package/dist/vendor/contracts/adapter-capabilities.d.ts +1 -1
- package/dist/vendor/contracts/context-handoff.d.ts +100 -0
- package/dist/vendor/contracts/context-handoff.js +11 -0
- package/dist/vendor/contracts/context-manifest.d.ts +195 -0
- package/dist/vendor/contracts/context-manifest.js +17 -0
- package/dist/vendor/contracts/context-shadow.d.ts +75 -0
- package/dist/vendor/contracts/context-shadow.js +8 -0
- package/dist/vendor/contracts/exits.d.ts +126 -0
- package/dist/vendor/contracts/exits.js +32 -0
- package/dist/vendor/contracts/index.d.ts +39 -2
- package/dist/vendor/contracts/index.js +22 -0
- package/dist/vendor/contracts/mission.d.ts +102 -0
- package/dist/vendor/contracts/mission.js +64 -0
- package/dist/vendor/contracts/operator.d.ts +3 -1
- package/dist/vendor/contracts/operator.js +2 -1
- package/dist/vendor/contracts/verified-handoff.d.ts +100 -0
- package/dist/vendor/contracts/verified-handoff.js +35 -0
- package/dist/vendor/core/context-chain-gate.d.ts +89 -0
- package/dist/vendor/core/context-chain-gate.js +173 -0
- package/dist/vendor/core/context-compiler.d.ts +53 -0
- package/dist/vendor/core/context-compiler.js +261 -0
- package/dist/vendor/core/context-handoff.d.ts +42 -0
- package/dist/vendor/core/context-handoff.js +167 -0
- package/dist/vendor/core/context-shadow.d.ts +39 -0
- package/dist/vendor/core/context-shadow.js +200 -0
- package/dist/vendor/core/exit-signal.d.ts +88 -0
- package/dist/vendor/core/exit-signal.js +339 -0
- package/dist/vendor/core/exits.d.ts +41 -0
- package/dist/vendor/core/exits.js +258 -0
- package/dist/vendor/core/index.d.ts +42 -1
- package/dist/vendor/core/index.js +1086 -662
- package/dist/vendor/core/mission/aggregate.d.ts +27 -0
- package/dist/vendor/core/mission/aggregate.js +42 -0
- package/dist/vendor/core/mission/index.d.ts +2 -0
- package/dist/vendor/core/mission/index.js +1 -0
- package/dist/vendor/core/persistence/compiler.d.ts +8 -0
- package/dist/vendor/core/persistence/compiler.js +156 -0
- package/dist/vendor/core/persistence/context-handoff-store.d.ts +68 -0
- package/dist/vendor/core/persistence/context-handoff-store.js +112 -0
- package/dist/vendor/core/persistence/index.d.ts +4 -0
- package/dist/vendor/core/persistence/index.js +2 -0
- package/dist/vendor/core/persistence/ledger.d.ts +1 -1
- package/dist/vendor/core/persistence/mission-store.d.ts +56 -0
- package/dist/vendor/core/persistence/mission-store.js +281 -0
- package/dist/vendor/core/policy.d.ts +2 -0
- package/dist/vendor/core/policy.js +5 -2
- package/dist/vendor/core/rollback.js +15 -1
- package/dist/vendor/core/savings.d.ts +13 -0
- package/dist/vendor/core/savings.js +22 -0
- package/dist/vendor/core/termination-store.d.ts +15 -0
- package/dist/vendor/core/termination-store.js +44 -0
- package/dist/vendor/core/verified-handoff.d.ts +67 -0
- package/dist/vendor/core/verified-handoff.js +210 -0
- package/package.json +76 -83
- package/server.json +21 -21
- package/LICENSE +0 -216
- package/dist/vendor/adapters/adapters/src/claude-cli.d.ts +0 -146
- package/dist/vendor/adapters/adapters/src/claude-cli.js +0 -1100
- package/dist/vendor/adapters/adapters/src/cli-bridge.d.ts +0 -103
- package/dist/vendor/adapters/adapters/src/cli-bridge.js +0 -569
- package/dist/vendor/adapters/adapters/src/codex-launcher.d.ts +0 -78
- package/dist/vendor/adapters/adapters/src/codex-launcher.js +0 -663
- package/dist/vendor/adapters/adapters/src/direct-provider.d.ts +0 -10
- package/dist/vendor/adapters/adapters/src/direct-provider.js +0 -40
- package/dist/vendor/adapters/adapters/src/index.d.ts +0 -8
- package/dist/vendor/adapters/adapters/src/index.js +0 -8
- package/dist/vendor/adapters/adapters/src/openai-compatible.d.ts +0 -62
- package/dist/vendor/adapters/adapters/src/openai-compatible.js +0 -306
- package/dist/vendor/adapters/adapters/src/runtime-support.d.ts +0 -17
- package/dist/vendor/adapters/adapters/src/runtime-support.js +0 -59
- package/dist/vendor/adapters/adapters/src/stub-agent-cli.d.ts +0 -8
- package/dist/vendor/adapters/adapters/src/stub-agent-cli.js +0 -40
- package/dist/vendor/adapters/adapters/src/stub-direct-provider.d.ts +0 -8
- package/dist/vendor/adapters/adapters/src/stub-direct-provider.js +0 -12
- package/dist/vendor/adapters/adapters/src/verifier-only.d.ts +0 -9
- package/dist/vendor/adapters/adapters/src/verifier-only.js +0 -63
- package/dist/vendor/adapters/contracts/src/context-graph.d.ts +0 -48
- package/dist/vendor/adapters/contracts/src/context-graph.js +0 -26
- package/dist/vendor/adapters/contracts/src/execution-policy.d.ts +0 -78
- package/dist/vendor/adapters/contracts/src/execution-policy.js +0 -43
- package/dist/vendor/adapters/contracts/src/governance.d.ts +0 -24
- package/dist/vendor/adapters/contracts/src/governance.js +0 -11
- package/dist/vendor/adapters/contracts/src/identity.d.ts +0 -29
- package/dist/vendor/adapters/contracts/src/identity.js +0 -30
- package/dist/vendor/adapters/contracts/src/index.d.ts +0 -412
- package/dist/vendor/adapters/contracts/src/index.js +0 -225
- package/dist/vendor/adapters/contracts/src/operator.d.ts +0 -19
- package/dist/vendor/adapters/contracts/src/operator.js +0 -11
- package/dist/vendor/adapters/contracts/src/trajectory.d.ts +0 -26
- package/dist/vendor/adapters/contracts/src/trajectory.js +0 -27
- package/dist/vendor/adapters/core/src/compiler.d.ts +0 -52
- package/dist/vendor/adapters/core/src/compiler.js +0 -52
- package/dist/vendor/adapters/core/src/context-graph.d.ts +0 -3
- package/dist/vendor/adapters/core/src/context-graph.js +0 -371
- package/dist/vendor/adapters/core/src/context-integrity.d.ts +0 -26
- package/dist/vendor/adapters/core/src/context-integrity.js +0 -80
- package/dist/vendor/adapters/core/src/grounding.d.ts +0 -38
- package/dist/vendor/adapters/core/src/grounding.js +0 -273
- package/dist/vendor/adapters/core/src/identity.d.ts +0 -20
- package/dist/vendor/adapters/core/src/identity.js +0 -86
- package/dist/vendor/adapters/core/src/index.d.ts +0 -186
- package/dist/vendor/adapters/core/src/index.js +0 -1495
- package/dist/vendor/adapters/core/src/leash.d.ts +0 -48
- package/dist/vendor/adapters/core/src/leash.js +0 -497
- package/dist/vendor/adapters/core/src/persistence/compiler.d.ts +0 -18
- package/dist/vendor/adapters/core/src/persistence/compiler.js +0 -34
- package/dist/vendor/adapters/core/src/persistence/index.d.ts +0 -10
- package/dist/vendor/adapters/core/src/persistence/index.js +0 -5
- package/dist/vendor/adapters/core/src/persistence/integrity.d.ts +0 -39
- package/dist/vendor/adapters/core/src/persistence/integrity.js +0 -252
- package/dist/vendor/adapters/core/src/persistence/ledger.d.ts +0 -23
- package/dist/vendor/adapters/core/src/persistence/ledger.js +0 -9
- package/dist/vendor/adapters/core/src/persistence/memory-store.d.ts +0 -63
- package/dist/vendor/adapters/core/src/persistence/memory-store.js +0 -115
- package/dist/vendor/adapters/core/src/persistence/runs-reader.d.ts +0 -66
- package/dist/vendor/adapters/core/src/persistence/runs-reader.js +0 -140
- package/dist/vendor/adapters/core/src/persistence/store.d.ts +0 -89
- package/dist/vendor/adapters/core/src/persistence/store.js +0 -125
- package/dist/vendor/adapters/core/src/persistence/trace-store.d.ts +0 -85
- package/dist/vendor/adapters/core/src/persistence/trace-store.js +0 -125
- package/dist/vendor/adapters/core/src/policy-compiler.d.ts +0 -2
- package/dist/vendor/adapters/core/src/policy-compiler.js +0 -195
- package/dist/vendor/adapters/core/src/policy.d.ts +0 -156
- package/dist/vendor/adapters/core/src/policy.js +0 -704
- package/dist/vendor/adapters/core/src/repo-analyzer.d.ts +0 -53
- package/dist/vendor/adapters/core/src/repo-analyzer.js +0 -263
- package/dist/vendor/adapters/core/src/rollback.d.ts +0 -15
- package/dist/vendor/adapters/core/src/rollback.js +0 -253
- package/dist/vendor/adapters/core/src/routing.d.ts +0 -82
- package/dist/vendor/adapters/core/src/routing.js +0 -336
- package/dist/vendor/adapters/core/src/test-integrity.d.ts +0 -64
- package/dist/vendor/adapters/core/src/test-integrity.js +0 -197
- package/dist/vendor/adapters/core/src/trajectory.d.ts +0 -18
- package/dist/vendor/adapters/core/src/trajectory.js +0 -295
- package/dist/vendor/adapters/stub-agent-cli.d.ts +0 -8
- package/dist/vendor/adapters/stub-agent-cli.js +0 -40
- package/dist/vendor/adapters/verifier-only.d.ts +0 -9
- package/dist/vendor/adapters/verifier-only.js +0 -63
package/dist/server.js
CHANGED
|
@@ -16,12 +16,14 @@
|
|
|
16
16
|
* Manual start:
|
|
17
17
|
* node dist/server.js
|
|
18
18
|
*/
|
|
19
|
+
import { createServer as createHttpServer } from "node:http";
|
|
19
20
|
import { fileURLToPath } from "node:url";
|
|
20
21
|
import { realpathSync } from "node:fs";
|
|
21
22
|
import path from "node:path";
|
|
22
23
|
import { resolveRunsRoot } from "./vendor/core/index.js";
|
|
23
24
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
24
25
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
26
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
25
27
|
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
26
28
|
import { MARTIN_MCP_PACKAGE_VERSION } from "./package-version.js";
|
|
27
29
|
import { getMartinPrompt, listMartinPrompts } from "./prompts.js";
|
|
@@ -1401,6 +1403,10 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1401
1403
|
type: "array",
|
|
1402
1404
|
items: { type: "string" },
|
|
1403
1405
|
description: "Optional file paths to scope the estimate."
|
|
1406
|
+
},
|
|
1407
|
+
workingDirectory: {
|
|
1408
|
+
type: "string",
|
|
1409
|
+
description: "Optional workspace path for recording the estimate receipt against the same task root you plan to run."
|
|
1404
1410
|
}
|
|
1405
1411
|
},
|
|
1406
1412
|
required: ["objective"]
|
|
@@ -1518,7 +1524,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1518
1524
|
workingDirectory: output.environment.workingDirectory,
|
|
1519
1525
|
engine: input.engine,
|
|
1520
1526
|
receiptScope: output.receiptScope
|
|
1521
|
-
});
|
|
1527
|
+
}).catch(() => { });
|
|
1522
1528
|
return createToolSuccessResult(output, output.summary);
|
|
1523
1529
|
}
|
|
1524
1530
|
if (name === "martin_plan") {
|
|
@@ -1535,7 +1541,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1535
1541
|
repoRoot: output.workingDirectory,
|
|
1536
1542
|
runsRoot: resolveRunsRoot(process.env)
|
|
1537
1543
|
}
|
|
1538
|
-
});
|
|
1544
|
+
}).catch(() => { });
|
|
1539
1545
|
return createToolSuccessResult(output, `Plan ready for ${output.objective} with ${output.risk.level} risk and ${output.approvalRecommendation.replace(/_/gu, " ")} approval.`);
|
|
1540
1546
|
}
|
|
1541
1547
|
if (name === "martin_preflight") {
|
|
@@ -1553,7 +1559,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1553
1559
|
allowedPaths: output.normalized.allowedPaths,
|
|
1554
1560
|
deniedPaths: output.normalized.deniedPaths,
|
|
1555
1561
|
budget: output.normalized.budget
|
|
1556
|
-
});
|
|
1562
|
+
}).catch(() => { });
|
|
1557
1563
|
}
|
|
1558
1564
|
return createToolSuccessResult(output, output.summary);
|
|
1559
1565
|
}
|
|
@@ -1640,8 +1646,8 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1640
1646
|
const engine = input.engine ?? "claude";
|
|
1641
1647
|
const budgetUsd = input.budgetUsd ?? 5;
|
|
1642
1648
|
const fileScope = input.fileScope ?? [];
|
|
1643
|
-
const workingDirectory = resolveSafeRepoRoot();
|
|
1644
1649
|
const runsRoot = resolveRunsRoot(process.env);
|
|
1650
|
+
const workingDirectory = input.workingDirectory ?? resolveSafeRepoRoot();
|
|
1645
1651
|
const route = classifyRoute({
|
|
1646
1652
|
objective,
|
|
1647
1653
|
verificationPlan: [],
|
|
@@ -1673,8 +1679,14 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1673
1679
|
step: "estimate",
|
|
1674
1680
|
workingDirectory,
|
|
1675
1681
|
objective,
|
|
1676
|
-
engine
|
|
1677
|
-
|
|
1682
|
+
engine,
|
|
1683
|
+
receiptScope: {
|
|
1684
|
+
invocationRoot: resolveSafeRepoRoot(),
|
|
1685
|
+
workingDirectory,
|
|
1686
|
+
repoRoot: workingDirectory,
|
|
1687
|
+
runsRoot
|
|
1688
|
+
}
|
|
1689
|
+
}).catch(() => { });
|
|
1678
1690
|
return createToolSuccessResult(output, `Estimate: ${route.selectedMode} route (${route.recommendedModelTier}), ~$${route.expectedCostUsd.toFixed(2)} expected cost, ${route.expectedPreworkBurnPct}% pre-work burn. Recommended budget: $${recommendedBudgetUsd.toFixed(2)}.`);
|
|
1679
1691
|
}
|
|
1680
1692
|
return createToolErrorResult(toToolFailure(new Error(`Unknown tool: ${name}`)));
|
|
@@ -1698,6 +1710,114 @@ export async function connectMartinMcpStdioServer() {
|
|
|
1698
1710
|
await server.connect(transport);
|
|
1699
1711
|
return server;
|
|
1700
1712
|
}
|
|
1713
|
+
export function parseMartinMcpServerArgs(argv) {
|
|
1714
|
+
let transport = "stdio";
|
|
1715
|
+
let host = "127.0.0.1";
|
|
1716
|
+
let port = 3033;
|
|
1717
|
+
let path = "/mcp";
|
|
1718
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1719
|
+
const token = argv[index];
|
|
1720
|
+
if (token === "--http") {
|
|
1721
|
+
transport = "http";
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
if (token === "--port") {
|
|
1725
|
+
const next = argv[index + 1];
|
|
1726
|
+
const parsed = Number(next);
|
|
1727
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > 65535) {
|
|
1728
|
+
throw new Error(`Invalid --port value '${next ?? ""}'.`);
|
|
1729
|
+
}
|
|
1730
|
+
port = parsed;
|
|
1731
|
+
index += 1;
|
|
1732
|
+
continue;
|
|
1733
|
+
}
|
|
1734
|
+
if (token === "--host") {
|
|
1735
|
+
const next = argv[index + 1];
|
|
1736
|
+
if (!next) {
|
|
1737
|
+
throw new Error("Missing value for --host.");
|
|
1738
|
+
}
|
|
1739
|
+
host = next;
|
|
1740
|
+
index += 1;
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
if (token === "--path") {
|
|
1744
|
+
const next = argv[index + 1];
|
|
1745
|
+
if (!next || !next.startsWith("/")) {
|
|
1746
|
+
throw new Error("MCP HTTP path must start with '/'.");
|
|
1747
|
+
}
|
|
1748
|
+
path = next;
|
|
1749
|
+
index += 1;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
return transport === "http"
|
|
1753
|
+
? { transport, host, port, path }
|
|
1754
|
+
: { transport };
|
|
1755
|
+
}
|
|
1756
|
+
export async function connectMartinMcpHttpServer(options = {}) {
|
|
1757
|
+
const host = options.host ?? "127.0.0.1";
|
|
1758
|
+
const port = options.port ?? 3033;
|
|
1759
|
+
const path = options.path ?? "/mcp";
|
|
1760
|
+
const mcpServer = createMartinMcpServer();
|
|
1761
|
+
const transport = new StreamableHTTPServerTransport({
|
|
1762
|
+
sessionIdGenerator: undefined
|
|
1763
|
+
});
|
|
1764
|
+
await mcpServer.connect(transport);
|
|
1765
|
+
const httpServer = createHttpServer(async (req, res) => {
|
|
1766
|
+
try {
|
|
1767
|
+
const requestUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? `${host}:${port}`}`);
|
|
1768
|
+
if (requestUrl.pathname !== path) {
|
|
1769
|
+
res.statusCode = 404;
|
|
1770
|
+
res.setHeader("content-type", "application/json");
|
|
1771
|
+
res.end(JSON.stringify({ error: "not_found", path: requestUrl.pathname }));
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
if (req.method === "POST") {
|
|
1775
|
+
const parsedBody = await readJsonBody(req, res);
|
|
1776
|
+
if (res.writableEnded) {
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
await transport.handleRequest(req, res, parsedBody);
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
if (req.method === "GET" || req.method === "DELETE") {
|
|
1783
|
+
await transport.handleRequest(req, res);
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
res.statusCode = 405;
|
|
1787
|
+
res.setHeader("allow", "GET, POST, DELETE");
|
|
1788
|
+
res.end();
|
|
1789
|
+
}
|
|
1790
|
+
catch (error) {
|
|
1791
|
+
if (!res.headersSent) {
|
|
1792
|
+
res.statusCode = 500;
|
|
1793
|
+
res.setHeader("content-type", "application/json");
|
|
1794
|
+
res.end(JSON.stringify({ error: "internal_error", message: error.message }));
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
});
|
|
1798
|
+
await new Promise((resolve, reject) => {
|
|
1799
|
+
httpServer.once("error", reject);
|
|
1800
|
+
httpServer.listen(port, host, () => {
|
|
1801
|
+
httpServer.off("error", reject);
|
|
1802
|
+
resolve();
|
|
1803
|
+
});
|
|
1804
|
+
});
|
|
1805
|
+
const address = httpServer.address();
|
|
1806
|
+
const resolvedPort = typeof address === "object" && address ? address.port : port;
|
|
1807
|
+
return {
|
|
1808
|
+
server: httpServer,
|
|
1809
|
+
host,
|
|
1810
|
+
port: resolvedPort,
|
|
1811
|
+
path,
|
|
1812
|
+
endpoint: `http://${host}:${resolvedPort}${path}`,
|
|
1813
|
+
close: async () => {
|
|
1814
|
+
await transport.close();
|
|
1815
|
+
await new Promise((resolve) => {
|
|
1816
|
+
httpServer.close(() => resolve());
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1701
1821
|
export function isDirectExecutionEntry(entryPath, moduleUrl = import.meta.url) {
|
|
1702
1822
|
if (typeof entryPath !== "string" || entryPath.length === 0) {
|
|
1703
1823
|
return false;
|
|
@@ -1718,5 +1838,30 @@ function realPathOrResolved(filePath) {
|
|
|
1718
1838
|
}
|
|
1719
1839
|
}
|
|
1720
1840
|
if (isDirectExecution()) {
|
|
1721
|
-
|
|
1841
|
+
const startup = parseMartinMcpServerArgs(process.argv.slice(2));
|
|
1842
|
+
if (startup.transport === "http") {
|
|
1843
|
+
await connectMartinMcpHttpServer(startup);
|
|
1844
|
+
}
|
|
1845
|
+
else {
|
|
1846
|
+
await connectMartinMcpStdioServer();
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
async function readJsonBody(req, res) {
|
|
1850
|
+
const chunks = [];
|
|
1851
|
+
for await (const chunk of req) {
|
|
1852
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
1853
|
+
}
|
|
1854
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
1855
|
+
if (!raw) {
|
|
1856
|
+
return undefined;
|
|
1857
|
+
}
|
|
1858
|
+
try {
|
|
1859
|
+
return JSON.parse(raw);
|
|
1860
|
+
}
|
|
1861
|
+
catch {
|
|
1862
|
+
res.statusCode = 400;
|
|
1863
|
+
res.setHeader("content-type", "application/json");
|
|
1864
|
+
res.end(JSON.stringify({ error: "invalid_json" }));
|
|
1865
|
+
return undefined;
|
|
1866
|
+
}
|
|
1722
1867
|
}
|
|
@@ -2,7 +2,7 @@ import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildEven
|
|
|
2
2
|
import { readRunControlState } from "./run-controls.js";
|
|
3
3
|
import { martinEvalTool } from "./eval.js";
|
|
4
4
|
import { assessRunRisk } from "./workflow-governance.js";
|
|
5
|
-
import type { ReceiptIntegritySummary, ReceiptScope } from "../vendor/contracts/index.js";
|
|
5
|
+
import type { ReceiptIntegritySummary, ReceiptScope, TerminationEnvelopeV1, VerifiedHandoffV1 } from "../vendor/contracts/index.js";
|
|
6
6
|
export interface MartinRunDossierInput {
|
|
7
7
|
file?: string;
|
|
8
8
|
loopId?: string;
|
|
@@ -45,6 +45,8 @@ export interface MartinRunDossierOutput {
|
|
|
45
45
|
};
|
|
46
46
|
evaluation: Awaited<ReturnType<typeof martinEvalTool>>;
|
|
47
47
|
control: Awaited<ReturnType<typeof readRunControlState>>;
|
|
48
|
+
terminationEnvelope?: TerminationEnvelopeV1;
|
|
49
|
+
verifiedHandoff: VerifiedHandoffV1;
|
|
48
50
|
format: "json" | "md" | "github-pr";
|
|
49
51
|
rendered?: string;
|
|
50
52
|
inspection: {
|
|
@@ -4,6 +4,7 @@ import { loadDetailedLoopRecord, readAttemptArtifactFiles, readLedgerEvents } fr
|
|
|
4
4
|
import { readRunControlState } from "./run-controls.js";
|
|
5
5
|
import { martinEvalTool } from "./eval.js";
|
|
6
6
|
import { assessRunRisk, inspectRepoSignals } from "./workflow-governance.js";
|
|
7
|
+
import { buildVerifiedHandoff } from "../vendor/core/index.js";
|
|
7
8
|
export async function martinRunDossierTool(input) {
|
|
8
9
|
const detail = await loadDetailedLoopRecord(input);
|
|
9
10
|
const ledgerEvents = await readLedgerEvents(detail);
|
|
@@ -54,6 +55,7 @@ export async function martinRunDossierTool(input) {
|
|
|
54
55
|
cost: buildCostSnapshot(detail.loop.cost),
|
|
55
56
|
receiptIntegrity: resolveReceiptIntegrity(detail.loop),
|
|
56
57
|
...(detail.loop.receiptScope ? { receiptScope: detail.loop.receiptScope } : {}),
|
|
58
|
+
...(detail.loop.terminationEnvelope ? { terminationEnvelope: detail.loop.terminationEnvelope } : {}),
|
|
57
59
|
attempts,
|
|
58
60
|
verification,
|
|
59
61
|
artifacts: buildArtifactSummary(detail.loop),
|
|
@@ -72,7 +74,37 @@ export async function martinRunDossierTool(input) {
|
|
|
72
74
|
...(detail.canonicalLoopRecordPath ? { canonicalLoopRecordPath: detail.canonicalLoopRecordPath } : {}),
|
|
73
75
|
...(detail.ledgerPath ? { ledgerPath: detail.ledgerPath } : {})
|
|
74
76
|
},
|
|
75
|
-
warnings: [...detail.warnings, ...verification.warnings]
|
|
77
|
+
warnings: [...detail.warnings, ...verification.warnings],
|
|
78
|
+
verifiedHandoff: buildVerifiedHandoff({
|
|
79
|
+
loop: detail.loop,
|
|
80
|
+
receiptIntegrity: resolveReceiptIntegrity(detail.loop),
|
|
81
|
+
verification: {
|
|
82
|
+
status: verification.status,
|
|
83
|
+
summary: verification.summary ?? "No verification summary recorded.",
|
|
84
|
+
steps: [],
|
|
85
|
+
warnings: verification.warnings,
|
|
86
|
+
},
|
|
87
|
+
scope: {
|
|
88
|
+
status: detail.loop.task?.allowedPaths?.length ||
|
|
89
|
+
detail.loop.task?.deniedPaths?.length
|
|
90
|
+
? "WITHIN_SCOPE"
|
|
91
|
+
: "NOT_EVALUATED",
|
|
92
|
+
allowedPaths: detail.loop.task?.allowedPaths ?? [],
|
|
93
|
+
deniedPaths: detail.loop.task?.deniedPaths ?? [],
|
|
94
|
+
changedFiles: [],
|
|
95
|
+
violations: [],
|
|
96
|
+
},
|
|
97
|
+
testIntegrity: {
|
|
98
|
+
status: "NOT_EVALUATED",
|
|
99
|
+
verdict: "NOT_EVALUATED",
|
|
100
|
+
protectedPaths: [],
|
|
101
|
+
changedProtectedPaths: [],
|
|
102
|
+
findings: [],
|
|
103
|
+
summary: "Automatic test-integrity evidence was not recorded for this run.",
|
|
104
|
+
},
|
|
105
|
+
unresolvedWork: verification.status === "passed" ? [] : [verification.summary ?? "Verification did not pass."],
|
|
106
|
+
nextAction: review.nextAction,
|
|
107
|
+
}),
|
|
76
108
|
};
|
|
77
109
|
if (output.format !== "json") {
|
|
78
110
|
output.rendered = renderDossier(output);
|
|
@@ -81,7 +113,13 @@ export async function martinRunDossierTool(input) {
|
|
|
81
113
|
}
|
|
82
114
|
function renderDossier(output) {
|
|
83
115
|
const lines = [
|
|
84
|
-
output.format === "github-pr" ? "## MartinLoop
|
|
116
|
+
output.format === "github-pr" ? "## MartinLoop Verified Handoff" : "# MartinLoop Verified Handoff",
|
|
117
|
+
"",
|
|
118
|
+
`Outcome: ${output.verifiedHandoff.outcome}`,
|
|
119
|
+
`Definition of Done: ${output.verifiedHandoff.definitionOfDone.acceptanceCriteria.length} acceptance criterion/criteria`,
|
|
120
|
+
`Verification: ${output.verifiedHandoff.verification.status}`,
|
|
121
|
+
`Scope: ${output.verifiedHandoff.scope.status}`,
|
|
122
|
+
`Test Integrity: ${output.verifiedHandoff.testIntegrity.verdict}`,
|
|
85
123
|
"",
|
|
86
124
|
`Objective: ${output.loop.objective}`,
|
|
87
125
|
`Run: ${output.loop.loopId}`,
|
package/dist/tools/run-loop.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { type RunStore, type RunMartinInput, type RunMartinResult } from "../vendor/core/index.js";
|
|
1
|
+
import { type RunStore } from "../vendor/core/index.js";
|
|
3
2
|
import type { LoopBudget, ReceiptScope } from "../vendor/contracts/index.js";
|
|
4
3
|
import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, type MartinEngine } from "./tool-support.js";
|
|
5
4
|
export interface RunLoopInput {
|
|
@@ -44,7 +43,5 @@ export interface RunLoopOutput {
|
|
|
44
43
|
artifacts: ReturnType<typeof buildArtifactSummary>;
|
|
45
44
|
};
|
|
46
45
|
}
|
|
47
|
-
export declare function __setProofModeVerifierSpawnImplForTests(spawnImpl?: SpawnLike): void;
|
|
48
46
|
export declare function __setRunStoreOverrideForTests(store?: RunStore): void;
|
|
49
|
-
export declare function __setRunMartinImplForTests(impl?: (input: RunMartinInput) => Promise<RunMartinResult>): void;
|
|
50
47
|
export declare function runLoopTool(input: RunLoopInput): Promise<RunLoopOutput>;
|
package/dist/tools/run-loop.js
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
import { createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, probeCodexLaunch, resolveCliCommandAvailability
|
|
1
|
+
import { createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, createStubDirectProviderAdapter, probeCodexLaunch, resolveCliCommandAvailability } from "../vendor/adapters/index.js";
|
|
2
2
|
import { createFileRunStore, evaluateCostGovernor, resolveRunsRoot, runMartin } from "../vendor/core/index.js";
|
|
3
3
|
import { normalizeSafePathPatterns, resolveSafeRepoRoot } from "../server-validation.js";
|
|
4
4
|
import { MartinToolError } from "./tool-errors.js";
|
|
5
5
|
import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, buildRunRecordPaths, getEngineAvailability, resolveExecutionMode } from "./tool-support.js";
|
|
6
6
|
import { normalizeLoopBudget } from "./workflow-governance.js";
|
|
7
|
-
let proofModeVerifierSpawnImpl;
|
|
8
7
|
let runStoreOverrideForTests;
|
|
9
|
-
let runMartinImpl = runMartin;
|
|
10
|
-
export function __setProofModeVerifierSpawnImplForTests(spawnImpl) {
|
|
11
|
-
proofModeVerifierSpawnImpl = spawnImpl;
|
|
12
|
-
}
|
|
13
8
|
export function __setRunStoreOverrideForTests(store) {
|
|
14
9
|
runStoreOverrideForTests = store;
|
|
15
10
|
}
|
|
16
|
-
export function __setRunMartinImplForTests(impl) {
|
|
17
|
-
runMartinImpl = impl ?? runMartin;
|
|
18
|
-
}
|
|
19
11
|
export async function runLoopTool(input) {
|
|
20
12
|
const workingDirectory = resolveSafeRepoRoot(input.workingDirectory);
|
|
21
13
|
const engine = input.engine ?? "claude";
|
|
@@ -67,11 +59,10 @@ export async function runLoopTool(input) {
|
|
|
67
59
|
}
|
|
68
60
|
}
|
|
69
61
|
const adapter = !executionMode.liveMode
|
|
70
|
-
?
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
...(proofModeVerifierSpawnImpl ? { spawnImpl: proofModeVerifierSpawnImpl } : {})
|
|
62
|
+
? createStubDirectProviderAdapter({
|
|
63
|
+
label: "Proof mode stub adapter (MARTIN_LIVE=false)",
|
|
64
|
+
providerId: "stub",
|
|
65
|
+
model: "stub"
|
|
75
66
|
})
|
|
76
67
|
: engine === "codex"
|
|
77
68
|
? createCodexCliAdapter({
|
|
@@ -102,7 +93,7 @@ export async function runLoopTool(input) {
|
|
|
102
93
|
partialBudget.maxTokens = input.maxTokens;
|
|
103
94
|
}
|
|
104
95
|
const budget = normalizeLoopBudget(partialBudget);
|
|
105
|
-
const result = await
|
|
96
|
+
const result = await runMartin({
|
|
106
97
|
workspaceId: input.workspaceId ?? "ws_mcp",
|
|
107
98
|
projectId: input.projectId ?? "proj_mcp",
|
|
108
99
|
store: runStoreOverrideForTests ?? createFileRunStore({ runsRoot }),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LoopArtifact, LoopBudget, LoopCost, LoopEvent, LoopTask, ReceiptIntegritySummary, ReceiptScope } from "../vendor/contracts/index.js";
|
|
1
|
+
import type { LoopArtifact, LoopBudget, LoopCost, LoopEvent, LoopTask, ReceiptIntegritySummary, ReceiptScope, TerminationEnvelopeV1 } from "../vendor/contracts/index.js";
|
|
2
2
|
import { type LedgerEvent, type LoopAttemptRecord, type LoopRunRecord } from "../vendor/core/index.js";
|
|
3
3
|
export declare const MARTIN_ENGINE_VALUES: readonly ["claude", "codex", "gemini", "openai"];
|
|
4
4
|
export type MartinEngine = (typeof MARTIN_ENGINE_VALUES)[number];
|
|
@@ -14,6 +14,7 @@ export interface InspectableLoopRecord extends Omit<LoopRunRecord, "attempts" |
|
|
|
14
14
|
metadata?: Record<string, string>;
|
|
15
15
|
receiptIntegrity?: ReceiptIntegritySummary;
|
|
16
16
|
receiptScope?: ReceiptScope;
|
|
17
|
+
terminationEnvelope?: TerminationEnvelopeV1;
|
|
17
18
|
routingEconomics?: import("../vendor/contracts/index.js").RoutingEconomics;
|
|
18
19
|
}
|
|
19
20
|
export interface LoopPreview {
|
|
@@ -105,11 +105,6 @@ function discoverCommonInstallDirectories(command) {
|
|
|
105
105
|
if (localAppData) {
|
|
106
106
|
dirs.push(join(localAppData, "OpenAI", "Codex", "bin"));
|
|
107
107
|
}
|
|
108
|
-
// Claude Code native installer places binary at %USERPROFILE%\.local\bin
|
|
109
|
-
const userProfile = process.env.USERPROFILE ?? process.env.HOMEPATH;
|
|
110
|
-
if (userProfile) {
|
|
111
|
-
dirs.push(join(userProfile, ".local", "bin"));
|
|
112
|
-
}
|
|
113
108
|
// Scoop
|
|
114
109
|
if (home) {
|
|
115
110
|
dirs.push(join(home, "scoop", "shims"));
|
|
@@ -166,13 +161,8 @@ function isOnPathDirectly(command, resolvedPath) {
|
|
|
166
161
|
* Returns a platform-specific one-liner install command for a known CLI.
|
|
167
162
|
*/
|
|
168
163
|
function suggestInstallCommand(command) {
|
|
169
|
-
if (command === "claude") {
|
|
170
|
-
const installCmd = process.platform === "win32"
|
|
171
|
-
? "irm https://claude.ai/install.ps1 | iex"
|
|
172
|
-
: "curl -fsSL https://claude.ai/install.sh | bash";
|
|
173
|
-
return `Install with: ${installCmd}`;
|
|
174
|
-
}
|
|
175
164
|
const npmInstalls = {
|
|
165
|
+
claude: "npm install -g @anthropic-ai/claude-code",
|
|
176
166
|
codex: "npm install -g @openai/codex",
|
|
177
167
|
gemini: "npm install -g @google/gemini-cli"
|
|
178
168
|
};
|
|
@@ -439,7 +429,6 @@ export function buildSuggestedResourceUris(loopId) {
|
|
|
439
429
|
"martin://runs/triage",
|
|
440
430
|
"martin://runs/latest",
|
|
441
431
|
"martin://runs/latest/summary",
|
|
442
|
-
"martin://runs/latest/receipt",
|
|
443
432
|
"martin://runs/latest/proof-card",
|
|
444
433
|
"martin://runs/latest/budget-status",
|
|
445
434
|
"martin://runs/latest/verifier-evidence",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*
|
|
12
12
|
* MCP tools and integration tests use the same factories.
|
|
13
13
|
*/
|
|
14
|
-
import { readGitExecutionArtifacts, resolveGitRepositoryRoot, runSubprocess, runVerification } from "./cli-bridge.js";
|
|
15
|
-
import { buildCodexExecArgs
|
|
14
|
+
import { readGitChangedFiles, readGitExecutionArtifacts, resolveGitRepositoryRoot, runSubprocess, runVerification } from "./cli-bridge.js";
|
|
15
|
+
import { buildCodexExecArgs } from "./codex-launcher.js";
|
|
16
16
|
import { createAdapterCapabilities, normalizeStructuredErrors, normalizeUsage } from "./runtime-support.js";
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
// Cost estimation
|
|
@@ -381,14 +381,10 @@ function createStreamingUsageInspector(capUsd, modelLabel, promptTokenEstimate)
|
|
|
381
381
|
catch {
|
|
382
382
|
return;
|
|
383
383
|
}
|
|
384
|
-
|
|
385
|
-
// assistant-message usage events — skip to avoid double-counting.
|
|
384
|
+
extractUsageFromEvent(event, terminate);
|
|
386
385
|
if (event.type === "result") {
|
|
387
386
|
finalResult = event;
|
|
388
387
|
}
|
|
389
|
-
else {
|
|
390
|
-
extractUsageFromEvent(event, terminate);
|
|
391
|
-
}
|
|
392
388
|
};
|
|
393
389
|
return {
|
|
394
390
|
onChunk: (chunk, terminate) => {
|
|
@@ -514,6 +510,14 @@ export function createAgentCliAdapter(options) {
|
|
|
514
510
|
async execute(request) {
|
|
515
511
|
const prompt = buildPrompt(request);
|
|
516
512
|
const estimatedUsage = estimateUsage(prompt, options.model ?? options.command, options.command);
|
|
513
|
+
const repoRoot = request.context.repoRoot;
|
|
514
|
+
const gitRepoRoot = repoRoot ? resolveGitRepositoryRoot(repoRoot) : undefined;
|
|
515
|
+
// A governed run may begin in a deliberately dirty workspace. Capture that
|
|
516
|
+
// baseline so existing operator work is neither reported as this run's
|
|
517
|
+
// execution nor treated as scope creep.
|
|
518
|
+
const baselineChangedFiles = gitRepoRoot
|
|
519
|
+
? new Set(await readGitChangedFiles(gitRepoRoot, 5_000, options.spawnImpl))
|
|
520
|
+
: new Set();
|
|
517
521
|
// Preflight: bail if projected cost exceeds remaining budget
|
|
518
522
|
if (request.context.remainingBudgetUsd > 0) {
|
|
519
523
|
const projected = estimatePromptCost(prompt, options.model ?? "", options.command);
|
|
@@ -549,7 +553,8 @@ export function createAgentCliAdapter(options) {
|
|
|
549
553
|
timeoutMs,
|
|
550
554
|
spawnImpl: options.spawnImpl,
|
|
551
555
|
...(stdinData === undefined ? {} : { stdinData }),
|
|
552
|
-
...(streamingUsage ? { onStdoutChunk: streamingUsage.onChunk } : {})
|
|
556
|
+
...(streamingUsage ? { onStdoutChunk: streamingUsage.onChunk } : {}),
|
|
557
|
+
...(request.signal !== undefined ? { signal: request.signal } : {})
|
|
553
558
|
});
|
|
554
559
|
if (agentResult.terminationReason) {
|
|
555
560
|
const snapshot = streamingUsage?.snapshot();
|
|
@@ -695,30 +700,36 @@ export function createAgentCliAdapter(options) {
|
|
|
695
700
|
: undefined
|
|
696
701
|
});
|
|
697
702
|
const verificationStack = request.context.verificationStack;
|
|
698
|
-
const verification = await runVerification(request.context.verificationPlan, workingDirectory, verifyTimeoutMs, verificationStack, options.spawnImpl
|
|
703
|
+
const verification = await runVerification(request.context.verificationPlan, workingDirectory, verifyTimeoutMs, verificationStack, options.spawnImpl, {
|
|
704
|
+
runId: request.loopId,
|
|
705
|
+
workspaceId: request.workspaceId,
|
|
706
|
+
cwd: workingDirectory,
|
|
707
|
+
});
|
|
699
708
|
// Check for zero-diff (agent ran but made no file changes)
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
709
|
+
const postRunChangedFiles = gitRepoRoot
|
|
710
|
+
? await readGitChangedFiles(gitRepoRoot, 5_000, options.spawnImpl)
|
|
711
|
+
: [];
|
|
712
|
+
const agentChangedFiles = postRunChangedFiles.filter((file) => !baselineChangedFiles.has(file));
|
|
713
|
+
const noDiff = gitRepoRoot !== undefined && agentChangedFiles.length === 0;
|
|
706
714
|
// Extract structured errors from stderr/stdout for better failure context
|
|
707
715
|
const structuredErrors = normalizeStructuredErrors(extractStructuredErrors(agentResult.stderr, agentResult.stdout));
|
|
708
|
-
const
|
|
716
|
+
const rawExecutionArtifacts = gitRepoRoot
|
|
709
717
|
? await readGitExecutionArtifacts(gitRepoRoot, 5000, options.spawnImpl)
|
|
710
718
|
: undefined;
|
|
719
|
+
const executionArtifacts = rawExecutionArtifacts
|
|
720
|
+
? {
|
|
721
|
+
...(agentChangedFiles.length > 0 ? { changedFiles: agentChangedFiles } : {}),
|
|
722
|
+
...(baselineChangedFiles.size === 0 && rawExecutionArtifacts.diffStats
|
|
723
|
+
? { diffStats: rawExecutionArtifacts.diffStats }
|
|
724
|
+
: {})
|
|
725
|
+
}
|
|
726
|
+
: undefined;
|
|
711
727
|
// Scope contract enforcement: check touched files against allowedPaths/deniedPaths
|
|
712
728
|
let scopeViolations = [];
|
|
713
729
|
const scopeCtx = request.context;
|
|
714
730
|
if (gitRepoRoot && (scopeCtx.allowedPaths?.length || scopeCtx.deniedPaths?.length)) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
timeoutMs: 5000,
|
|
718
|
-
spawnImpl: options.spawnImpl
|
|
719
|
-
});
|
|
720
|
-
if (diffResult.exitCode === 0 && diffResult.stdout.trim()) {
|
|
721
|
-
const touchedFiles = diffResult.stdout.trim().split("\n").filter(Boolean);
|
|
731
|
+
if (agentChangedFiles.length > 0) {
|
|
732
|
+
const touchedFiles = agentChangedFiles;
|
|
722
733
|
const allowed = scopeCtx.allowedPaths ?? [];
|
|
723
734
|
const denied = scopeCtx.deniedPaths ?? [];
|
|
724
735
|
for (const file of touchedFiles) {
|
|
@@ -741,7 +752,7 @@ export function createAgentCliAdapter(options) {
|
|
|
741
752
|
status: "completed",
|
|
742
753
|
summary,
|
|
743
754
|
usage,
|
|
744
|
-
verification
|
|
755
|
+
verification,
|
|
745
756
|
...(executionArtifacts
|
|
746
757
|
? {
|
|
747
758
|
execution: {
|
|
@@ -786,7 +797,7 @@ export function createAgentCliAdapter(options) {
|
|
|
786
797
|
}
|
|
787
798
|
// Reset tracked files to HEAD so next attempt starts from clean state
|
|
788
799
|
try {
|
|
789
|
-
if (gitRepoRoot) {
|
|
800
|
+
if (gitRepoRoot && baselineChangedFiles.size === 0) {
|
|
790
801
|
await runSubprocess("git", ["restore", "--staged", "--worktree", "."], {
|
|
791
802
|
cwd: gitRepoRoot,
|
|
792
803
|
timeoutMs: 5000
|
|
@@ -803,7 +814,7 @@ export function createAgentCliAdapter(options) {
|
|
|
803
814
|
? `${summary}${errorBlock}${scopeViolations.length > 0 ? `\nScope violations: ${scopeViolations.join(", ")}` : ""}`
|
|
804
815
|
: summary,
|
|
805
816
|
usage,
|
|
806
|
-
verification
|
|
817
|
+
verification,
|
|
807
818
|
...(executionArtifacts
|
|
808
819
|
? {
|
|
809
820
|
execution: {
|
|
@@ -906,7 +917,7 @@ export function createCodexCliAdapter(options = {}) {
|
|
|
906
917
|
const sandbox = options.sandbox ?? "workspace-write";
|
|
907
918
|
const workingDirectory = options.workingDirectory ?? process.cwd();
|
|
908
919
|
const command = options.command ?? "codex";
|
|
909
|
-
const launchModel = options.model
|
|
920
|
+
const launchModel = options.model;
|
|
910
921
|
return createAgentCliAdapter({
|
|
911
922
|
command,
|
|
912
923
|
adapterIdSuffix: "codex",
|
|
@@ -1152,11 +1163,3 @@ function extractStructuredErrors(stderr, stdout) {
|
|
|
1152
1163
|
}
|
|
1153
1164
|
return errors.slice(0, 10); // cap at 10 to avoid bloating prompts
|
|
1154
1165
|
}
|
|
1155
|
-
async function checkNoDiff(repoRoot, spawnImpl) {
|
|
1156
|
-
const result = await runSubprocess("git", ["diff", "--name-only", "HEAD"], {
|
|
1157
|
-
cwd: repoRoot,
|
|
1158
|
-
timeoutMs: 5000,
|
|
1159
|
-
spawnImpl
|
|
1160
|
-
});
|
|
1161
|
-
return result.exitCode === 0 && result.stdout.trim().length === 0;
|
|
1162
|
-
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { type ChildProcess, type SpawnOptions } from "node:child_process";
|
|
2
2
|
import { diffStatsFromNumstat } from "./runtime-support.js";
|
|
3
|
+
import type { VerifierExecutionBinding } from "../core/index.js";
|
|
3
4
|
export type SpawnLike = (command: string, args?: readonly string[], options?: SpawnOptions) => ChildProcess;
|
|
4
5
|
export interface SubprocessResult {
|
|
5
6
|
exitCode: number;
|
|
6
7
|
stdout: string;
|
|
7
8
|
stderr: string;
|
|
8
9
|
timedOut: boolean;
|
|
10
|
+
completed: boolean;
|
|
11
|
+
crashed: boolean;
|
|
9
12
|
/**
|
|
10
13
|
* True when the subprocess was terminated early because its combined
|
|
11
14
|
* stdout+stderr exceeded `maxOutputBytes` — a circuit breaker against
|
|
@@ -30,10 +33,13 @@ export interface VerificationOutcome {
|
|
|
30
33
|
summary: string;
|
|
31
34
|
steps: VerificationStepOutcome[];
|
|
32
35
|
warnings?: string[];
|
|
36
|
+
binding: VerifierExecutionBinding;
|
|
33
37
|
}
|
|
34
38
|
export interface VerificationStepOutcome {
|
|
35
39
|
command: string;
|
|
36
40
|
launched: boolean;
|
|
41
|
+
completed: boolean;
|
|
42
|
+
crashed: boolean;
|
|
37
43
|
exitCode?: number;
|
|
38
44
|
timedOut: boolean;
|
|
39
45
|
fastFail: boolean;
|
|
@@ -60,12 +66,14 @@ export declare function runSubprocess(command: string, args: string[], options:
|
|
|
60
66
|
* a runaway final usage figure.
|
|
61
67
|
*/
|
|
62
68
|
onStdoutChunk?: (chunk: Buffer, terminate: (reason: string) => void) => void;
|
|
69
|
+
/** Optional abort signal — kills the subprocess when aborted. */
|
|
70
|
+
signal?: AbortSignal;
|
|
63
71
|
}): Promise<SubprocessResult>;
|
|
64
72
|
export declare function runVerification(commands: string[], cwd: string, timeoutMs: number, verificationStack?: Array<{
|
|
65
73
|
command: string;
|
|
66
74
|
type: string;
|
|
67
75
|
fastFail?: boolean;
|
|
68
|
-
}>, spawnImpl?: SpawnLike): Promise<VerificationOutcome>;
|
|
76
|
+
}>, spawnImpl?: SpawnLike, binding?: Omit<VerifierExecutionBinding, "commands">): Promise<VerificationOutcome>;
|
|
69
77
|
export declare function readGitExecutionArtifacts(repoRoot: string, timeoutMs: number, spawnImpl?: SpawnLike): Promise<{
|
|
70
78
|
changedFiles?: string[];
|
|
71
79
|
diffStats?: ReturnType<typeof diffStatsFromNumstat>;
|