@martinloop/mcp 0.2.7 → 0.3.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.
Files changed (52) 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 +18 -2
  10. package/dist/tools/doctor.d.ts +12 -1
  11. package/dist/tools/doctor.js +37 -6
  12. package/dist/tools/eval.js +3 -2
  13. package/dist/tools/get-run.d.ts +2 -0
  14. package/dist/tools/get-run.js +2 -1
  15. package/dist/tools/get-verification-results.d.ts +2 -0
  16. package/dist/tools/get-verification-results.js +2 -1
  17. package/dist/tools/pr-tools.js +2 -1
  18. package/dist/tools/preflight.d.ts +14 -1
  19. package/dist/tools/preflight.js +36 -5
  20. package/dist/tools/run-dossier.d.ts +2 -0
  21. package/dist/tools/run-dossier.js +4 -2
  22. package/dist/tools/run-loop.d.ts +3 -2
  23. package/dist/tools/run-loop.js +48 -28
  24. package/dist/tools/tool-errors.js +1 -1
  25. package/dist/tools/tool-support.d.ts +6 -3
  26. package/dist/tools/tool-support.js +12 -5
  27. package/dist/vendor/adapters/claude-cli.d.ts +25 -0
  28. package/dist/vendor/adapters/claude-cli.js +279 -19
  29. package/dist/vendor/adapters/cli-bridge.d.ts +1 -0
  30. package/dist/vendor/adapters/cli-bridge.js +44 -3
  31. package/dist/vendor/adapters/codex-launcher.d.ts +44 -0
  32. package/dist/vendor/adapters/codex-launcher.js +247 -0
  33. package/dist/vendor/adapters/index.d.ts +3 -2
  34. package/dist/vendor/adapters/index.js +3 -2
  35. package/dist/vendor/adapters/openai-compatible.d.ts +19 -4
  36. package/dist/vendor/adapters/openai-compatible.js +44 -19
  37. package/dist/vendor/adapters/runtime-support.d.ts +3 -0
  38. package/dist/vendor/adapters/runtime-support.js +8 -1
  39. package/dist/vendor/adapters/verifier-only.js +4 -3
  40. package/dist/vendor/contracts/index.d.ts +39 -0
  41. package/dist/vendor/contracts/index.js +2 -0
  42. package/dist/vendor/core/index.d.ts +23 -3
  43. package/dist/vendor/core/index.js +88 -15
  44. package/dist/vendor/core/persistence/index.d.ts +2 -0
  45. package/dist/vendor/core/persistence/index.js +1 -0
  46. package/dist/vendor/core/persistence/integrity.d.ts +38 -0
  47. package/dist/vendor/core/persistence/integrity.js +239 -0
  48. package/dist/vendor/core/persistence/store.d.ts +7 -0
  49. package/dist/vendor/core/persistence/store.js +25 -1
  50. package/dist/vendor/core/policy.d.ts +9 -0
  51. package/package.json +1 -1
  52. package/server.json +2 -2
@@ -2,6 +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 } from "../vendor/contracts/index.js";
5
6
  export interface MartinRunDossierInput {
6
7
  file?: string;
7
8
  loopId?: string;
@@ -15,6 +16,7 @@ export interface MartinRunDossierOutput {
15
16
  loop: ReturnType<typeof buildLoopPreview>;
16
17
  budget: ReturnType<typeof buildBudgetSnapshot>;
17
18
  cost: ReturnType<typeof buildCostSnapshot>;
19
+ receiptIntegrity: ReceiptIntegritySummary;
18
20
  attempts: Array<{
19
21
  index: number;
20
22
  attemptId?: string;
@@ -1,4 +1,5 @@
1
- import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildEventSummaries, buildLoopPreview, buildSuggestedPromptNames, buildSuggestedResourceUris, buildVerificationSummary } from "./tool-support.js";
1
+ import { buildArtifactSummary, buildBudgetSnapshot, buildCostSnapshot, buildEventSummaries, buildLoopPreview, resolveReceiptIntegrity, buildSuggestedPromptNames, buildSuggestedResourceUris, buildVerificationSummary } from "./tool-support.js";
2
+ import { resolveTrustedLoopRepoRoot } from "../server-validation.js";
2
3
  import { loadDetailedLoopRecord, readAttemptArtifactFiles, readLedgerEvents } from "./run-store.js";
3
4
  import { readRunControlState } from "./run-controls.js";
4
5
  import { martinEvalTool } from "./eval.js";
@@ -9,7 +10,7 @@ export async function martinRunDossierTool(input) {
9
10
  const verification = buildVerificationSummary(detail.loop, ledgerEvents);
10
11
  const control = await readRunControlState(detail);
11
12
  const evaluation = await martinEvalTool(input);
12
- const repoRoot = detail.loop.task?.repoRoot ?? process.cwd();
13
+ const repoRoot = resolveTrustedLoopRepoRoot(detail.loop.task?.repoRoot);
13
14
  const risk = assessRunRisk({
14
15
  objective: detail.loop.task?.objective ?? detail.loop.loopId,
15
16
  allowedPaths: detail.loop.task?.allowedPaths ?? [],
@@ -51,6 +52,7 @@ export async function martinRunDossierTool(input) {
51
52
  loop: buildLoopPreview(detail.loop),
52
53
  budget: buildBudgetSnapshot(detail.loop.budget),
53
54
  cost: buildCostSnapshot(detail.loop.cost),
55
+ receiptIntegrity: resolveReceiptIntegrity(detail.loop),
54
56
  attempts,
55
57
  verification,
56
58
  artifacts: buildArtifactSummary(detail.loop),
@@ -1,9 +1,9 @@
1
- import { type LoopBudget } from "../vendor/contracts/index.js";
1
+ import { type LoopBudget, type ReceiptScope } from "../vendor/contracts/index.js";
2
2
  import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, type MartinEngine } from "./tool-support.js";
3
3
  export interface RunLoopInput {
4
4
  objective: string;
5
5
  workingDirectory?: string;
6
- engine?: "claude" | "codex";
6
+ engine?: "claude" | "codex" | "gemini";
7
7
  model?: string;
8
8
  maxUsd?: number;
9
9
  maxIterations?: number;
@@ -35,6 +35,7 @@ export interface RunLoopOutput {
35
35
  runDirectory: string;
36
36
  loopRecordPath: string;
37
37
  ledgerPath: string;
38
+ receiptScope: ReceiptScope;
38
39
  loop: ReturnType<typeof buildLoopPreview>;
39
40
  verification: ReturnType<typeof buildVerificationSummary>;
40
41
  artifacts: ReturnType<typeof buildArtifactSummary>;
@@ -1,8 +1,7 @@
1
- import { createClaudeCliAdapter, createCodexCliAdapter, createStubDirectProviderAdapter } from "../vendor/adapters/index.js";
1
+ import { createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, probeCodexLaunch, resolveCliCommandAvailability, createVerifierOnlyAdapter } from "../vendor/adapters/index.js";
2
2
  import { createFileRunStore, evaluateCostGovernor, resolveRunsRoot, runMartin } from "../vendor/core/index.js";
3
3
  import { DEFAULT_BUDGET } from "../vendor/contracts/index.js";
4
4
  import { normalizeSafePathPatterns, resolveSafeRepoRoot } from "../server-validation.js";
5
- import { evaluateMcpRunGate } from "../workflow-state.js";
6
5
  import { MartinToolError } from "./tool-errors.js";
7
6
  import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, buildRunRecordPaths, getEngineAvailability, resolveExecutionMode } from "./tool-support.js";
8
7
  export async function runLoopTool(input) {
@@ -12,38 +11,57 @@ export async function runLoopTool(input) {
12
11
  const allowedPaths = normalizeSafePathPatterns(input.allowedPaths, "allowedPaths");
13
12
  const deniedPaths = normalizeSafePathPatterns(input.deniedPaths, "deniedPaths");
14
13
  const executionMode = resolveExecutionMode();
15
- const engineAvailability = getEngineAvailability(engine);
14
+ const workspaceRoot = resolveSafeRepoRoot();
16
15
  const runsRoot = resolveRunsRoot(process.env);
17
- const gate = await evaluateMcpRunGate({
18
- runsRoot,
16
+ const receiptScope = {
17
+ invocationRoot: workspaceRoot,
19
18
  workingDirectory,
20
- objective: input.objective,
21
- engine,
22
- verificationPlan: input.verificationPlan
23
- });
24
- if (!gate.allowed) {
25
- throw new MartinToolError("policy_blocked", gate.summary, {
26
- category: "policy_blocked",
27
- suggestion: gate.nextAction,
28
- retryable: false,
29
- details: {
30
- missingSteps: gate.missingSteps,
31
- nextAction: gate.nextAction
19
+ repoRoot: workingDirectory,
20
+ runsRoot
21
+ };
22
+ if (executionMode.liveMode) {
23
+ if (engine === "codex") {
24
+ const engineAvailability = resolveCliCommandAvailability("codex");
25
+ if (!engineAvailability.available) {
26
+ throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
27
+ category: "environment",
28
+ suggestion: "Install the requested CLI or set MARTIN_LIVE=false for a no-spend proof run.",
29
+ retryable: false
30
+ });
32
31
  }
33
- });
34
- }
35
- if (executionMode.liveMode && !engineAvailability.available) {
36
- throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
37
- category: "environment",
38
- suggestion: "Install the requested CLI or set MARTIN_LIVE=false for stub execution.",
39
- retryable: false
40
- });
32
+ const codexProbe = probeCodexLaunch({
33
+ workingDirectory,
34
+ availability: engineAvailability
35
+ });
36
+ if (!codexProbe.ok) {
37
+ throw new MartinToolError("engine_unavailable", codexProbe.summary, {
38
+ category: "environment",
39
+ suggestion: "Run martin_doctor or martin_preflight with engine='codex' before retrying live governed work.",
40
+ retryable: false
41
+ });
42
+ }
43
+ }
44
+ else {
45
+ const engineAvailability = getEngineAvailability(engine);
46
+ if (!engineAvailability.available) {
47
+ throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
48
+ category: "environment",
49
+ suggestion: "Install the requested CLI or set MARTIN_LIVE=false for a no-spend proof run.",
50
+ retryable: false
51
+ });
52
+ }
53
+ }
41
54
  }
42
- const adapter = process.env.MARTIN_LIVE === "false"
43
- ? createStubDirectProviderAdapter({ label: "Stub adapter (MARTIN_LIVE=false)", providerId: "stub", model: "stub" })
55
+ const adapter = !executionMode.liveMode
56
+ ? createVerifierOnlyAdapter({
57
+ workingDirectory,
58
+ label: "Proof mode adapter (MARTIN_LIVE=false)"
59
+ })
44
60
  : engine === "codex"
45
61
  ? createCodexCliAdapter({ workingDirectory, ...(model ? { model } : {}) })
46
- : createClaudeCliAdapter({ workingDirectory, ...(model ? { model } : {}) });
62
+ : engine === "gemini"
63
+ ? createGeminiCliAdapter({ workingDirectory, ...(model ? { model } : {}) })
64
+ : createClaudeCliAdapter({ workingDirectory, ...(model ? { model } : {}) });
47
65
  const partialBudget = {};
48
66
  if (input.maxUsd !== undefined) {
49
67
  partialBudget.maxUsd = input.maxUsd;
@@ -62,6 +80,7 @@ export async function runLoopTool(input) {
62
80
  workspaceId: input.workspaceId ?? "ws_mcp",
63
81
  projectId: input.projectId ?? "proj_mcp",
64
82
  store: createFileRunStore({ runsRoot }),
83
+ receiptScope,
65
84
  task: {
66
85
  title: input.objective.slice(0, 100),
67
86
  objective: input.objective,
@@ -106,6 +125,7 @@ export async function runLoopTool(input) {
106
125
  budget,
107
126
  inspection: {
108
127
  ...recordPaths,
128
+ receiptScope: result.loop.receiptScope ?? receiptScope,
109
129
  loop: buildLoopPreview(result.loop),
110
130
  verification,
111
131
  artifacts
@@ -92,7 +92,7 @@ export function toToolFailure(error) {
92
92
  code: "engine_unavailable",
93
93
  category: "environment",
94
94
  message,
95
- suggestion: "Install the requested CLI or set MARTIN_LIVE=false for stub execution.",
95
+ suggestion: "Install the requested CLI or set MARTIN_LIVE=false for a no-spend proof run.",
96
96
  retryable: false
97
97
  };
98
98
  }
@@ -1,6 +1,6 @@
1
- import type { LoopArtifact, LoopBudget, LoopCost, LoopEvent, LoopTask } from "../vendor/contracts/index.js";
1
+ import type { LoopArtifact, LoopBudget, LoopCost, LoopEvent, LoopTask, ReceiptIntegritySummary, ReceiptScope } from "../vendor/contracts/index.js";
2
2
  import { type LedgerEvent, type LoopAttemptRecord, type LoopRunRecord } from "../vendor/core/index.js";
3
- export type MartinEngine = "claude" | "codex";
3
+ export type MartinEngine = "claude" | "codex" | "gemini";
4
4
  export interface InspectableLoopAttempt extends LoopAttemptRecord {
5
5
  attemptId?: string;
6
6
  summary?: string;
@@ -11,6 +11,8 @@ export interface InspectableLoopRecord extends Omit<LoopRunRecord, "attempts" |
11
11
  artifacts?: LoopArtifact[];
12
12
  events?: LoopEvent[];
13
13
  metadata?: Record<string, string>;
14
+ receiptIntegrity?: ReceiptIntegritySummary;
15
+ receiptScope?: ReceiptScope;
14
16
  }
15
17
  export interface LoopPreview {
16
18
  loopId: string;
@@ -90,7 +92,7 @@ export interface CliAvailability {
90
92
  }
91
93
  export interface ExecutionMode {
92
94
  liveMode: boolean;
93
- mode: "live" | "stub";
95
+ mode: "live" | "proof";
94
96
  detail: string;
95
97
  }
96
98
  export interface RunStoreInspection extends LoopCollectionSummary {
@@ -113,6 +115,7 @@ export declare function buildLoopPreview(loop: InspectableLoopRecord): LoopPrevi
113
115
  export declare function buildAttemptSummary(attempt: InspectableLoopAttempt, artifacts?: AttemptArtifactFiles): AttemptSummary;
114
116
  export declare function buildArtifactSummary(loop: InspectableLoopRecord): ArtifactSummary;
115
117
  export declare function buildVerificationSummary(loop: InspectableLoopRecord, ledgerEvents?: LedgerEvent[]): VerificationSummary;
118
+ export declare function resolveReceiptIntegrity(loop: InspectableLoopRecord): ReceiptIntegritySummary;
116
119
  export declare function buildEventSummaries(loop: InspectableLoopRecord, limit?: number): EventSummary[];
117
120
  export declare function buildLoopCollectionSummary(loops: Array<LoopRunRecord | InspectableLoopRecord>): LoopCollectionSummary;
118
121
  export declare function inspectRunsRoot(runsRoot?: string): Promise<RunStoreInspection>;
@@ -11,10 +11,10 @@ export function resolveExecutionMode() {
11
11
  const liveMode = process.env.MARTIN_LIVE !== "false";
12
12
  return {
13
13
  liveMode,
14
- mode: liveMode ? "live" : "stub",
14
+ mode: liveMode ? "live" : "proof",
15
15
  detail: liveMode
16
16
  ? "Live CLI execution is enabled."
17
- : "Stub mode is active because MARTIN_LIVE=false."
17
+ : "Proof mode is active because MARTIN_LIVE=false."
18
18
  };
19
19
  }
20
20
  export function detectCliAvailability(command) {
@@ -126,7 +126,11 @@ export function buildVerificationSummary(loop, ledgerEvents = []) {
126
126
  const verificationEvents = (loop.events ?? []).filter((event) => event.type === "verification.completed");
127
127
  const verificationLedgerEvents = ledgerEvents.filter((event) => event.kind === "verification.completed");
128
128
  const warnings = [];
129
+ const integrity = resolveReceiptIntegrity(loop);
129
130
  const ledgerWarnings = getLedgerWarnings(ledgerEvents);
131
+ if (integrity.state !== "verified") {
132
+ warnings.push(`Receipt integrity is ${integrity.state}; persisted verifier evidence is not trustworthy yet.`);
133
+ }
130
134
  warnings.push(...ledgerWarnings);
131
135
  if (verificationEvents.length === 0) {
132
136
  warnings.push(verificationLedgerEvents.length > 0
@@ -163,6 +167,12 @@ export function buildVerificationSummary(loop, ledgerEvents = []) {
163
167
  warnings
164
168
  };
165
169
  }
170
+ export function resolveReceiptIntegrity(loop) {
171
+ return (loop.receiptIntegrity ?? {
172
+ state: "unsigned",
173
+ reason: "Receipt integrity metadata was not available on the loop record."
174
+ });
175
+ }
166
176
  export function buildEventSummaries(loop, limit = 5) {
167
177
  return (loop.events ?? [])
168
178
  .slice(-limit)
@@ -287,9 +297,6 @@ export function buildSuggestedResourceUris(loopId) {
287
297
  `martin://runs/${loopId}/verification`,
288
298
  "martin://guides/mcp-usage",
289
299
  "martin://guides/agent-start",
290
- "martin://guides/command-map",
291
- "martin://guides/ide-onboarding",
292
- "martin://guides/operating-rules",
293
300
  "martin://guides/publish-readiness"
294
301
  ];
295
302
  }
@@ -80,6 +80,21 @@ export interface CodexCliAdapterOptions {
80
80
  extraArgs?: string[];
81
81
  spawnImpl?: SpawnLike;
82
82
  }
83
+ export interface GeminiCliAdapterOptions {
84
+ workingDirectory?: string;
85
+ timeoutMs?: number;
86
+ verifyTimeoutMs?: number;
87
+ label?: string;
88
+ /** Override the model passed via --model flag. Defaults to the Gemini `flash` alias. */
89
+ model?: string;
90
+ /** Approval mode for headless Gemini runs. Defaults to yolo for autonomous execution. */
91
+ approvalMode?: "default" | "auto_edit" | "yolo" | "plan";
92
+ /** Enable Gemini sandbox mode when the host is configured for it. Disabled by default. */
93
+ sandbox?: boolean;
94
+ /** Extra args appended after core args. */
95
+ extraArgs?: string[];
96
+ spawnImpl?: SpawnLike;
97
+ }
83
98
  export declare function createAgentCliAdapter(options: AgentCliAdapterOptions): MartinAdapter;
84
99
  /**
85
100
  * Spawns `claude --output-format json --print "<prompt>" --dangerously-skip-permissions [extraArgs]`.
@@ -102,3 +117,13 @@ export declare function createClaudeCliAdapter(options?: ClaudeCliAdapterOptions
102
117
  * npm install -g @openai/codex
103
118
  */
104
119
  export declare function createCodexCliAdapter(options?: CodexCliAdapterOptions): MartinAdapter;
120
+ /**
121
+ * Spawns `gemini --model <model> --prompt "" --approval-mode <mode> --output-format json [...]`.
122
+ *
123
+ * The prompt is delivered via stdin while forcing headless mode with `--prompt ""`,
124
+ * which keeps large MartinLoop prompts off the command line on Windows.
125
+ *
126
+ * Requires the Gemini CLI to be installed and authenticated:
127
+ * npm install -g @google/gemini-cli
128
+ */
129
+ export declare function createGeminiCliAdapter(options?: GeminiCliAdapterOptions): MartinAdapter;