@martinloop/mcp 0.5.0 → 0.5.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 (42) hide show
  1. package/dist/resources.js +15 -3
  2. package/dist/server.js +40 -8
  3. package/dist/tools/doctor.js +5 -4
  4. package/dist/tools/get-status.d.ts +2 -0
  5. package/dist/tools/get-status.js +1 -0
  6. package/dist/tools/preflight.js +1 -1
  7. package/dist/tools/run-loop.d.ts +6 -1
  8. package/dist/tools/run-loop.js +22 -7
  9. package/dist/tools/tool-errors.js +1 -1
  10. package/dist/tools/tool-response.js +9 -2
  11. package/dist/tools/tool-support.d.ts +3 -2
  12. package/dist/tools/tool-support.js +5 -3
  13. package/dist/vendor/adapters/claude-cli.d.ts +2 -2
  14. package/dist/vendor/adapters/claude-cli.js +59 -34
  15. package/dist/vendor/adapters/index.d.ts +1 -1
  16. package/dist/vendor/adapters/index.js +1 -1
  17. package/dist/vendor/adapters/openai-compatible.d.ts +2 -3
  18. package/dist/vendor/adapters/openai-compatible.js +33 -7
  19. package/dist/vendor/adapters/verifier-only.d.ts +9 -0
  20. package/dist/vendor/adapters/verifier-only.js +68 -0
  21. package/dist/vendor/contracts/index.d.ts +16 -6
  22. package/dist/vendor/contracts/index.js +1 -1
  23. package/dist/vendor/contracts/verified-handoff.d.ts +6 -0
  24. package/dist/vendor/contracts/verified-handoff.js +5 -0
  25. package/dist/vendor/core/grounding.d.ts +2 -1
  26. package/dist/vendor/core/grounding.js +40 -5
  27. package/dist/vendor/core/index.d.ts +4 -6
  28. package/dist/vendor/core/index.js +25 -19
  29. package/dist/vendor/core/persistence/runs-reader.d.ts +2 -0
  30. package/dist/vendor/core/policy.js +6 -6
  31. package/dist/vendor/core/red-blue/red-phase.d.ts +2 -1
  32. package/dist/vendor/core/red-blue/red-phase.js +3 -3
  33. package/dist/vendor/core/red-blue/risk-tiers.d.ts +2 -4
  34. package/dist/vendor/core/red-blue/risk-tiers.js +1 -3
  35. package/dist/vendor/core/routing.d.ts +0 -42
  36. package/dist/vendor/core/routing.js +4 -154
  37. package/dist/vendor/core/verified-handoff.d.ts +6 -1
  38. package/dist/vendor/core/verified-handoff.js +28 -0
  39. package/package.json +6 -2
  40. package/server.json +2 -2
  41. package/dist/vendor/adapters/stub-direct-provider.d.ts +0 -8
  42. package/dist/vendor/adapters/stub-direct-provider.js +0 -12
package/dist/resources.js CHANGED
@@ -37,6 +37,16 @@ export const MARTIN_STATIC_RESOURCE_URIS = {
37
37
  modeStatus: "martin://agent/mode-status",
38
38
  memorySummary: "martin://agent/memory-summary"
39
39
  };
40
+ function formatRecordedCost(usd, provenance) {
41
+ if (provenance === "unavailable")
42
+ return "unavailable";
43
+ if (provenance === "actual")
44
+ return `$${usd.toFixed(2)} provider-settled actual`;
45
+ if (provenance === "calculated") {
46
+ return `$${usd.toFixed(2)} calculated from observed usage`;
47
+ }
48
+ return `$${usd.toFixed(2)} estimated`;
49
+ }
40
50
  export const MARTIN_RESOURCE_TEMPLATES = [
41
51
  {
42
52
  name: "martin_run_record",
@@ -376,7 +386,7 @@ async function loadLatestRunForCompactResource(runsRoot) {
376
386
  empty: true,
377
387
  warnings: [
378
388
  "No Martin run records were found yet.",
379
- "Run `npx martin-loop demo`, then run `npx martin-loop run ... --proof --verify <command>` for a no-spend local proof pass."
389
+ "Run `npx martin-loop demo`, then use `--proof --verify <command>` for explicitly non-governed verification-only evidence."
380
390
  ]
381
391
  };
382
392
  }
@@ -404,6 +414,7 @@ async function buildLatestSummaryResource(runsRoot) {
404
414
  budget: loop.budget,
405
415
  cost: {
406
416
  actualUsd: loop.cost.actualUsd,
417
+ provenance: loop.cost.provenance ?? "unavailable",
407
418
  avoidedUsdEstimate: loop.cost.avoidedUsd ?? 0,
408
419
  tokensIn: loop.cost.tokensIn,
409
420
  tokensOut: loop.cost.tokensOut,
@@ -439,6 +450,7 @@ async function buildLatestBudgetStatusResource(runsRoot) {
439
450
  budget: loop.budget,
440
451
  cost: {
441
452
  actualUsd: loop.cost.actualUsd,
453
+ provenance: loop.cost.provenance ?? "unavailable",
442
454
  avoidedUsdEstimate: loop.cost.avoidedUsd ?? 0,
443
455
  tokensIn: loop.cost.tokensIn,
444
456
  tokensOut: loop.cost.tokensOut,
@@ -575,7 +587,7 @@ async function buildLatestProofCardResource(runsRoot) {
575
587
  `Run: \`${loop.loopId}\``,
576
588
  `Status: ${loop.status} / ${loop.lifecycleState}`,
577
589
  `Attempts: ${loop.attempts.length}`,
578
- `Spend: $${loop.cost.actualUsd.toFixed(2)} actual, $${(loop.cost.avoidedUsd ?? 0).toFixed(2)} avoided estimate`,
590
+ `Spend: ${formatRecordedCost(loop.cost.actualUsd, loop.cost.provenance ?? "unavailable")}, $${(loop.cost.avoidedUsd ?? 0).toFixed(2)} avoided estimate`,
579
591
  `Tokens: ${loop.cost.tokensIn} in / ${loop.cost.tokensOut} out`,
580
592
  `Verifier: ${verifierStatus}`,
581
593
  "",
@@ -599,7 +611,7 @@ function compactEmptyState(kind, runsRoot, warnings) {
599
611
  status: "empty",
600
612
  runsRoot,
601
613
  summary: "No Martin run records are available yet.",
602
- nextStep: "Run `npx martin-loop doctor`, create the demo workspace with `npx martin-loop demo`, then run `npx martin-loop run ... --proof --verify <command>`.",
614
+ nextStep: "Run `npx martin-loop doctor`, create the demo workspace, then use `--proof --verify <command>` only for non-governed verification evidence.",
603
615
  warnings
604
616
  };
605
617
  }
package/dist/server.js CHANGED
@@ -55,6 +55,15 @@ const stringArraySchema = {
55
55
  type: "array",
56
56
  items: { type: "string" }
57
57
  };
58
+ function describeCostProvenance(provenance) {
59
+ if (provenance === "actual")
60
+ return "provider-settled actual";
61
+ if (provenance === "calculated")
62
+ return "calculated from observed usage";
63
+ if (provenance === "estimated")
64
+ return "estimated";
65
+ return "unavailable";
66
+ }
58
67
  const loopPreviewSchema = {
59
68
  type: "object",
60
69
  additionalProperties: true,
@@ -68,6 +77,10 @@ const loopPreviewSchema = {
68
77
  updatedAt: { type: "string" },
69
78
  attempts: { type: "integer" },
70
79
  costUsd: { type: "number" },
80
+ costProvenance: {
81
+ type: "string",
82
+ enum: ["actual", "calculated", "estimated", "unavailable"]
83
+ },
71
84
  avoidedUsd: { type: "number" },
72
85
  pressure: { type: "string" },
73
86
  shouldStop: { type: "boolean" },
@@ -87,6 +100,7 @@ const loopPreviewSchema = {
87
100
  "lifecycleState",
88
101
  "attempts",
89
102
  "costUsd",
103
+ "costProvenance",
90
104
  "avoidedUsd",
91
105
  "pressure",
92
106
  "shouldStop",
@@ -113,9 +127,13 @@ const costSchema = {
113
127
  actualUsd: { type: "number" },
114
128
  avoidedUsd: { type: "number" },
115
129
  tokensIn: { type: "integer" },
116
- tokensOut: { type: "integer" }
130
+ tokensOut: { type: "integer" },
131
+ provenance: {
132
+ type: "string",
133
+ enum: ["actual", "calculated", "estimated", "unavailable"]
134
+ }
117
135
  },
118
- required: ["actualUsd", "avoidedUsd", "tokensIn", "tokensOut"]
136
+ required: ["actualUsd", "avoidedUsd", "tokensIn", "tokensOut", "provenance"]
119
137
  };
120
138
  const verificationSchema = {
121
139
  type: "object",
@@ -248,6 +266,10 @@ const runOutputSchema = {
248
266
  reason: { type: "string" },
249
267
  attempts: { type: "integer" },
250
268
  costUsd: { type: "number" },
269
+ costProvenance: {
270
+ type: "string",
271
+ enum: ["actual", "calculated", "estimated", "unavailable"]
272
+ },
251
273
  verificationPassed: { type: "boolean" },
252
274
  loopId: { type: "string" },
253
275
  pressure: { type: "string" },
@@ -279,6 +301,7 @@ const runOutputSchema = {
279
301
  "reason",
280
302
  "attempts",
281
303
  "costUsd",
304
+ "costProvenance",
282
305
  "verificationPassed",
283
306
  "loopId",
284
307
  "pressure",
@@ -346,6 +369,10 @@ const statusOutputSchema = {
346
369
  lifecycleState: { type: "string" },
347
370
  attempts: { type: "integer" },
348
371
  costUsd: { type: "number" },
372
+ costProvenance: {
373
+ type: "string",
374
+ enum: ["actual", "calculated", "estimated", "unavailable"]
375
+ },
349
376
  avoidedUsd: { type: "number" },
350
377
  pressure: { type: "string" },
351
378
  shouldStop: { type: "boolean" },
@@ -369,6 +396,7 @@ const statusOutputSchema = {
369
396
  "lifecycleState",
370
397
  "attempts",
371
398
  "costUsd",
399
+ "costProvenance",
372
400
  "avoidedUsd",
373
401
  "pressure",
374
402
  "shouldStop",
@@ -1503,17 +1531,22 @@ export function createMartinMcpServer(serverInfo) {
1503
1531
  });
1504
1532
  }
1505
1533
  const output = await runLoopTool(input);
1506
- return createToolSuccessResult(output, `Run ${output.loopId} is ${output.status}/${output.lifecycleState} after ${output.attempts} attempt(s); spend ${output.costUsd.toFixed(2)} USD.`);
1534
+ return createToolSuccessResult(output, [
1535
+ `Run ${output.loopId} is ${output.status}/${output.lifecycleState}`,
1536
+ `after ${output.attempts} attempt(s); recorded cost ${output.costUsd.toFixed(2)} USD (${describeCostProvenance(output.costProvenance)}).`,
1537
+ `Execution mode: ${output.executionMode}; governance claim eligible:`,
1538
+ `${output.governanceClaimEligible ? "yes" : "no"}.`
1539
+ ].join(" "));
1507
1540
  }
1508
1541
  if (name === "martin_inspect") {
1509
1542
  const input = validateToolInput("martin_inspect", args);
1510
1543
  const output = await inspectLoopTool(input);
1511
- return createToolSuccessResult(output, `Inspected ${output.loopCount} run(s) from ${output.source}; total actual spend ${output.portfolio.totalActualUsd.toFixed(2)} USD.`);
1544
+ return createToolSuccessResult(output, `Inspected ${output.loopCount} run(s) from ${output.source}; total recorded cost ${output.portfolio.totalActualUsd.toFixed(2)} USD.`);
1512
1545
  }
1513
1546
  if (name === "martin_status") {
1514
1547
  const input = validateToolInput("martin_status", args);
1515
1548
  const output = await getStatusTool(input);
1516
- return createToolSuccessResult(output, `Loop ${output.loopId} is ${output.status}/${output.lifecycleState}; pressure is ${output.pressure} with ${output.remainingBudgetUsd.toFixed(2)} USD remaining.`);
1549
+ return createToolSuccessResult(output, `Loop ${output.loopId} is ${output.status}/${output.lifecycleState}; recorded cost is ${output.costUsd.toFixed(2)} USD (${describeCostProvenance(output.costProvenance)}), and pressure is ${output.pressure} with ${output.remainingBudgetUsd.toFixed(2)} USD remaining.`);
1517
1550
  }
1518
1551
  if (name === "martin_doctor") {
1519
1552
  const input = validateToolInput("martin_doctor", args);
@@ -1671,8 +1704,7 @@ export function createMartinMcpServer(serverInfo) {
1671
1704
  compressed: route.compressed,
1672
1705
  ...(route.compressionSummary ? { compressionSummary: route.compressionSummary } : {}),
1673
1706
  recommendedBudgetUsd,
1674
- recommendedModelTier: route.recommendedModelTier,
1675
- estimatedSavingVsSonnetUsd: route.estimatedSavingVsSonnetUsd
1707
+ modelAuthority: "agent_or_provider_default"
1676
1708
  };
1677
1709
  await recordMcpWorkflowStep({
1678
1710
  runsRoot,
@@ -1687,7 +1719,7 @@ export function createMartinMcpServer(serverInfo) {
1687
1719
  runsRoot
1688
1720
  }
1689
1721
  }).catch(() => { });
1690
- return createToolSuccessResult(output, `Estimate: ${route.selectedMode} route (${route.recommendedModelTier}), ~$${route.expectedCostUsd.toFixed(2)} expected cost, ${route.expectedPreworkBurnPct}% pre-work burn. Recommended budget: $${recommendedBudgetUsd.toFixed(2)}.`);
1722
+ return createToolSuccessResult(output, `Estimate: ${route.selectedMode} route, ~$${route.expectedCostUsd.toFixed(2)} expected cost, ${route.expectedPreworkBurnPct}% pre-work burn. Model authority remains with the agent/provider. Recommended budget: $${recommendedBudgetUsd.toFixed(2)}.`);
1691
1723
  }
1692
1724
  return createToolErrorResult(toToolFailure(new Error(`Unknown tool: ${name}`)));
1693
1725
  }
@@ -115,10 +115,11 @@ export async function martinDoctorTool(input) {
115
115
  ...(gemini.resolvedPath ? { resolvedPath: gemini.resolvedPath } : {})
116
116
  },
117
117
  openai: {
118
- available: Boolean(process.env.MARTIN_OPENAI_API_KEY),
119
- detail: process.env.MARTIN_OPENAI_API_KEY
120
- ? `OpenAI-compatible endpoint configured (model: ${process.env.MARTIN_OPENAI_MODEL ?? "gpt-4.1-mini"})`
121
- : "Set MARTIN_OPENAI_API_KEY and optionally MARTIN_OPENAI_BASE_URL + MARTIN_OPENAI_MODEL to enable."
118
+ available: Boolean(process.env.MARTIN_OPENAI_MODEL &&
119
+ (process.env.MARTIN_OPENAI_API_KEY || process.env.MARTIN_OPENAI_BASE_URL)),
120
+ detail: process.env.MARTIN_OPENAI_MODEL
121
+ ? `OpenAI-compatible model configured: ${process.env.MARTIN_OPENAI_MODEL}.`
122
+ : "MODEL_CONFIGURATION_REQUIRED: set MARTIN_OPENAI_MODEL and the endpoint/auth required by your provider."
122
123
  }
123
124
  },
124
125
  ...(input.engine ? { requestedEngine: input.engine } : {}),
@@ -1,3 +1,4 @@
1
+ import type { CostProvenance } from "../vendor/contracts/index.js";
1
2
  import { type LoopPreview } from "./tool-support.js";
2
3
  export interface GetStatusInput {
3
4
  /** JSON-serialized LoopRecord. */
@@ -18,6 +19,7 @@ export interface GetStatusOutput {
18
19
  lifecycleState: string;
19
20
  attempts: number;
20
21
  costUsd: number;
22
+ costProvenance: CostProvenance;
21
23
  avoidedUsd: number;
22
24
  pressure: string;
23
25
  shouldStop: boolean;
@@ -31,6 +31,7 @@ export async function getStatusTool(input) {
31
31
  lifecycleState: loop.lifecycleState,
32
32
  attempts: loop.attempts.length,
33
33
  costUsd: loop.cost.actualUsd,
34
+ costProvenance: loop.cost.provenance ?? "unavailable",
34
35
  avoidedUsd: loop.cost.avoidedUsd ?? 0,
35
36
  pressure: costState.pressure,
36
37
  shouldStop: costState.shouldStop,
@@ -36,7 +36,7 @@ export async function martinPreflightTool(input) {
36
36
  maxTokens: input.maxTokens
37
37
  });
38
38
  if (!executionMode.liveMode) {
39
- warnings.push("Proof mode is active; preflight only proves configuration shape, not live CLI readiness.");
39
+ warnings.push("Verification-only mode is active; it cannot emit governed VERIFIED or prove live CLI readiness.");
40
40
  }
41
41
  else if (!engineAvailability.available) {
42
42
  warnings.push(`Requested engine '${engine}' is not available on PATH.`);
@@ -1,5 +1,6 @@
1
+ import { type SpawnLike } from "../vendor/adapters/index.js";
1
2
  import { type RunStore } from "../vendor/core/index.js";
2
- import type { LoopBudget, ReceiptScope } from "../vendor/contracts/index.js";
3
+ import type { CostProvenance, ExecutionMode, LoopBudget, ReceiptScope } from "../vendor/contracts/index.js";
3
4
  import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, type MartinEngine } from "./tool-support.js";
4
5
  export interface RunLoopInput {
5
6
  objective: string;
@@ -22,7 +23,10 @@ export interface RunLoopOutput {
22
23
  reason: string;
23
24
  attempts: number;
24
25
  costUsd: number;
26
+ costProvenance: CostProvenance;
25
27
  verificationPassed: boolean;
28
+ executionMode: ExecutionMode;
29
+ governanceClaimEligible: boolean;
26
30
  loopId: string;
27
31
  pressure: string;
28
32
  shouldStop: boolean;
@@ -43,5 +47,6 @@ export interface RunLoopOutput {
43
47
  artifacts: ReturnType<typeof buildArtifactSummary>;
44
48
  };
45
49
  }
50
+ export declare function __setProofModeVerifierSpawnImplForTests(spawnImpl?: SpawnLike): void;
46
51
  export declare function __setRunStoreOverrideForTests(store?: RunStore): void;
47
52
  export declare function runLoopTool(input: RunLoopInput): Promise<RunLoopOutput>;
@@ -1,10 +1,14 @@
1
- import { createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, createStubDirectProviderAdapter, probeCodexLaunch, resolveCliCommandAvailability } from "../vendor/adapters/index.js";
1
+ import { createClaudeCliAdapter, createCodexCliAdapter, createGeminiCliAdapter, createVerifierOnlyAdapter, probeCodexLaunch, resolveCliCommandAvailability } from "../vendor/adapters/index.js";
2
2
  import { createFileRunStore, evaluateCostGovernor, resolveRunsRoot, runMartin } from "../vendor/core/index.js";
3
3
  import { normalizeSafePathPatterns, resolveSafeRepoRoot } from "../server-validation.js";
4
4
  import { MartinToolError } from "./tool-errors.js";
5
5
  import { buildArtifactSummary, buildVerificationSummary, buildLoopPreview, buildRunRecordPaths, getEngineAvailability, resolveExecutionMode } from "./tool-support.js";
6
6
  import { normalizeLoopBudget } from "./workflow-governance.js";
7
+ let proofModeVerifierSpawnImpl;
7
8
  let runStoreOverrideForTests;
9
+ export function __setProofModeVerifierSpawnImplForTests(spawnImpl) {
10
+ proofModeVerifierSpawnImpl = spawnImpl;
11
+ }
8
12
  export function __setRunStoreOverrideForTests(store) {
9
13
  runStoreOverrideForTests = store;
10
14
  }
@@ -15,6 +19,9 @@ export async function runLoopTool(input) {
15
19
  const allowedPaths = normalizeSafePathPatterns(input.allowedPaths, "allowedPaths");
16
20
  const deniedPaths = normalizeSafePathPatterns(input.deniedPaths, "deniedPaths");
17
21
  const executionMode = resolveExecutionMode();
22
+ const evidenceExecutionMode = executionMode.liveMode
23
+ ? "governed"
24
+ : "verification_only";
18
25
  const workspaceRoot = resolveSafeRepoRoot();
19
26
  const runsRoot = resolveRunsRoot(process.env);
20
27
  const receiptScope = {
@@ -30,7 +37,7 @@ export async function runLoopTool(input) {
30
37
  if (!engineAvailability.available) {
31
38
  throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
32
39
  category: "environment",
33
- suggestion: "Install the requested CLI or set MARTIN_LIVE=false for a no-spend proof run.",
40
+ suggestion: "Install the requested CLI. MARTIN_LIVE=false is verification-only and cannot emit governed VERIFIED.",
34
41
  retryable: false
35
42
  });
36
43
  }
@@ -52,17 +59,18 @@ export async function runLoopTool(input) {
52
59
  if (!engineAvailability.available) {
53
60
  throw new MartinToolError("engine_unavailable", `Engine '${engine}' is not available on PATH.`, {
54
61
  category: "environment",
55
- suggestion: "Install the requested CLI or set MARTIN_LIVE=false for a no-spend proof run.",
62
+ suggestion: "Install the requested CLI. MARTIN_LIVE=false is verification-only and cannot emit governed VERIFIED.",
56
63
  retryable: false
57
64
  });
58
65
  }
59
66
  }
60
67
  }
61
68
  const adapter = !executionMode.liveMode
62
- ? createStubDirectProviderAdapter({
63
- label: "Proof mode stub adapter (MARTIN_LIVE=false)",
64
- providerId: "stub",
65
- model: "stub"
69
+ ? createVerifierOnlyAdapter({
70
+ workingDirectory,
71
+ label: "Verification-only adapter (MARTIN_LIVE=false)",
72
+ ...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
73
+ ...(proofModeVerifierSpawnImpl ? { spawnImpl: proofModeVerifierSpawnImpl } : {})
66
74
  })
67
75
  : engine === "codex"
68
76
  ? createCodexCliAdapter({
@@ -110,6 +118,10 @@ export async function runLoopTool(input) {
110
118
  ...(deniedPaths ? { deniedPaths } : {})
111
119
  },
112
120
  budget,
121
+ metadata: {
122
+ executionMode: evidenceExecutionMode,
123
+ governanceClaimEligible: evidenceExecutionMode === "governed" ? "true" : "false"
124
+ },
113
125
  adapter
114
126
  });
115
127
  const lastAttempt = result.loop.attempts.at(-1);
@@ -133,7 +145,10 @@ export async function runLoopTool(input) {
133
145
  reason: result.decision.reason,
134
146
  attempts: result.loop.attempts.length,
135
147
  costUsd: result.loop.cost.actualUsd,
148
+ costProvenance: result.loop.cost.provenance ?? "unavailable",
136
149
  verificationPassed,
150
+ executionMode: evidenceExecutionMode,
151
+ governanceClaimEligible: evidenceExecutionMode === "governed",
137
152
  loopId: result.loop.loopId,
138
153
  pressure: costState.pressure,
139
154
  shouldStop: costState.shouldStop,
@@ -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 a no-spend proof run.",
95
+ suggestion: "Install the requested CLI. MARTIN_LIVE=false is verification-only and cannot emit governed VERIFIED.",
96
96
  retryable: false
97
97
  };
98
98
  }
@@ -4,13 +4,20 @@ function createJsonBlock(output) {
4
4
  text: JSON.stringify(output, null, 2)
5
5
  };
6
6
  }
7
+ function resolveHumanArtifact(output, summary) {
8
+ const rendered = "rendered" in output ? output.rendered : undefined;
9
+ return typeof rendered === "string" && rendered.trim().length > 0 ? rendered : summary;
10
+ }
7
11
  export function createToolSuccessResult(output, summary) {
8
12
  return {
9
- content: [createJsonBlock(output), { type: "text", text: summary }],
13
+ content: [
14
+ { type: "text", text: resolveHumanArtifact(output, summary) },
15
+ createJsonBlock(output)
16
+ ],
10
17
  structuredContent: output,
11
18
  _meta: {
12
19
  "martinloop/summary": summary,
13
- "martinloop/contentVersion": "2026-05-15"
20
+ "martinloop/contentVersion": "2026-08-18"
14
21
  }
15
22
  };
16
23
  }
@@ -1,4 +1,4 @@
1
- import type { LoopArtifact, LoopBudget, LoopCost, LoopEvent, LoopTask, ReceiptIntegritySummary, ReceiptScope, TerminationEnvelopeV1 } from "../vendor/contracts/index.js";
1
+ import type { CostProvenance, LoopArtifact, LoopBudget, LoopCost, LoopEvent, LoopTask, ReceiptIntegritySummary, ReceiptScope, TerminationEnvelopeV1 } from "../vendor/contracts/index.js";
2
2
  import { type LedgerEvent, type LoopAttemptRecord, type LoopRunRecord } from "../vendor/core/index.js";
3
3
  export declare const MARTIN_ENGINE_VALUES: readonly ["claude", "codex", "gemini", "openai"];
4
4
  export type MartinEngine = (typeof MARTIN_ENGINE_VALUES)[number];
@@ -27,6 +27,7 @@ export interface LoopPreview {
27
27
  updatedAt?: string;
28
28
  attempts: number;
29
29
  costUsd: number;
30
+ costProvenance: CostProvenance;
30
31
  avoidedUsd: number;
31
32
  pressure: string;
32
33
  shouldStop: boolean;
@@ -128,7 +129,7 @@ export declare function inspectRunsRoot(runsRoot?: string): Promise<RunStoreInsp
128
129
  export declare function buildRunRecordPaths(runsRoot: string, loopId: string): CanonicalRunPaths;
129
130
  export declare function buildAttemptArtifactDirectory(runsRoot: string, loopId: string, attemptIndex: number): string;
130
131
  export declare function buildAttemptArtifactsReference(runsRoot: string, loopId: string, attemptIndex: number): Promise<AttemptArtifactFiles>;
131
- export declare function buildCostSnapshot(cost: Pick<LoopCost, "actualUsd" | "tokensIn" | "tokensOut"> & {
132
+ export declare function buildCostSnapshot(cost: Pick<LoopCost, "actualUsd" | "tokensIn" | "tokensOut" | "provenance"> & {
132
133
  avoidedUsd?: number;
133
134
  }): LoopCost;
134
135
  export declare function buildBudgetSnapshot(budget: LoopBudget): LoopBudget;
@@ -15,7 +15,7 @@ export function resolveExecutionMode() {
15
15
  mode: liveMode ? "live" : "proof",
16
16
  detail: liveMode
17
17
  ? "Live CLI execution is enabled."
18
- : "Proof mode is active because MARTIN_LIVE=false."
18
+ : "Verification-only mode is active because MARTIN_LIVE=false; governed VERIFIED is unavailable."
19
19
  };
20
20
  }
21
21
  export function detectCliAvailability(command) {
@@ -175,7 +175,7 @@ function suggestInstallCommand(command) {
175
175
  export function getEngineAvailability(engine) {
176
176
  return detectCliAvailability(engine);
177
177
  }
178
- export function createSkippedCliAvailability(command, detail = "Proof mode skipped live CLI availability detection.") {
178
+ export function createSkippedCliAvailability(command, detail = "Verification-only mode skipped live CLI availability detection; governed VERIFIED is unavailable.") {
179
179
  return {
180
180
  command,
181
181
  available: false,
@@ -208,6 +208,7 @@ export function buildLoopPreview(loop) {
208
208
  ...(loop.updatedAt ? { updatedAt: loop.updatedAt } : {}),
209
209
  attempts: loop.attempts.length,
210
210
  costUsd: loop.cost.actualUsd,
211
+ costProvenance: loop.cost.provenance ?? "unavailable",
211
212
  avoidedUsd: loop.cost.avoidedUsd ?? 0,
212
213
  pressure: costState.pressure,
213
214
  shouldStop: costState.shouldStop,
@@ -411,7 +412,8 @@ export function buildCostSnapshot(cost) {
411
412
  actualUsd: cost.actualUsd,
412
413
  avoidedUsd: cost.avoidedUsd ?? 0,
413
414
  tokensIn: cost.tokensIn,
414
- tokensOut: cost.tokensOut
415
+ tokensOut: cost.tokensOut,
416
+ provenance: cost.provenance ?? "unavailable"
415
417
  };
416
418
  }
417
419
  export function buildBudgetSnapshot(budget) {
@@ -96,7 +96,7 @@ export interface GeminiCliAdapterOptions {
96
96
  timeoutMs?: number;
97
97
  verifyTimeoutMs?: number;
98
98
  label?: string;
99
- /** Override the model passed via --model flag. Defaults to the Gemini `flash` alias. */
99
+ /** Explicit model override passed via --model. Omitted to preserve Gemini Auto. */
100
100
  model?: string;
101
101
  /** Approval mode for headless Gemini runs. Defaults to yolo for autonomous execution. */
102
102
  approvalMode?: "default" | "auto_edit" | "yolo" | "plan";
@@ -135,7 +135,7 @@ export declare function createClaudeCliAdapter(options?: ClaudeCliAdapterOptions
135
135
  */
136
136
  export declare function createCodexCliAdapter(options?: CodexCliAdapterOptions): MartinAdapter;
137
137
  /**
138
- * Spawns `gemini --model <model> --prompt "" --approval-mode <mode> --output-format json [...]`.
138
+ * Spawns `gemini [--model <explicit-model>] --prompt "" --approval-mode <mode> --output-format json [...]`.
139
139
  *
140
140
  * The prompt is delivered via stdin while forcing headless mode with `--prompt ""`,
141
141
  * which keeps large MartinLoop prompts off the command line on Windows.