@martinloop/mcp 0.3.5 → 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 -137
- package/dist/discovery-metadata.d.ts +1 -1
- package/dist/discovery-metadata.js +2 -0
- package/dist/prompts.js +19 -1
- package/dist/resources.d.ts +3 -0
- package/dist/resources.js +125 -17
- package/dist/server-validation.d.ts +1 -1
- package/dist/server-validation.js +12 -0
- package/dist/server.js +18 -7
- package/dist/tools/doctor.js +6 -0
- package/dist/tools/run-loop.d.ts +2 -1
- package/dist/tools/run-loop.js +5 -1
- package/dist/tools/tool-support.d.ts +1 -1
- package/dist/tools/tool-support.js +2 -1
- 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.js +21 -20
- package/dist/vendor/adapters/cli-bridge.d.ts +7 -0
- package/dist/vendor/adapters/cli-bridge.js +94 -8
- package/dist/vendor/adapters/codex-launcher.js +18 -7
- 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 +84 -51
- package/dist/vendor/adapters/verifier-only.js +2 -2
- package/dist/vendor/contracts/index.d.ts +2 -2
- package/dist/vendor/contracts/index.js +1 -0
- package/dist/vendor/core/compiler.js +4 -9
- package/dist/vendor/core/index.d.ts +10 -2
- package/dist/vendor/core/index.js +32 -91
- package/dist/vendor/core/persistence/memory-store.d.ts +63 -0
- package/dist/vendor/core/persistence/memory-store.js +115 -0
- package/dist/vendor/core/persistence/trace-store.d.ts +85 -0
- package/dist/vendor/core/persistence/trace-store.js +125 -0
- package/dist/vendor/core/repo-analyzer.d.ts +53 -0
- package/dist/vendor/core/repo-analyzer.js +263 -0
- package/dist/vendor/core/rollback.js +2 -3
- package/dist/vendor/core/routing.d.ts +42 -0
- package/dist/vendor/core/routing.js +156 -5
- package/dist/vendor/core/test-integrity.d.ts +64 -0
- package/dist/vendor/core/test-integrity.js +197 -0
- package/dist/workflow-state.d.ts +1 -1
- package/dist/workflow-state.js +12 -4
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -522,10 +522,14 @@ export function createAgentCliAdapter(options) {
|
|
|
522
522
|
};
|
|
523
523
|
}
|
|
524
524
|
if (agentResult.exitCode !== 0 && agentResult.stdout.trim().length === 0) {
|
|
525
|
+
const fullStderr = agentResult.stderr.trim();
|
|
526
|
+
const stderrSnippet = fullStderr ? fullStderr.slice(0, 2000) : "(no stderr)";
|
|
527
|
+
const stdoutLen = agentResult.stdout.length;
|
|
528
|
+
const diagnosticSummary = `${options.command} exited (code ${String(agentResult.exitCode)}) with empty stdout (${String(stdoutLen)} bytes). stderr: ${stderrSnippet}`;
|
|
525
529
|
const failureMessage = formatPreVerifierSubprocessFailure(options.command, agentResult.stderr, agentResult.exitCode);
|
|
526
530
|
return {
|
|
527
531
|
status: "failed",
|
|
528
|
-
summary:
|
|
532
|
+
summary: diagnosticSummary,
|
|
529
533
|
usage: normalizeUsage({
|
|
530
534
|
actualUsd: 0,
|
|
531
535
|
tokensIn: 0,
|
|
@@ -534,6 +538,9 @@ export function createAgentCliAdapter(options) {
|
|
|
534
538
|
}),
|
|
535
539
|
verification: { passed: false, summary: `Verifier not run: ${failureMessage}` },
|
|
536
540
|
failure: {
|
|
541
|
+
// Raw stderr is preserved in diagnosticSummary above; use the
|
|
542
|
+
// normalized/classified message here so downstream consumers and
|
|
543
|
+
// tests get a consistent, parseable failure signal.
|
|
537
544
|
message: failureMessage
|
|
538
545
|
}
|
|
539
546
|
};
|
|
@@ -796,11 +803,15 @@ export function createClaudeCliAdapter(options = {}) {
|
|
|
796
803
|
"--verbose",
|
|
797
804
|
"--print",
|
|
798
805
|
"--dangerously-skip-permissions",
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
//
|
|
802
|
-
//
|
|
803
|
-
|
|
806
|
+
// Subprocess isolation strategy:
|
|
807
|
+
// --bare: skips hooks (prevents SessionEnd/hook failures causing non-zero exits),
|
|
808
|
+
// MCP server loading, LSP, CLAUDE.md discovery, and background prefetches.
|
|
809
|
+
// Requires ANTHROPIC_API_KEY (OAuth/keychain auth not available in bare mode).
|
|
810
|
+
// --strict-mcp-config: fallback when ANTHROPIC_API_KEY is not set — still
|
|
811
|
+
// prevents parent MCP servers from being inherited by the subprocess.
|
|
812
|
+
...(process.env["ANTHROPIC_API_KEY"] ? ["--bare"] : ["--strict-mcp-config"]),
|
|
813
|
+
// NOTE: --max-tokens does not exist in the claude CLI. Token cap enforcement
|
|
814
|
+
// is handled at the MartinLoop layer via streamingUsageCap, not via subprocess flags.
|
|
804
815
|
...modelArgs,
|
|
805
816
|
...extraArgs
|
|
806
817
|
],
|
|
@@ -900,21 +911,13 @@ function buildPrompt(request) {
|
|
|
900
911
|
const lines = [];
|
|
901
912
|
const mutationMode = request.context.mutationMode ?? "edit";
|
|
902
913
|
lines.push("You are running in autonomous agentic mode.");
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
lines.push("Do not ask for confirmation. Do not ask clarifying questions.");
|
|
906
|
-
}
|
|
907
|
-
else {
|
|
908
|
-
lines.push("MAKE ALL REQUIRED FILE EDITS NOW. Do not ask for confirmation. Do not ask clarifying questions.");
|
|
909
|
-
lines.push("Do not explain what you found without also making the changes. Edit the files and complete the task.");
|
|
910
|
-
}
|
|
914
|
+
lines.push("MAKE ALL REQUIRED FILE EDITS NOW. Do not ask for confirmation. Do not ask clarifying questions.");
|
|
915
|
+
lines.push("Do not explain what you found without also making the changes. Edit the files and complete the task.");
|
|
911
916
|
lines.push("");
|
|
912
917
|
lines.push("If PROGRESS.md exists in your working directory, read it first for context from prior attempts.");
|
|
913
918
|
lines.push("If it does not exist, proceed with the objective below.");
|
|
914
919
|
lines.push("");
|
|
915
|
-
lines.push(
|
|
916
|
-
? "Complete the following verification-only task without making file changes."
|
|
917
|
-
: "Complete the following coding task. Make all necessary file changes.");
|
|
920
|
+
lines.push("Complete the following coding task. Make all necessary file changes.");
|
|
918
921
|
lines.push("When you are done, the verification commands listed below must pass.");
|
|
919
922
|
lines.push("");
|
|
920
923
|
lines.push("OBJECTIVE:");
|
|
@@ -952,9 +955,7 @@ function buildPrompt(request) {
|
|
|
952
955
|
lines.push(` Attempt ${String(attemptNumber)}`);
|
|
953
956
|
lines.push(` Remaining budget: $${String(request.context.remainingBudgetUsd)} USD`);
|
|
954
957
|
lines.push(` Remaining iterations: ${String(request.context.remainingIterations)}`);
|
|
955
|
-
lines.push(
|
|
956
|
-
? " Do not modify files; only run verification."
|
|
957
|
-
: " Do not expand scope beyond what is needed to pass verification.");
|
|
958
|
+
lines.push(" Do not expand scope beyond what is needed to pass verification.");
|
|
958
959
|
lines.push("");
|
|
959
960
|
if (request.previousAttempts.length > 0) {
|
|
960
961
|
lines.push("PRIOR FAILED ATTEMPTS (learn from these — do not repeat the same mistakes):");
|
|
@@ -87,6 +87,13 @@ export interface SpawnPlan {
|
|
|
87
87
|
args: string[];
|
|
88
88
|
}
|
|
89
89
|
export declare function createSpawnPlan(command: string, args: string[], cwd: string, preserveRawForInjectedSpawn: boolean): SpawnPlan;
|
|
90
|
+
/**
|
|
91
|
+
* npm generates Windows shims (.cmd/.ps1, occasionally .bat) that ultimately just exec
|
|
92
|
+
* `node <real-cli-script>.js <args>` relative to the shim's own directory. Parse the shim text to
|
|
93
|
+
* find that real script path and return it if it resolves to a file that actually exists on disk;
|
|
94
|
+
* otherwise return undefined so callers fall back to the existing wrapper-shell behavior unchanged.
|
|
95
|
+
*/
|
|
96
|
+
export declare function resolveNpmShimScript(shimPath: string): string | undefined;
|
|
90
97
|
/**
|
|
91
98
|
* Returns true if the command string contains shell operators that cannot be
|
|
92
99
|
* passed as literal arguments to spawn(). These must be routed through a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { delimiter, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { diffStatsFromNumstat } from "./runtime-support.js";
|
|
5
5
|
const gitRepositoryRootCache = new Map();
|
|
6
6
|
export async function runSubprocess(command, args, options) {
|
|
@@ -319,20 +319,106 @@ export function createSpawnPlan(command, args, cwd, preserveRawForInjectedSpawn)
|
|
|
319
319
|
};
|
|
320
320
|
}
|
|
321
321
|
const extension = extname(resolvedOrUndefined).toLowerCase();
|
|
322
|
-
if (extension === ".cmd" || extension === ".bat") {
|
|
322
|
+
if (extension === ".cmd" || extension === ".bat" || extension === ".ps1") {
|
|
323
|
+
// npm-installed CLIs resolve to a generated shim on Windows. Wrapping that shim through an
|
|
324
|
+
// extra cmd.exe/powershell.exe hop adds a process layer that can lose the OS-level
|
|
325
|
+
// workspace-write sandbox permission when the whole tree is already nested inside another
|
|
326
|
+
// restricted parent process (e.g. VS Code's extension host), even though the same shim works
|
|
327
|
+
// fine from a top-level PowerShell window. When we can statically resolve the shim's real
|
|
328
|
+
// wrapped `node <script>` target, invoke that directly instead — this removes the extra hop
|
|
329
|
+
// for every Windows launch, nested or not, with no behavior change when resolution fails.
|
|
330
|
+
const directScript = resolveNpmShimScript(resolvedOrUndefined);
|
|
331
|
+
if (directScript !== undefined) {
|
|
332
|
+
// Use the system node rather than process.execPath. When MartinLoop runs
|
|
333
|
+
// inside Claude Code desktop or VS Code, process.execPath is Electron's
|
|
334
|
+
// bundled Node — which has different module resolution and may fail to
|
|
335
|
+
// load native CLI scripts. Prefer an explicit `node` from PATH instead.
|
|
336
|
+
const systemNode = resolveSystemNode();
|
|
337
|
+
return { command: systemNode, args: [directScript, ...args] };
|
|
338
|
+
}
|
|
339
|
+
if (extension === ".ps1") {
|
|
340
|
+
return {
|
|
341
|
+
command: "powershell.exe",
|
|
342
|
+
args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", resolvedOrUndefined, ...args]
|
|
343
|
+
};
|
|
344
|
+
}
|
|
323
345
|
return {
|
|
324
346
|
command: process.env.ComSpec || "cmd.exe",
|
|
325
347
|
args: ["/d", "/c", resolvedOrUndefined, ...args]
|
|
326
348
|
};
|
|
327
349
|
}
|
|
328
|
-
if (extension === ".ps1") {
|
|
329
|
-
return {
|
|
330
|
-
command: "powershell.exe",
|
|
331
|
-
args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", resolvedOrUndefined, ...args]
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
350
|
return { command: resolvedOrUndefined, args };
|
|
335
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* npm generates Windows shims (.cmd/.ps1, occasionally .bat) that ultimately just exec
|
|
354
|
+
* `node <real-cli-script>.js <args>` relative to the shim's own directory. Parse the shim text to
|
|
355
|
+
* find that real script path and return it if it resolves to a file that actually exists on disk;
|
|
356
|
+
* otherwise return undefined so callers fall back to the existing wrapper-shell behavior unchanged.
|
|
357
|
+
*/
|
|
358
|
+
export function resolveNpmShimScript(shimPath) {
|
|
359
|
+
let contents;
|
|
360
|
+
try {
|
|
361
|
+
contents = readFileSync(shimPath, "utf8");
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
const shimDir = dirname(shimPath);
|
|
367
|
+
const scriptPathPattern = /["']?(?:%~?dp0%?|\$basedir)[\\/]([^"'\s]+\.[cm]?js)["']?/gi;
|
|
368
|
+
const matches = contents.matchAll(scriptPathPattern);
|
|
369
|
+
for (const match of matches) {
|
|
370
|
+
const relativeScript = match[1];
|
|
371
|
+
if (!relativeScript) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const segments = relativeScript.split(/[\\/]+/u).filter(Boolean);
|
|
375
|
+
const resolvedScript = resolve(shimDir, ...segments);
|
|
376
|
+
if (existsSync(resolvedScript)) {
|
|
377
|
+
return resolvedScript;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Resolve a reliable `node` executable for spawning CLI scripts on Windows.
|
|
384
|
+
*
|
|
385
|
+
* When MartinLoop runs inside Claude Code desktop, VS Code, or the Codex IDE,
|
|
386
|
+
* `process.execPath` points to the host application's bundled Electron Node —
|
|
387
|
+
* not the system Node. Electron's Node has different module resolution paths
|
|
388
|
+
* and may fail to load native npm CLI scripts that expect the system Node.
|
|
389
|
+
*
|
|
390
|
+
* Strategy:
|
|
391
|
+
* 1. Use MARTIN_NODE_PATH env var if explicitly set (escape hatch for CI)
|
|
392
|
+
* 2. Look for `node` / `node.exe` on PATH — the system install
|
|
393
|
+
* 3. Fall back to process.execPath (Electron Node) if nothing else found
|
|
394
|
+
*/
|
|
395
|
+
function resolveSystemNode() {
|
|
396
|
+
// Explicit override — useful in CI or restricted environments
|
|
397
|
+
const envOverride = process.env.MARTIN_NODE_PATH?.trim();
|
|
398
|
+
if (envOverride && envOverride.length > 0 && existsSync(envOverride)) {
|
|
399
|
+
return envOverride;
|
|
400
|
+
}
|
|
401
|
+
// Search PATH for a real `node` executable, skipping Electron binaries.
|
|
402
|
+
// Electron's node path typically contains "electron" or "Claude" in it.
|
|
403
|
+
for (const dir of windowsPathDirectories()) {
|
|
404
|
+
for (const candidate of ["node.exe", "node.cmd", "node"]) {
|
|
405
|
+
const fullPath = join(dir, candidate);
|
|
406
|
+
if (existsSync(fullPath) && !isElectronNode(fullPath)) {
|
|
407
|
+
return fullPath;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
// Last resort: use process.execPath even if it's Electron's node
|
|
412
|
+
return process.execPath;
|
|
413
|
+
}
|
|
414
|
+
function isElectronNode(nodePath) {
|
|
415
|
+
const lower = nodePath.toLowerCase();
|
|
416
|
+
return (lower.includes("electron") ||
|
|
417
|
+
lower.includes("claude") ||
|
|
418
|
+
lower.includes("vscode") ||
|
|
419
|
+
lower.includes("code.exe") ||
|
|
420
|
+
lower.includes("cursor"));
|
|
421
|
+
}
|
|
336
422
|
function resolveWindowsCommand(command, cwd) {
|
|
337
423
|
const hasPathSegment = command.includes("\\") || command.includes("/");
|
|
338
424
|
const baseCandidates = expandWindowsCommandCandidates(hasPathSegment ? resolve(cwd, command) : command);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
3
3
|
import { dirname, extname, join, resolve } from "node:path";
|
|
4
|
+
import { resolveNpmShimScript } from "./cli-bridge.js";
|
|
4
5
|
const codexLaunchProbeCache = new Map();
|
|
5
6
|
export const DEFAULT_CODEX_CHATGPT_MODEL = "gpt-5.4";
|
|
6
7
|
const CODEX_LAUNCH_PROBE_PROMPT = [
|
|
@@ -121,17 +122,27 @@ function buildProbeCommand(command, args, platform) {
|
|
|
121
122
|
switch (extension) {
|
|
122
123
|
case ".cmd":
|
|
123
124
|
case ".bat":
|
|
125
|
+
case ".ps1": {
|
|
126
|
+
// Bypass the npm shim's cmd.exe/powershell.exe wrapper hop when we can statically resolve
|
|
127
|
+
// the real `node <script>` target it wraps — keeps the live probe's process-nesting depth
|
|
128
|
+
// consistent with the real run's invocation (see createSpawnPlan in cli-bridge.ts).
|
|
129
|
+
const directScript = resolveNpmShimScript(command);
|
|
130
|
+
if (directScript !== undefined) {
|
|
131
|
+
return { command: process.execPath, args: [directScript, ...args], invocationMode: "direct" };
|
|
132
|
+
}
|
|
133
|
+
if (extension === ".ps1") {
|
|
134
|
+
return {
|
|
135
|
+
command: "powershell.exe",
|
|
136
|
+
args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", command, ...args],
|
|
137
|
+
invocationMode: "powershell"
|
|
138
|
+
};
|
|
139
|
+
}
|
|
124
140
|
return {
|
|
125
141
|
command: process.env.ComSpec || "cmd.exe",
|
|
126
142
|
args: ["/d", "/c", command, ...args],
|
|
127
143
|
invocationMode: "cmd_shell"
|
|
128
144
|
};
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
command: "powershell.exe",
|
|
132
|
-
args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", command, ...args],
|
|
133
|
-
invocationMode: "powershell"
|
|
134
|
-
};
|
|
145
|
+
}
|
|
135
146
|
default:
|
|
136
147
|
return { command, args, invocationMode: "direct" };
|
|
137
148
|
}
|
|
@@ -236,7 +247,7 @@ function classifyProbeFailure(stderr, stdout, diagnosis) {
|
|
|
236
247
|
...diagnosis,
|
|
237
248
|
sandboxCompatible: false,
|
|
238
249
|
warnings,
|
|
239
|
-
remediation: "Update or reinstall Codex on this Windows host until `codex exec --sandbox workspace-write` can launch a simple shell command before running governed Codex work."
|
|
250
|
+
remediation: "MartinLoop already invokes Codex's underlying binary directly when it can resolve the npm shim (bypassing the cmd.exe/PowerShell wrapper hop), so this failure persisted even at the shallowest invocation depth available. Update or reinstall Codex on this Windows host until `codex exec --sandbox workspace-write` can launch a simple shell command before running governed Codex work."
|
|
240
251
|
}
|
|
241
252
|
};
|
|
242
253
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type ContextGraphNodeKind = "source_file" | "document" | "manifest" | "script" | "config";
|
|
2
|
+
export type ContextGraphEdgeKind = "imports" | "documents";
|
|
3
|
+
export interface ContextGraphNode {
|
|
4
|
+
nodeId: string;
|
|
5
|
+
path: string;
|
|
6
|
+
kind: ContextGraphNodeKind;
|
|
7
|
+
language?: string;
|
|
8
|
+
sizeBytes: number;
|
|
9
|
+
headings: string[];
|
|
10
|
+
symbols: string[];
|
|
11
|
+
keywords: string[];
|
|
12
|
+
imports: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface ContextGraphEdge {
|
|
15
|
+
fromNodeId: string;
|
|
16
|
+
toNodeId: string;
|
|
17
|
+
kind: ContextGraphEdgeKind;
|
|
18
|
+
}
|
|
19
|
+
export interface ContextGraphSnapshot {
|
|
20
|
+
schemaVersion: "martin.context-graph.v1";
|
|
21
|
+
repoRoot: string;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
nodeCount: number;
|
|
24
|
+
edgeCount: number;
|
|
25
|
+
truncated: boolean;
|
|
26
|
+
nodes: ContextGraphNode[];
|
|
27
|
+
edges: ContextGraphEdge[];
|
|
28
|
+
}
|
|
29
|
+
export interface ContextQuery {
|
|
30
|
+
text?: string;
|
|
31
|
+
terms?: string[];
|
|
32
|
+
pathPrefix?: string;
|
|
33
|
+
kinds?: ContextGraphNodeKind[];
|
|
34
|
+
limit?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface ContextGraphBuildOptions {
|
|
37
|
+
maxFiles?: number;
|
|
38
|
+
maxFileBytes?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface ContextGraphHit {
|
|
41
|
+
nodeId: string;
|
|
42
|
+
path: string;
|
|
43
|
+
kind: ContextGraphNodeKind;
|
|
44
|
+
score: number;
|
|
45
|
+
matchedTerms: string[];
|
|
46
|
+
reasons: string[];
|
|
47
|
+
}
|
|
48
|
+
export declare function cloneContextGraphSnapshot(snapshot: ContextGraphSnapshot): ContextGraphSnapshot;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function cloneContextGraphSnapshot(snapshot) {
|
|
2
|
+
return {
|
|
3
|
+
schemaVersion: snapshot.schemaVersion,
|
|
4
|
+
repoRoot: snapshot.repoRoot,
|
|
5
|
+
createdAt: snapshot.createdAt,
|
|
6
|
+
nodeCount: snapshot.nodeCount,
|
|
7
|
+
edgeCount: snapshot.edgeCount,
|
|
8
|
+
truncated: snapshot.truncated,
|
|
9
|
+
nodes: snapshot.nodes.map((node) => ({
|
|
10
|
+
nodeId: node.nodeId,
|
|
11
|
+
path: node.path,
|
|
12
|
+
kind: node.kind,
|
|
13
|
+
...(node.language ? { language: node.language } : {}),
|
|
14
|
+
sizeBytes: node.sizeBytes,
|
|
15
|
+
headings: [...node.headings],
|
|
16
|
+
symbols: [...node.symbols],
|
|
17
|
+
keywords: [...node.keywords],
|
|
18
|
+
imports: [...node.imports]
|
|
19
|
+
})),
|
|
20
|
+
edges: snapshot.edges.map((edge) => ({
|
|
21
|
+
fromNodeId: edge.fromNodeId,
|
|
22
|
+
toNodeId: edge.toNodeId,
|
|
23
|
+
kind: edge.kind
|
|
24
|
+
}))
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { ApprovalPolicy, LoopBudget, LoopTask, MutationMode } from "./index.js";
|
|
2
|
+
import type { DestructiveActionPolicy, PolicyProfile, TelemetryDestination } from "./governance.js";
|
|
3
|
+
export interface ExecutionPolicy {
|
|
4
|
+
configPath: string;
|
|
5
|
+
budget: LoopBudget;
|
|
6
|
+
governance: {
|
|
7
|
+
policyProfile: PolicyProfile;
|
|
8
|
+
telemetryDestination: TelemetryDestination;
|
|
9
|
+
destructiveActionPolicy: DestructiveActionPolicy;
|
|
10
|
+
};
|
|
11
|
+
task: {
|
|
12
|
+
verificationPlan: string[];
|
|
13
|
+
mutationMode?: MutationMode;
|
|
14
|
+
repoRoot?: string;
|
|
15
|
+
allowedPaths?: string[];
|
|
16
|
+
deniedPaths?: string[];
|
|
17
|
+
acceptanceCriteria?: string[];
|
|
18
|
+
approvalPolicy?: ApprovalPolicy;
|
|
19
|
+
};
|
|
20
|
+
provenance: ExecutionPolicyProvenanceEntry[];
|
|
21
|
+
}
|
|
22
|
+
export interface ExecutionPolicyProvenanceEntry {
|
|
23
|
+
field: string;
|
|
24
|
+
source: "default" | "config" | "request";
|
|
25
|
+
value: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ExecutionPolicyDefaults {
|
|
28
|
+
budget: LoopBudget;
|
|
29
|
+
policyProfile: PolicyProfile;
|
|
30
|
+
telemetryDestination: TelemetryDestination;
|
|
31
|
+
destructiveActionPolicy: DestructiveActionPolicy;
|
|
32
|
+
verifierRules: string[];
|
|
33
|
+
}
|
|
34
|
+
export interface ExecutionPolicyConfigInput {
|
|
35
|
+
policyProfile?: string;
|
|
36
|
+
budget?: Partial<LoopBudget>;
|
|
37
|
+
governance?: {
|
|
38
|
+
destructiveActionPolicy?: string;
|
|
39
|
+
telemetryDestination?: string;
|
|
40
|
+
verifierRules?: string[];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export interface ExecutionPolicyRequestInput {
|
|
44
|
+
budget: LoopBudget;
|
|
45
|
+
budgetOverrides?: Partial<Record<keyof LoopBudget, true>>;
|
|
46
|
+
policyProfile?: string;
|
|
47
|
+
telemetryDestination?: string;
|
|
48
|
+
verificationPlan?: string[];
|
|
49
|
+
mutationMode?: LoopTask["mutationMode"];
|
|
50
|
+
repoRoot?: string;
|
|
51
|
+
allowedPaths?: string[];
|
|
52
|
+
deniedPaths?: string[];
|
|
53
|
+
acceptanceCriteria?: string[];
|
|
54
|
+
approvalPolicy?: ApprovalPolicy;
|
|
55
|
+
}
|
|
56
|
+
export interface ExecutionPolicyCompileInput {
|
|
57
|
+
configPath: string;
|
|
58
|
+
defaults: ExecutionPolicyDefaults;
|
|
59
|
+
config?: ExecutionPolicyConfigInput;
|
|
60
|
+
request: ExecutionPolicyRequestInput;
|
|
61
|
+
}
|
|
62
|
+
export type RoutingMode = "direct" | "manager" | "adaptive" | "consensus";
|
|
63
|
+
export interface RoutingPolicy {
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
mode: RoutingMode;
|
|
66
|
+
/** Max percentage of total budget that can be spent before first code change. */
|
|
67
|
+
maxPreworkBudgetPct: number;
|
|
68
|
+
/** Hard dollar cap on pre-work spend. */
|
|
69
|
+
maxPreworkCostUsd: number;
|
|
70
|
+
/** Max model calls allowed for manager/router/planner before worker starts. */
|
|
71
|
+
maxManagerCalls: number;
|
|
72
|
+
/** Skip orchestration entirely if task confidence is above this threshold (0-1). */
|
|
73
|
+
skipOrchestrationIfConfidenceAbove: number;
|
|
74
|
+
/** Force direct execution for tasks classified as simple. */
|
|
75
|
+
requireDirectForSimpleTasks: boolean;
|
|
76
|
+
}
|
|
77
|
+
export declare const DEFAULT_ROUTING_POLICY: RoutingPolicy;
|
|
78
|
+
export declare function cloneExecutionPolicy(policy: ExecutionPolicy): ExecutionPolicy;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DEFAULT_ROUTING_POLICY = {
|
|
2
|
+
enabled: true,
|
|
3
|
+
mode: "adaptive",
|
|
4
|
+
maxPreworkBudgetPct: 25,
|
|
5
|
+
maxPreworkCostUsd: 2.0,
|
|
6
|
+
maxManagerCalls: 2,
|
|
7
|
+
skipOrchestrationIfConfidenceAbove: 0.85,
|
|
8
|
+
requireDirectForSimpleTasks: true
|
|
9
|
+
};
|
|
10
|
+
export function cloneExecutionPolicy(policy) {
|
|
11
|
+
return {
|
|
12
|
+
configPath: policy.configPath,
|
|
13
|
+
budget: {
|
|
14
|
+
maxUsd: policy.budget.maxUsd,
|
|
15
|
+
softLimitUsd: policy.budget.softLimitUsd,
|
|
16
|
+
maxIterations: policy.budget.maxIterations,
|
|
17
|
+
maxTokens: policy.budget.maxTokens
|
|
18
|
+
},
|
|
19
|
+
governance: {
|
|
20
|
+
policyProfile: policy.governance.policyProfile,
|
|
21
|
+
telemetryDestination: policy.governance.telemetryDestination,
|
|
22
|
+
destructiveActionPolicy: policy.governance.destructiveActionPolicy
|
|
23
|
+
},
|
|
24
|
+
task: {
|
|
25
|
+
verificationPlan: [...policy.task.verificationPlan],
|
|
26
|
+
...(policy.task.mutationMode ? { mutationMode: policy.task.mutationMode } : {}),
|
|
27
|
+
...(policy.task.repoRoot ? { repoRoot: policy.task.repoRoot } : {}),
|
|
28
|
+
...(policy.task.allowedPaths ? { allowedPaths: [...policy.task.allowedPaths] } : {}),
|
|
29
|
+
...(policy.task.deniedPaths ? { deniedPaths: [...policy.task.deniedPaths] } : {}),
|
|
30
|
+
...(policy.task.acceptanceCriteria
|
|
31
|
+
? { acceptanceCriteria: [...policy.task.acceptanceCriteria] }
|
|
32
|
+
: {}),
|
|
33
|
+
...(policy.task.approvalPolicy
|
|
34
|
+
? {
|
|
35
|
+
approvalPolicy: {
|
|
36
|
+
...policy.task.approvalPolicy
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
: {})
|
|
40
|
+
},
|
|
41
|
+
provenance: policy.provenance.map((entry) => ({ ...entry }))
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type PolicyProfile = "strict" | "balanced" | "overnight" | "debug";
|
|
2
|
+
export type DestructiveActionPolicy = "never" | "approval" | "allowed";
|
|
3
|
+
export type TelemetryDestination = "local-only" | "control-plane";
|
|
4
|
+
export type GovernanceSnapshot = {
|
|
5
|
+
policyProfile: PolicyProfile;
|
|
6
|
+
maxUsd: number;
|
|
7
|
+
softLimitUsd: number;
|
|
8
|
+
maxTokens: number;
|
|
9
|
+
maxIterations: number;
|
|
10
|
+
allowedAdapters: string[];
|
|
11
|
+
allowedModels: string[];
|
|
12
|
+
destructiveActionPolicy: DestructiveActionPolicy;
|
|
13
|
+
approvalRequired: boolean;
|
|
14
|
+
verifierRules: string[];
|
|
15
|
+
escalationRoute: string;
|
|
16
|
+
telemetryDestination: TelemetryDestination;
|
|
17
|
+
retentionPolicy: string;
|
|
18
|
+
provenance: Array<{
|
|
19
|
+
field: string;
|
|
20
|
+
value: string;
|
|
21
|
+
source: string;
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
export declare function createGovernanceSnapshot(snapshot: GovernanceSnapshot): GovernanceSnapshot;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function createGovernanceSnapshot(snapshot) {
|
|
2
|
+
return {
|
|
3
|
+
...snapshot,
|
|
4
|
+
allowedAdapters: [...snapshot.allowedAdapters],
|
|
5
|
+
allowedModels: [...snapshot.allowedModels],
|
|
6
|
+
verifierRules: [...snapshot.verifierRules],
|
|
7
|
+
provenance: snapshot.provenance.map((entry) => ({
|
|
8
|
+
...entry
|
|
9
|
+
}))
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type IdentityAlgorithm = "ed25519";
|
|
2
|
+
export type IdentityRole = "governor" | "verifier" | "worker" | "reviewer" | "observer";
|
|
3
|
+
export interface IdentityClaims {
|
|
4
|
+
subject: string;
|
|
5
|
+
issuer: string;
|
|
6
|
+
role: IdentityRole;
|
|
7
|
+
scopes: string[];
|
|
8
|
+
issuedAt: string;
|
|
9
|
+
expiresAt?: string;
|
|
10
|
+
workspaceId?: string;
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
metadata?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export interface IdentityToken {
|
|
15
|
+
schemaVersion: "martin.identity.v1";
|
|
16
|
+
algorithm: IdentityAlgorithm;
|
|
17
|
+
keyId: string;
|
|
18
|
+
claims: IdentityClaims;
|
|
19
|
+
signature: string;
|
|
20
|
+
}
|
|
21
|
+
export interface IdentityAttestation {
|
|
22
|
+
valid: boolean;
|
|
23
|
+
verifiedAt: string;
|
|
24
|
+
reason: string;
|
|
25
|
+
token: IdentityToken;
|
|
26
|
+
}
|
|
27
|
+
export declare function cloneIdentityClaims(claims: IdentityClaims): IdentityClaims;
|
|
28
|
+
export declare function cloneIdentityToken(token: IdentityToken): IdentityToken;
|
|
29
|
+
export declare function cloneIdentityAttestation(attestation: IdentityAttestation): IdentityAttestation;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function cloneIdentityClaims(claims) {
|
|
2
|
+
return {
|
|
3
|
+
subject: claims.subject,
|
|
4
|
+
issuer: claims.issuer,
|
|
5
|
+
role: claims.role,
|
|
6
|
+
scopes: [...claims.scopes],
|
|
7
|
+
issuedAt: claims.issuedAt,
|
|
8
|
+
...(claims.expiresAt ? { expiresAt: claims.expiresAt } : {}),
|
|
9
|
+
...(claims.workspaceId ? { workspaceId: claims.workspaceId } : {}),
|
|
10
|
+
...(claims.sessionId ? { sessionId: claims.sessionId } : {}),
|
|
11
|
+
...(claims.metadata ? { metadata: { ...claims.metadata } } : {})
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function cloneIdentityToken(token) {
|
|
15
|
+
return {
|
|
16
|
+
schemaVersion: token.schemaVersion,
|
|
17
|
+
algorithm: token.algorithm,
|
|
18
|
+
keyId: token.keyId,
|
|
19
|
+
claims: cloneIdentityClaims(token.claims),
|
|
20
|
+
signature: token.signature
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function cloneIdentityAttestation(attestation) {
|
|
24
|
+
return {
|
|
25
|
+
valid: attestation.valid,
|
|
26
|
+
verifiedAt: attestation.verifiedAt,
|
|
27
|
+
reason: attestation.reason,
|
|
28
|
+
token: cloneIdentityToken(attestation.token)
|
|
29
|
+
};
|
|
30
|
+
}
|