@martinloop/mcp 0.2.7 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +49 -104
  2. package/dist/package-version.d.ts +1 -1
  3. package/dist/package-version.js +1 -1
  4. package/dist/prompts.d.ts +1 -1
  5. package/dist/resources.d.ts +1 -1
  6. package/dist/resources.js +2 -2
  7. package/dist/server-validation.d.ts +1 -0
  8. package/dist/server-validation.js +8 -0
  9. package/dist/server.js +87 -9
  10. package/dist/tools/doctor.d.ts +39 -1
  11. package/dist/tools/doctor.js +68 -9
  12. package/dist/tools/eval.js +3 -2
  13. package/dist/tools/get-run.d.ts +3 -0
  14. package/dist/tools/get-run.js +3 -1
  15. package/dist/tools/get-verification-results.d.ts +3 -0
  16. package/dist/tools/get-verification-results.js +3 -1
  17. package/dist/tools/plan.js +4 -2
  18. package/dist/tools/pr-tools.js +2 -1
  19. package/dist/tools/preflight.d.ts +41 -1
  20. package/dist/tools/preflight.js +74 -19
  21. package/dist/tools/run-dossier.d.ts +3 -0
  22. package/dist/tools/run-dossier.js +5 -2
  23. package/dist/tools/run-loop.d.ts +7 -2
  24. package/dist/tools/run-loop.js +67 -35
  25. package/dist/tools/run-store.js +67 -15
  26. package/dist/tools/tool-errors.js +1 -1
  27. package/dist/tools/tool-support.d.ts +8 -3
  28. package/dist/tools/tool-support.js +61 -18
  29. package/dist/tools/workflow-governance.d.ts +19 -3
  30. package/dist/tools/workflow-governance.js +107 -55
  31. package/dist/vendor/adapters/claude-cli.d.ts +45 -3
  32. package/dist/vendor/adapters/claude-cli.js +465 -45
  33. package/dist/vendor/adapters/cli-bridge.d.ts +46 -0
  34. package/dist/vendor/adapters/cli-bridge.js +147 -38
  35. package/dist/vendor/adapters/codex-launcher.d.ts +76 -0
  36. package/dist/vendor/adapters/codex-launcher.js +538 -0
  37. package/dist/vendor/adapters/index.d.ts +3 -2
  38. package/dist/vendor/adapters/index.js +3 -2
  39. package/dist/vendor/adapters/openai-compatible.d.ts +19 -4
  40. package/dist/vendor/adapters/openai-compatible.js +50 -19
  41. package/dist/vendor/adapters/runtime-support.d.ts +3 -0
  42. package/dist/vendor/adapters/runtime-support.js +9 -1
  43. package/dist/vendor/adapters/stub-direct-provider.js +3 -0
  44. package/dist/vendor/adapters/verifier-only.d.ts +2 -0
  45. package/dist/vendor/adapters/verifier-only.js +11 -4
  46. package/dist/vendor/contracts/index.d.ts +39 -0
  47. package/dist/vendor/contracts/index.js +2 -0
  48. package/dist/vendor/core/context-integrity.js +28 -3
  49. package/dist/vendor/core/grounding.d.ts +1 -0
  50. package/dist/vendor/core/grounding.js +6 -2
  51. package/dist/vendor/core/index.d.ts +24 -3
  52. package/dist/vendor/core/index.js +113 -21
  53. package/dist/vendor/core/leash.js +85 -8
  54. package/dist/vendor/core/persistence/index.d.ts +2 -0
  55. package/dist/vendor/core/persistence/index.js +1 -0
  56. package/dist/vendor/core/persistence/integrity.d.ts +38 -0
  57. package/dist/vendor/core/persistence/integrity.js +248 -0
  58. package/dist/vendor/core/persistence/store.d.ts +7 -0
  59. package/dist/vendor/core/persistence/store.js +25 -1
  60. package/dist/vendor/core/policy.d.ts +9 -0
  61. package/dist/workflow-state.d.ts +9 -0
  62. package/dist/workflow-state.js +46 -3
  63. package/package.json +2 -2
  64. package/server.json +2 -2
@@ -12,6 +12,8 @@ export interface RunContract {
12
12
  export interface AttemptArtifacts {
13
13
  /** Compiled PromptPacket written as compiled-context.json */
14
14
  compiledContext: unknown;
15
+ /** Structured verification evidence captured from the authoritative MartinLoop verifier (optional) */
16
+ verification?: unknown;
15
17
  /** Unified diff string from the patch (optional) */
16
18
  diff?: string;
17
19
  /** Raw verifier command output (optional) */
@@ -35,6 +37,11 @@ export interface AttemptArtifacts {
35
37
  * is durably written before the run proceeds to the next step.
36
38
  */
37
39
  export interface RunStore {
40
+ /**
41
+ * Optional runs root hint for filesystem-backed stores.
42
+ * Orchestration should prefer this over recomputing the default home store.
43
+ */
44
+ runsRoot?: string;
38
45
  /**
39
46
  * Write contract.json for a new run. Called once at run start.
40
47
  * The contract is immutable after this point.
@@ -1,7 +1,8 @@
1
1
  /// <reference types="node" />
2
- import { appendFile, mkdir, writeFile } from "node:fs/promises";
2
+ import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
+ import { writeReceiptIntegrityMaterial } from "./integrity.js";
5
6
  // ─── FileRunStore implementation ─────────────────────────────────────────────
6
7
  export function resolveRunsRoot(env = process.env) {
7
8
  return env["MARTIN_RUNS_DIR"]?.trim() ??
@@ -31,6 +32,7 @@ export function artifactDir(runsRoot, runId, attemptIndex) {
31
32
  export function createFileRunStore(options = {}) {
32
33
  const runsRoot = options.runsRoot ?? resolveRunsRoot();
33
34
  return {
35
+ runsRoot,
34
36
  async initRun(contract) {
35
37
  const dir = runDir(runsRoot, contract.runId);
36
38
  await mkdir(dir, { recursive: true });
@@ -50,6 +52,9 @@ export function createFileRunStore(options = {}) {
50
52
  const dir = artifactDir(runsRoot, runId, attemptIndex);
51
53
  await mkdir(dir, { recursive: true });
52
54
  await writeJsonFile(join(dir, "compiled-context.json"), artifacts.compiledContext);
55
+ if (artifacts.verification !== undefined) {
56
+ await writeJsonFile(join(dir, "verification.json"), artifacts.verification);
57
+ }
53
58
  if (artifacts.diff !== undefined) {
54
59
  await writeFile(join(dir, "diff.patch"), artifacts.diff, "utf8");
55
60
  }
@@ -79,6 +84,25 @@ export function createFileRunStore(options = {}) {
79
84
  const dir = runDir(runsRoot, runId);
80
85
  await mkdir(dir, { recursive: true });
81
86
  await writeJsonFile(join(dir, "loop-record.json"), loop);
87
+ const ledgerRaw = await readFile(join(dir, "ledger.jsonl"), "utf8").catch(() => "");
88
+ const ledgerEntries = ledgerRaw
89
+ .split(/\r?\n/u)
90
+ .map((line) => line.trim())
91
+ .filter(Boolean)
92
+ .map((line) => JSON.parse(line));
93
+ await writeReceiptIntegrityMaterial({
94
+ runId,
95
+ runsRoot,
96
+ loopRecord: loop,
97
+ ledgerEntries,
98
+ scope: loop.receiptScope ??
99
+ {
100
+ ...(loop.task.repoRoot ? { repoRoot: loop.task.repoRoot } : {}),
101
+ ...(loop.task.repoRoot ? { workingDirectory: loop.task.repoRoot } : {}),
102
+ runsRoot
103
+ },
104
+ signedAt: loop.updatedAt
105
+ });
82
106
  }
83
107
  };
84
108
  }
@@ -38,6 +38,15 @@ export interface MartinAdapterResultLike {
38
38
  verification: {
39
39
  passed: boolean;
40
40
  summary: string;
41
+ steps?: Array<{
42
+ command: string;
43
+ launched: boolean;
44
+ exitCode?: number;
45
+ timedOut: boolean;
46
+ fastFail?: boolean;
47
+ detail?: string;
48
+ }>;
49
+ warnings?: string[];
41
50
  };
42
51
  failure?: {
43
52
  message: string;
@@ -1,3 +1,4 @@
1
+ import type { LoopBudget, ReceiptScope } from "./vendor/contracts/index.js";
1
2
  type McpWorkflowStepName = "doctor" | "plan" | "preflight";
2
3
  export interface RecordMcpWorkflowStepInput {
3
4
  runsRoot: string;
@@ -6,6 +7,10 @@ export interface RecordMcpWorkflowStepInput {
6
7
  objective?: string;
7
8
  engine?: string;
8
9
  verificationPlan?: string[];
10
+ receiptScope?: ReceiptScope;
11
+ allowedPaths?: string[];
12
+ deniedPaths?: string[];
13
+ budget?: LoopBudget;
9
14
  }
10
15
  export interface EvaluateMcpRunGateInput {
11
16
  runsRoot: string;
@@ -13,6 +18,10 @@ export interface EvaluateMcpRunGateInput {
13
18
  objective: string;
14
19
  engine?: string;
15
20
  verificationPlan?: string[];
21
+ receiptScope?: ReceiptScope;
22
+ allowedPaths?: string[];
23
+ deniedPaths?: string[];
24
+ budget?: LoopBudget;
16
25
  }
17
26
  export interface McpRunGateResult {
18
27
  allowed: boolean;
@@ -15,7 +15,12 @@ export async function recordMcpWorkflowStep(input) {
15
15
  workingDirectory: normalizeWorkingDirectory(input.workingDirectory),
16
16
  ...(input.objective ? { objectiveKey: normalizeObjective(input.objective) } : {}),
17
17
  ...(input.engine ? { engine: input.engine } : {}),
18
- ...(input.verificationPlan ? { verificationPlanKey: hashVerificationPlan(input.verificationPlan) } : {})
18
+ ...(input.verificationPlan ? { verificationPlanKey: hashVerificationPlan(input.verificationPlan) } : {}),
19
+ ...(input.receiptScope ? { scopeKey: hashReceiptScope(input.receiptScope) } : {}),
20
+ ...(input.allowedPaths || input.deniedPaths
21
+ ? { pathScopeKey: hashPathScope(input.allowedPaths ?? [], input.deniedPaths ?? []) }
22
+ : {}),
23
+ ...(input.budget ? { budgetKey: hashBudget(input.budget) } : {})
19
24
  };
20
25
  await writeWorkflowState(input.runsRoot, state);
21
26
  }
@@ -26,18 +31,31 @@ export async function evaluateMcpRunGate(input) {
26
31
  const objectiveKey = normalizeObjective(input.objective);
27
32
  const engine = input.engine ?? "claude";
28
33
  const verificationPlanKey = hashVerificationPlan(input.verificationPlan ?? []);
34
+ const scopeKey = hashReceiptScope(input.receiptScope ?? {
35
+ invocationRoot: input.workingDirectory,
36
+ workingDirectory: input.workingDirectory,
37
+ repoRoot: input.workingDirectory,
38
+ runsRoot: input.runsRoot
39
+ });
40
+ const pathScopeKey = hashPathScope(input.allowedPaths ?? [], input.deniedPaths ?? []);
41
+ const budgetKey = input.budget ? hashBudget(input.budget) : undefined;
29
42
  const missingSteps = [];
30
- if (!isFresh(mcpState["doctor"], DOCTOR_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory)) {
43
+ if (!isFresh(mcpState["doctor"], DOCTOR_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory &&
44
+ receipt.scopeKey === scopeKey)) {
31
45
  missingSteps.push("doctor");
32
46
  }
33
47
  if (!isFresh(mcpState["plan"], PLAN_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory &&
48
+ receipt.scopeKey === scopeKey &&
34
49
  receipt.objectiveKey === objectiveKey)) {
35
50
  missingSteps.push("plan");
36
51
  }
37
52
  if (!isFresh(mcpState["preflight"], PREFLIGHT_TTL_MS, (receipt) => receipt.workingDirectory === workingDirectory &&
38
53
  receipt.objectiveKey === objectiveKey &&
39
54
  receipt.engine === engine &&
40
- receipt.verificationPlanKey === verificationPlanKey)) {
55
+ receipt.verificationPlanKey === verificationPlanKey &&
56
+ receipt.scopeKey === scopeKey &&
57
+ receipt.pathScopeKey === pathScopeKey &&
58
+ (budgetKey === undefined || receipt.budgetKey === budgetKey))) {
41
59
  missingSteps.push("preflight");
42
60
  }
43
61
  if (missingSteps.length === 0) {
@@ -100,3 +118,28 @@ function hashVerificationPlan(verificationPlan) {
100
118
  const normalized = verificationPlan.map((step) => step.trim()).filter(Boolean);
101
119
  return createHash("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
102
120
  }
121
+ function hashReceiptScope(receiptScope) {
122
+ const normalized = {
123
+ invocationRoot: normalizeWorkingDirectory(receiptScope.invocationRoot ?? receiptScope.workingDirectory ?? ""),
124
+ workingDirectory: normalizeWorkingDirectory(receiptScope.workingDirectory ?? receiptScope.repoRoot ?? ""),
125
+ repoRoot: normalizeWorkingDirectory(receiptScope.repoRoot ?? receiptScope.workingDirectory ?? ""),
126
+ runsRoot: normalizeWorkingDirectory(receiptScope.runsRoot ?? "")
127
+ };
128
+ return createHash("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
129
+ }
130
+ function hashPathScope(allowedPaths, deniedPaths) {
131
+ const normalized = {
132
+ allowedPaths: [...new Set(allowedPaths.map((entry) => entry.trim()).filter(Boolean))].sort(),
133
+ deniedPaths: [...new Set(deniedPaths.map((entry) => entry.trim()).filter(Boolean))].sort()
134
+ };
135
+ return createHash("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
136
+ }
137
+ function hashBudget(budget) {
138
+ const normalized = {
139
+ maxUsd: Number(budget.maxUsd.toFixed(4)),
140
+ softLimitUsd: Number(budget.softLimitUsd.toFixed(4)),
141
+ maxIterations: budget.maxIterations,
142
+ maxTokens: budget.maxTokens
143
+ };
144
+ return createHash("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
145
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@martinloop/mcp",
3
- "version": "0.2.7",
3
+ "version": "0.3.1",
4
4
  "mcpName": "io.github.Keesan12/martin-loop",
5
5
  "private": false,
6
6
  "type": "module",
@@ -55,7 +55,7 @@
55
55
  "smoke:published": "node ./scripts/smoke-published-package.mjs",
56
56
  "smoke:published:pack": "node ./scripts/smoke-published-package.mjs --package-spec=pack",
57
57
  "verify:release": "node --test ../../scripts/tests/publish-mcp-workflow.test.mjs ../../scripts/tests/mcp-publish-reliability.test.mjs ../../scripts/tests/mcp-release-docs.test.mjs",
58
- "test": "vitest run",
58
+ "test": "vitest run --maxWorkers=1",
59
59
  "lint": "tsc -p tsconfig.json --noEmit",
60
60
  "start": "node dist/server.js",
61
61
  "inspect:live": "node ./scripts/inspect-live.mjs"
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/Keesan12/martin-loop",
8
8
  "source": "github"
9
9
  },
10
- "version": "0.2.7",
10
+ "version": "0.3.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@martinloop/mcp",
15
- "version": "0.2.7",
15
+ "version": "0.3.1",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }