@martinloop/mcp 0.3.6 → 0.3.7
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 +137 -145
- package/dist/discovery-metadata.d.ts +1 -1
- package/dist/discovery-metadata.js +1 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resources.d.ts +2 -1
- package/dist/resources.js +251 -197
- package/dist/server-validation.d.ts +1 -1
- package/dist/server-validation.js +11 -16
- package/dist/server.d.ts +0 -23
- package/dist/server.js +7 -152
- package/dist/tools/run-loop.d.ts +2 -1
- package/dist/tools/run-loop.js +5 -1
- package/dist/tools/tool-support.js +1 -0
- package/dist/vendor/adapters/adapters/src/claude-cli.d.ts +146 -0
- package/dist/vendor/adapters/adapters/src/claude-cli.js +1100 -0
- package/dist/vendor/adapters/adapters/src/cli-bridge.d.ts +103 -0
- package/dist/vendor/adapters/adapters/src/cli-bridge.js +569 -0
- package/dist/vendor/adapters/adapters/src/codex-launcher.d.ts +78 -0
- package/dist/vendor/adapters/adapters/src/codex-launcher.js +663 -0
- package/dist/vendor/adapters/adapters/src/direct-provider.d.ts +10 -0
- package/dist/vendor/adapters/adapters/src/direct-provider.js +40 -0
- package/dist/vendor/adapters/adapters/src/index.d.ts +8 -0
- package/dist/vendor/adapters/adapters/src/index.js +8 -0
- package/dist/vendor/adapters/adapters/src/openai-compatible.d.ts +62 -0
- package/dist/vendor/adapters/adapters/src/openai-compatible.js +306 -0
- package/dist/vendor/adapters/adapters/src/runtime-support.d.ts +17 -0
- package/dist/vendor/adapters/adapters/src/runtime-support.js +59 -0
- package/dist/vendor/adapters/adapters/src/stub-agent-cli.d.ts +8 -0
- package/dist/vendor/adapters/adapters/src/stub-agent-cli.js +40 -0
- package/dist/vendor/adapters/adapters/src/stub-direct-provider.d.ts +8 -0
- package/dist/vendor/adapters/adapters/src/stub-direct-provider.js +12 -0
- package/dist/vendor/adapters/adapters/src/verifier-only.d.ts +9 -0
- package/dist/vendor/adapters/adapters/src/verifier-only.js +63 -0
- package/dist/vendor/adapters/claude-cli.d.ts +2 -2
- package/dist/vendor/adapters/claude-cli.js +28 -42
- package/dist/vendor/adapters/contracts/src/context-graph.d.ts +48 -0
- package/dist/vendor/adapters/contracts/src/context-graph.js +26 -0
- package/dist/vendor/adapters/contracts/src/execution-policy.d.ts +78 -0
- package/dist/vendor/adapters/contracts/src/execution-policy.js +43 -0
- package/dist/vendor/adapters/contracts/src/governance.d.ts +24 -0
- package/dist/vendor/adapters/contracts/src/governance.js +11 -0
- package/dist/vendor/adapters/contracts/src/identity.d.ts +29 -0
- package/dist/vendor/adapters/contracts/src/identity.js +30 -0
- package/dist/vendor/adapters/contracts/src/index.d.ts +412 -0
- package/dist/vendor/adapters/contracts/src/index.js +225 -0
- package/dist/vendor/adapters/contracts/src/operator.d.ts +19 -0
- package/dist/vendor/adapters/contracts/src/operator.js +11 -0
- package/dist/vendor/adapters/contracts/src/trajectory.d.ts +26 -0
- package/dist/vendor/adapters/contracts/src/trajectory.js +27 -0
- package/dist/vendor/adapters/core/src/compiler.d.ts +52 -0
- package/dist/vendor/adapters/core/src/compiler.js +52 -0
- package/dist/vendor/adapters/core/src/context-graph.d.ts +3 -0
- package/dist/vendor/adapters/core/src/context-graph.js +371 -0
- package/dist/vendor/adapters/core/src/context-integrity.d.ts +26 -0
- package/dist/vendor/adapters/core/src/context-integrity.js +80 -0
- package/dist/vendor/adapters/core/src/grounding.d.ts +38 -0
- package/dist/vendor/adapters/core/src/grounding.js +273 -0
- package/dist/vendor/adapters/core/src/identity.d.ts +20 -0
- package/dist/vendor/adapters/core/src/identity.js +86 -0
- package/dist/vendor/adapters/core/src/index.d.ts +186 -0
- package/dist/vendor/adapters/core/src/index.js +1495 -0
- package/dist/vendor/adapters/core/src/leash.d.ts +48 -0
- package/dist/vendor/adapters/core/src/leash.js +497 -0
- package/dist/vendor/adapters/core/src/persistence/compiler.d.ts +18 -0
- package/dist/vendor/adapters/core/src/persistence/compiler.js +34 -0
- package/dist/vendor/adapters/core/src/persistence/index.d.ts +10 -0
- package/dist/vendor/adapters/core/src/persistence/index.js +5 -0
- package/dist/vendor/adapters/core/src/persistence/integrity.d.ts +39 -0
- package/dist/vendor/adapters/core/src/persistence/integrity.js +252 -0
- package/dist/vendor/adapters/core/src/persistence/ledger.d.ts +23 -0
- package/dist/vendor/adapters/core/src/persistence/ledger.js +9 -0
- package/dist/vendor/adapters/core/src/persistence/memory-store.d.ts +63 -0
- package/dist/vendor/adapters/core/src/persistence/memory-store.js +115 -0
- package/dist/vendor/adapters/core/src/persistence/runs-reader.d.ts +66 -0
- package/dist/vendor/adapters/core/src/persistence/runs-reader.js +140 -0
- package/dist/vendor/adapters/core/src/persistence/store.d.ts +89 -0
- package/dist/vendor/adapters/core/src/persistence/store.js +125 -0
- package/dist/vendor/adapters/core/src/persistence/trace-store.d.ts +85 -0
- package/dist/vendor/adapters/core/src/persistence/trace-store.js +125 -0
- package/dist/vendor/adapters/core/src/policy-compiler.d.ts +2 -0
- package/dist/vendor/adapters/core/src/policy-compiler.js +195 -0
- package/dist/vendor/adapters/core/src/policy.d.ts +156 -0
- package/dist/vendor/adapters/core/src/policy.js +704 -0
- package/dist/vendor/adapters/core/src/repo-analyzer.d.ts +53 -0
- package/dist/vendor/adapters/core/src/repo-analyzer.js +263 -0
- package/dist/vendor/adapters/core/src/rollback.d.ts +15 -0
- package/dist/vendor/adapters/core/src/rollback.js +253 -0
- package/dist/vendor/adapters/core/src/routing.d.ts +82 -0
- package/dist/vendor/adapters/core/src/routing.js +336 -0
- package/dist/vendor/adapters/core/src/test-integrity.d.ts +64 -0
- package/dist/vendor/adapters/core/src/test-integrity.js +197 -0
- package/dist/vendor/adapters/core/src/trajectory.d.ts +18 -0
- package/dist/vendor/adapters/core/src/trajectory.js +295 -0
- package/dist/vendor/adapters/openai-compatible.js +6 -6
- package/dist/vendor/adapters/verifier-only.js +2 -2
- package/dist/vendor/contracts/index.d.ts +1 -1
- package/dist/vendor/core/compiler.js +4 -9
- package/dist/vendor/core/index.js +1 -88
- package/dist/vendor/core/rollback.js +2 -3
- package/package.json +76 -76
- package/server.json +21 -21
|
@@ -14,12 +14,12 @@ export function validateToolInput(name, args) {
|
|
|
14
14
|
return validateStatusInput(args);
|
|
15
15
|
case "martin_doctor":
|
|
16
16
|
return validateDoctorInput(args);
|
|
17
|
+
case "martin_estimate":
|
|
18
|
+
return validateEstimateInput(args);
|
|
17
19
|
case "martin_plan":
|
|
18
20
|
return validatePlanInput(args);
|
|
19
21
|
case "martin_preflight":
|
|
20
22
|
return validatePreflightInput(args);
|
|
21
|
-
case "martin_estimate":
|
|
22
|
-
return validateEstimateInput(args);
|
|
23
23
|
case "martin_logs":
|
|
24
24
|
return validateLogsInput(args);
|
|
25
25
|
case "martin_cancel":
|
|
@@ -254,27 +254,22 @@ function validateDoctorInput(args) {
|
|
|
254
254
|
...optionalEnumAsObject(record.engine, "engine", MARTIN_ENGINE_VALUES)
|
|
255
255
|
};
|
|
256
256
|
}
|
|
257
|
-
function validatePreflightInput(args) {
|
|
258
|
-
return validateRunInput(args);
|
|
259
|
-
}
|
|
260
|
-
function validatePlanInput(args) {
|
|
261
|
-
return validateRunInput(args);
|
|
262
|
-
}
|
|
263
257
|
function validateEstimateInput(args) {
|
|
264
258
|
const record = requireObject(args);
|
|
265
|
-
assertAllowedKeys(record, ["objective", "engine", "budgetUsd", "fileScope"
|
|
266
|
-
const engine = optionalEnum(record.engine, "engine", MARTIN_ENGINE_VALUES);
|
|
267
|
-
const fileScope = normalizeSafePathPatterns(record.fileScope, "fileScope");
|
|
259
|
+
assertAllowedKeys(record, ["objective", "engine", "budgetUsd", "fileScope"]);
|
|
268
260
|
return {
|
|
269
261
|
objective: requireString(record.objective, "objective"),
|
|
270
|
-
...(engine
|
|
262
|
+
...optionalEnumAsObject(record.engine, "engine", MARTIN_ENGINE_VALUES),
|
|
271
263
|
...optionalPositiveNumber(record.budgetUsd, "budgetUsd"),
|
|
272
|
-
...(fileScope
|
|
273
|
-
...(record.workingDirectory !== undefined
|
|
274
|
-
? { workingDirectory: resolveSafeRepoRoot(requireString(record.workingDirectory, "workingDirectory")) }
|
|
275
|
-
: {})
|
|
264
|
+
...optionalStringArrayAsObject(record.fileScope, "fileScope")
|
|
276
265
|
};
|
|
277
266
|
}
|
|
267
|
+
function validatePreflightInput(args) {
|
|
268
|
+
return validateRunInput(args);
|
|
269
|
+
}
|
|
270
|
+
function validatePlanInput(args) {
|
|
271
|
+
return validateRunInput(args);
|
|
272
|
+
}
|
|
278
273
|
function validateLogsInput(args) {
|
|
279
274
|
const record = requireObject(args);
|
|
280
275
|
assertAllowedKeys(record, ["file", "loopId", "runsDir", "latest", "limit"]);
|
package/dist/server.d.ts
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
* Manual start:
|
|
17
17
|
* node dist/server.js
|
|
18
18
|
*/
|
|
19
|
-
import { type Server as NodeHttpServer } from "node:http";
|
|
20
19
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
21
20
|
export declare function createMartinMcpServer(serverInfo?: {
|
|
22
21
|
name?: string;
|
|
@@ -89,26 +88,4 @@ export declare function connectMartinMcpStdioServer(): Promise<Server<{
|
|
|
89
88
|
} | undefined;
|
|
90
89
|
} | undefined;
|
|
91
90
|
}>>;
|
|
92
|
-
export interface MartinMcpHttpServerOptions {
|
|
93
|
-
host?: string;
|
|
94
|
-
port?: number;
|
|
95
|
-
path?: string;
|
|
96
|
-
}
|
|
97
|
-
export interface MartinMcpHttpServerHandle {
|
|
98
|
-
server: NodeHttpServer;
|
|
99
|
-
host: string;
|
|
100
|
-
port: number;
|
|
101
|
-
path: string;
|
|
102
|
-
endpoint: string;
|
|
103
|
-
close: () => Promise<void>;
|
|
104
|
-
}
|
|
105
|
-
export declare function parseMartinMcpServerArgs(argv: string[]): {
|
|
106
|
-
transport: "stdio";
|
|
107
|
-
} | {
|
|
108
|
-
transport: "http";
|
|
109
|
-
host: string;
|
|
110
|
-
port: number;
|
|
111
|
-
path: string;
|
|
112
|
-
};
|
|
113
|
-
export declare function connectMartinMcpHttpServer(options?: MartinMcpHttpServerOptions): Promise<MartinMcpHttpServerHandle>;
|
|
114
91
|
export declare function isDirectExecutionEntry(entryPath: string | undefined, moduleUrl?: string): boolean;
|
package/dist/server.js
CHANGED
|
@@ -16,14 +16,12 @@
|
|
|
16
16
|
* Manual start:
|
|
17
17
|
* node dist/server.js
|
|
18
18
|
*/
|
|
19
|
-
import { createServer as createHttpServer } from "node:http";
|
|
20
19
|
import { fileURLToPath } from "node:url";
|
|
21
20
|
import { realpathSync } from "node:fs";
|
|
22
21
|
import path from "node:path";
|
|
23
22
|
import { resolveRunsRoot } from "./vendor/core/index.js";
|
|
24
23
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
25
24
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
26
|
-
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
27
25
|
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
28
26
|
import { MARTIN_MCP_PACKAGE_VERSION } from "./package-version.js";
|
|
29
27
|
import { getMartinPrompt, listMartinPrompts } from "./prompts.js";
|
|
@@ -1403,10 +1401,6 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1403
1401
|
type: "array",
|
|
1404
1402
|
items: { type: "string" },
|
|
1405
1403
|
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."
|
|
1410
1404
|
}
|
|
1411
1405
|
},
|
|
1412
1406
|
required: ["objective"]
|
|
@@ -1524,7 +1518,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1524
1518
|
workingDirectory: output.environment.workingDirectory,
|
|
1525
1519
|
engine: input.engine,
|
|
1526
1520
|
receiptScope: output.receiptScope
|
|
1527
|
-
})
|
|
1521
|
+
});
|
|
1528
1522
|
return createToolSuccessResult(output, output.summary);
|
|
1529
1523
|
}
|
|
1530
1524
|
if (name === "martin_plan") {
|
|
@@ -1541,7 +1535,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1541
1535
|
repoRoot: output.workingDirectory,
|
|
1542
1536
|
runsRoot: resolveRunsRoot(process.env)
|
|
1543
1537
|
}
|
|
1544
|
-
})
|
|
1538
|
+
});
|
|
1545
1539
|
return createToolSuccessResult(output, `Plan ready for ${output.objective} with ${output.risk.level} risk and ${output.approvalRecommendation.replace(/_/gu, " ")} approval.`);
|
|
1546
1540
|
}
|
|
1547
1541
|
if (name === "martin_preflight") {
|
|
@@ -1559,7 +1553,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1559
1553
|
allowedPaths: output.normalized.allowedPaths,
|
|
1560
1554
|
deniedPaths: output.normalized.deniedPaths,
|
|
1561
1555
|
budget: output.normalized.budget
|
|
1562
|
-
})
|
|
1556
|
+
});
|
|
1563
1557
|
}
|
|
1564
1558
|
return createToolSuccessResult(output, output.summary);
|
|
1565
1559
|
}
|
|
@@ -1646,8 +1640,8 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1646
1640
|
const engine = input.engine ?? "claude";
|
|
1647
1641
|
const budgetUsd = input.budgetUsd ?? 5;
|
|
1648
1642
|
const fileScope = input.fileScope ?? [];
|
|
1643
|
+
const workingDirectory = resolveSafeRepoRoot();
|
|
1649
1644
|
const runsRoot = resolveRunsRoot(process.env);
|
|
1650
|
-
const workingDirectory = input.workingDirectory ?? resolveSafeRepoRoot();
|
|
1651
1645
|
const route = classifyRoute({
|
|
1652
1646
|
objective,
|
|
1653
1647
|
verificationPlan: [],
|
|
@@ -1679,14 +1673,8 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1679
1673
|
step: "estimate",
|
|
1680
1674
|
workingDirectory,
|
|
1681
1675
|
objective,
|
|
1682
|
-
engine
|
|
1683
|
-
|
|
1684
|
-
invocationRoot: resolveSafeRepoRoot(),
|
|
1685
|
-
workingDirectory,
|
|
1686
|
-
repoRoot: workingDirectory,
|
|
1687
|
-
runsRoot
|
|
1688
|
-
}
|
|
1689
|
-
}).catch(() => { });
|
|
1676
|
+
engine
|
|
1677
|
+
});
|
|
1690
1678
|
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)}.`);
|
|
1691
1679
|
}
|
|
1692
1680
|
return createToolErrorResult(toToolFailure(new Error(`Unknown tool: ${name}`)));
|
|
@@ -1710,114 +1698,6 @@ export async function connectMartinMcpStdioServer() {
|
|
|
1710
1698
|
await server.connect(transport);
|
|
1711
1699
|
return server;
|
|
1712
1700
|
}
|
|
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
|
-
}
|
|
1821
1701
|
export function isDirectExecutionEntry(entryPath, moduleUrl = import.meta.url) {
|
|
1822
1702
|
if (typeof entryPath !== "string" || entryPath.length === 0) {
|
|
1823
1703
|
return false;
|
|
@@ -1838,30 +1718,5 @@ function realPathOrResolved(filePath) {
|
|
|
1838
1718
|
}
|
|
1839
1719
|
}
|
|
1840
1720
|
if (isDirectExecution()) {
|
|
1841
|
-
|
|
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
|
-
}
|
|
1721
|
+
await connectMartinMcpStdioServer();
|
|
1867
1722
|
}
|
package/dist/tools/run-loop.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SpawnLike } from "../vendor/adapters/index.js";
|
|
2
|
-
import { type RunStore } from "../vendor/core/index.js";
|
|
2
|
+
import { type RunStore, type RunMartinInput, type RunMartinResult } from "../vendor/core/index.js";
|
|
3
3
|
import type { LoopBudget, ReceiptScope } from "../vendor/contracts/index.js";
|
|
4
4
|
import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, type MartinEngine } from "./tool-support.js";
|
|
5
5
|
export interface RunLoopInput {
|
|
@@ -46,4 +46,5 @@ export interface RunLoopOutput {
|
|
|
46
46
|
}
|
|
47
47
|
export declare function __setProofModeVerifierSpawnImplForTests(spawnImpl?: SpawnLike): void;
|
|
48
48
|
export declare function __setRunStoreOverrideForTests(store?: RunStore): void;
|
|
49
|
+
export declare function __setRunMartinImplForTests(impl?: (input: RunMartinInput) => Promise<RunMartinResult>): void;
|
|
49
50
|
export declare function runLoopTool(input: RunLoopInput): Promise<RunLoopOutput>;
|
package/dist/tools/run-loop.js
CHANGED
|
@@ -6,12 +6,16 @@ import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, build
|
|
|
6
6
|
import { normalizeLoopBudget } from "./workflow-governance.js";
|
|
7
7
|
let proofModeVerifierSpawnImpl;
|
|
8
8
|
let runStoreOverrideForTests;
|
|
9
|
+
let runMartinImpl = runMartin;
|
|
9
10
|
export function __setProofModeVerifierSpawnImplForTests(spawnImpl) {
|
|
10
11
|
proofModeVerifierSpawnImpl = spawnImpl;
|
|
11
12
|
}
|
|
12
13
|
export function __setRunStoreOverrideForTests(store) {
|
|
13
14
|
runStoreOverrideForTests = store;
|
|
14
15
|
}
|
|
16
|
+
export function __setRunMartinImplForTests(impl) {
|
|
17
|
+
runMartinImpl = impl ?? runMartin;
|
|
18
|
+
}
|
|
15
19
|
export async function runLoopTool(input) {
|
|
16
20
|
const workingDirectory = resolveSafeRepoRoot(input.workingDirectory);
|
|
17
21
|
const engine = input.engine ?? "claude";
|
|
@@ -98,7 +102,7 @@ export async function runLoopTool(input) {
|
|
|
98
102
|
partialBudget.maxTokens = input.maxTokens;
|
|
99
103
|
}
|
|
100
104
|
const budget = normalizeLoopBudget(partialBudget);
|
|
101
|
-
const result = await
|
|
105
|
+
const result = await runMartinImpl({
|
|
102
106
|
workspaceId: input.workspaceId ?? "ws_mcp",
|
|
103
107
|
projectId: input.projectId ?? "proj_mcp",
|
|
104
108
|
store: runStoreOverrideForTests ?? createFileRunStore({ runsRoot }),
|
|
@@ -429,6 +429,7 @@ export function buildSuggestedResourceUris(loopId) {
|
|
|
429
429
|
"martin://runs/triage",
|
|
430
430
|
"martin://runs/latest",
|
|
431
431
|
"martin://runs/latest/summary",
|
|
432
|
+
"martin://runs/latest/receipt",
|
|
432
433
|
"martin://runs/latest/proof-card",
|
|
433
434
|
"martin://runs/latest/budget-status",
|
|
434
435
|
"martin://runs/latest/verifier-evidence",
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real agent-CLI adapters.
|
|
3
|
+
*
|
|
4
|
+
* Exports a generic factory (`createAgentCliAdapter`) and two pre-configured
|
|
5
|
+
* factories (`createClaudeCliAdapter`, `createCodexCliAdapter`) that spawn
|
|
6
|
+
* the respective AI coding CLI as a child subprocess.
|
|
7
|
+
*
|
|
8
|
+
* Usage in CLI:
|
|
9
|
+
* createClaudeCliAdapter({ workingDirectory: process.cwd() })
|
|
10
|
+
* createCodexCliAdapter({ workingDirectory: process.cwd() })
|
|
11
|
+
*
|
|
12
|
+
* MCP tools and integration tests use the same factories.
|
|
13
|
+
*/
|
|
14
|
+
import type { MartinAdapter, MartinAdapterRequest } from "../../../core/index.js";
|
|
15
|
+
import { type SpawnLike } from "./cli-bridge.js";
|
|
16
|
+
/**
|
|
17
|
+
* Given a prompt string, returns the full argv array to pass to spawn().
|
|
18
|
+
* Example for Claude: () => ["--output-format", "json", "--print"]
|
|
19
|
+
* Example for Codex: () => ["exec", "--sandbox", "workspace-write", "-"]
|
|
20
|
+
*/
|
|
21
|
+
export type CliArgsBuilder = (prompt: string, request: MartinAdapterRequest) => string[];
|
|
22
|
+
export type CliStdinBuilder = (prompt: string) => string | undefined;
|
|
23
|
+
export interface AgentCliAdapterOptions {
|
|
24
|
+
/** The executable to spawn (e.g. "claude", "codex"). */
|
|
25
|
+
command: string;
|
|
26
|
+
/** Converts a prompt string into the argv array passed to spawn(). */
|
|
27
|
+
argsBuilder: CliArgsBuilder;
|
|
28
|
+
/** Optional stdin payload for CLIs that accept prompt input via stdin or `-`. */
|
|
29
|
+
stdinBuilder?: CliStdinBuilder;
|
|
30
|
+
/** Adapter ID suffix. Defaults to command. */
|
|
31
|
+
adapterIdSuffix?: string;
|
|
32
|
+
/** Working directory for all subprocesses. Defaults to process.cwd(). */
|
|
33
|
+
workingDirectory?: string;
|
|
34
|
+
/** Timeout for the agent subprocess in ms. Defaults to 300_000 (5 min). */
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
/** Timeout per verification command in ms. Defaults to 120_000 (2 min). */
|
|
37
|
+
verifyTimeoutMs?: number;
|
|
38
|
+
/** Human-readable label shown in loop records. */
|
|
39
|
+
label?: string;
|
|
40
|
+
/** Model name surfaced in adapter metadata (also used for cost estimation). */
|
|
41
|
+
model?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Whether the CLI outputs JSON when --output-format json is passed.
|
|
44
|
+
* Set to false for CLIs that don't support this flag (e.g. Codex).
|
|
45
|
+
* Defaults to true for Claude.
|
|
46
|
+
*/
|
|
47
|
+
supportsJsonOutput?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Set when `argsBuilder` requests `--output-format stream-json` (newline-
|
|
50
|
+
* delimited JSON events) rather than single-blob `json`. Enables (a)
|
|
51
|
+
* incremental result parsing that scans for the final `result` event, and
|
|
52
|
+
* (b) a live cumulative-cost circuit breaker that terminates the subprocess
|
|
53
|
+
* the moment projected spend crosses the remaining per-attempt budget,
|
|
54
|
+
* rather than only learning about an overspend after the process exits.
|
|
55
|
+
*/
|
|
56
|
+
streamingUsageCap?: boolean;
|
|
57
|
+
/** Test-only override for subprocess spawning. */
|
|
58
|
+
spawnImpl?: SpawnLike;
|
|
59
|
+
}
|
|
60
|
+
export interface ClaudeCliAdapterOptions {
|
|
61
|
+
workingDirectory?: string;
|
|
62
|
+
timeoutMs?: number;
|
|
63
|
+
verifyTimeoutMs?: number;
|
|
64
|
+
label?: string;
|
|
65
|
+
/** Override the model passed via --model flag. */
|
|
66
|
+
model?: string;
|
|
67
|
+
/** Extra args appended after core args (before prompt). */
|
|
68
|
+
extraArgs?: string[];
|
|
69
|
+
spawnImpl?: SpawnLike;
|
|
70
|
+
}
|
|
71
|
+
export interface CodexCliAdapterOptions {
|
|
72
|
+
/** Override the executable or absolute command path used to launch Codex. */
|
|
73
|
+
command?: string;
|
|
74
|
+
workingDirectory?: string;
|
|
75
|
+
timeoutMs?: number;
|
|
76
|
+
verifyTimeoutMs?: number;
|
|
77
|
+
label?: string;
|
|
78
|
+
/** Override the model passed via --model flag. */
|
|
79
|
+
model?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Deprecated no-op retained for compatibility.
|
|
82
|
+
*
|
|
83
|
+
* Codex CLI's supported non-interactive entrypoint is `codex exec`.
|
|
84
|
+
* MartinLoop now uses explicit sandboxing instead of the legacy
|
|
85
|
+
* `--full-auto` compatibility path, which can exit before verifier execution.
|
|
86
|
+
*/
|
|
87
|
+
fullAuto?: boolean;
|
|
88
|
+
/** Codex sandbox mode for model-generated commands. Defaults to workspace-write. */
|
|
89
|
+
sandbox?: "read-only" | "workspace-write" | "danger-full-access";
|
|
90
|
+
/** Extra args appended after core args (before prompt). */
|
|
91
|
+
extraArgs?: string[];
|
|
92
|
+
spawnImpl?: SpawnLike;
|
|
93
|
+
}
|
|
94
|
+
export interface GeminiCliAdapterOptions {
|
|
95
|
+
workingDirectory?: string;
|
|
96
|
+
timeoutMs?: number;
|
|
97
|
+
verifyTimeoutMs?: number;
|
|
98
|
+
label?: string;
|
|
99
|
+
/** Override the model passed via --model flag. Defaults to the Gemini `flash` alias. */
|
|
100
|
+
model?: string;
|
|
101
|
+
/** Approval mode for headless Gemini runs. Defaults to yolo for autonomous execution. */
|
|
102
|
+
approvalMode?: "default" | "auto_edit" | "yolo" | "plan";
|
|
103
|
+
/** Enable Gemini sandbox mode when the host is configured for it. Disabled by default. */
|
|
104
|
+
sandbox?: boolean;
|
|
105
|
+
/** Extra args appended after core args. */
|
|
106
|
+
extraArgs?: string[];
|
|
107
|
+
spawnImpl?: SpawnLike;
|
|
108
|
+
}
|
|
109
|
+
export declare function createAgentCliAdapter(options: AgentCliAdapterOptions): MartinAdapter;
|
|
110
|
+
/**
|
|
111
|
+
* Spawns `claude --output-format stream-json --verbose --print "<prompt>" [extraArgs]`.
|
|
112
|
+
*
|
|
113
|
+
* `stream-json` emits one JSON event per line — including per-turn usage on
|
|
114
|
+
* each `assistant` message and a final `result` event carrying the same
|
|
115
|
+
* `result`/`usage`/`total_cost_usd` fields as single-blob `json` output — so
|
|
116
|
+
* MartinLoop can both (a) recover real token usage/cost as before, and
|
|
117
|
+
* (b) watch cumulative spend live and self-terminate the subprocess the
|
|
118
|
+
* moment it crosses the remaining per-attempt budget (see
|
|
119
|
+
* `streamingUsageCap` / `createStreamingUsageInspector`), instead of only
|
|
120
|
+
* discovering an overspend after the whole process has already exited.
|
|
121
|
+
*
|
|
122
|
+
* Requires the Claude Code CLI to be installed and authenticated:
|
|
123
|
+
* https://docs.anthropic.com/claude-code
|
|
124
|
+
*/
|
|
125
|
+
export declare function createClaudeCliAdapter(options?: ClaudeCliAdapterOptions): MartinAdapter;
|
|
126
|
+
/**
|
|
127
|
+
* Spawns `codex exec --cd <workspace> --sandbox <mode> [--model <model>] [extraArgs] -`.
|
|
128
|
+
*
|
|
129
|
+
* The prompt is delivered via stdin so Windows shell quoting cannot truncate or
|
|
130
|
+
* reinterpret long MartinLoop prompts that contain paths, deny rules, or budget
|
|
131
|
+
* context.
|
|
132
|
+
*
|
|
133
|
+
* Requires the Codex CLI to be installed and authenticated:
|
|
134
|
+
* npm install -g @openai/codex
|
|
135
|
+
*/
|
|
136
|
+
export declare function createCodexCliAdapter(options?: CodexCliAdapterOptions): MartinAdapter;
|
|
137
|
+
/**
|
|
138
|
+
* Spawns `gemini --model <model> --prompt "" --approval-mode <mode> --output-format json [...]`.
|
|
139
|
+
*
|
|
140
|
+
* The prompt is delivered via stdin while forcing headless mode with `--prompt ""`,
|
|
141
|
+
* which keeps large MartinLoop prompts off the command line on Windows.
|
|
142
|
+
*
|
|
143
|
+
* Requires the Gemini CLI to be installed and authenticated:
|
|
144
|
+
* npm install -g @google/gemini-cli
|
|
145
|
+
*/
|
|
146
|
+
export declare function createGeminiCliAdapter(options?: GeminiCliAdapterOptions): MartinAdapter;
|