@martinloop/mcp 0.5.0 → 0.5.3

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 (51) hide show
  1. package/README.md +52 -14
  2. package/dist/resources.js +15 -3
  3. package/dist/server.js +40 -8
  4. package/dist/tools/doctor.js +5 -4
  5. package/dist/tools/get-status.d.ts +2 -0
  6. package/dist/tools/get-status.js +1 -0
  7. package/dist/tools/preflight.js +1 -1
  8. package/dist/tools/run-loop.d.ts +6 -1
  9. package/dist/tools/run-loop.js +22 -7
  10. package/dist/tools/tool-errors.js +1 -1
  11. package/dist/tools/tool-response.js +9 -2
  12. package/dist/tools/tool-support.d.ts +3 -2
  13. package/dist/tools/tool-support.js +5 -3
  14. package/dist/vendor/adapters/claude-cli.d.ts +2 -2
  15. package/dist/vendor/adapters/claude-cli.js +59 -34
  16. package/dist/vendor/adapters/codex-capabilities.d.ts +60 -0
  17. package/dist/vendor/adapters/codex-capabilities.js +237 -0
  18. package/dist/vendor/adapters/codex-cli.d.ts +22 -0
  19. package/dist/vendor/adapters/codex-cli.js +62 -0
  20. package/dist/vendor/adapters/codex-host.d.ts +104 -0
  21. package/dist/vendor/adapters/codex-host.js +577 -0
  22. package/dist/vendor/adapters/codex-launcher.d.ts +7 -135
  23. package/dist/vendor/adapters/codex-launcher.js +17 -766
  24. package/dist/vendor/adapters/index.d.ts +4 -3
  25. package/dist/vendor/adapters/index.js +4 -3
  26. package/dist/vendor/adapters/openai-compatible.d.ts +2 -3
  27. package/dist/vendor/adapters/openai-compatible.js +33 -7
  28. package/dist/vendor/adapters/verifier-only.d.ts +9 -0
  29. package/dist/vendor/adapters/verifier-only.js +68 -0
  30. package/dist/vendor/contracts/index.d.ts +16 -6
  31. package/dist/vendor/contracts/index.js +1 -1
  32. package/dist/vendor/contracts/verified-handoff.d.ts +6 -0
  33. package/dist/vendor/contracts/verified-handoff.js +5 -0
  34. package/dist/vendor/core/grounding.d.ts +2 -1
  35. package/dist/vendor/core/grounding.js +40 -5
  36. package/dist/vendor/core/index.d.ts +4 -6
  37. package/dist/vendor/core/index.js +25 -19
  38. package/dist/vendor/core/persistence/runs-reader.d.ts +2 -0
  39. package/dist/vendor/core/policy.js +6 -6
  40. package/dist/vendor/core/red-blue/red-phase.d.ts +2 -1
  41. package/dist/vendor/core/red-blue/red-phase.js +3 -3
  42. package/dist/vendor/core/red-blue/risk-tiers.d.ts +2 -4
  43. package/dist/vendor/core/red-blue/risk-tiers.js +1 -3
  44. package/dist/vendor/core/routing.d.ts +0 -42
  45. package/dist/vendor/core/routing.js +4 -154
  46. package/dist/vendor/core/verified-handoff.d.ts +6 -1
  47. package/dist/vendor/core/verified-handoff.js +28 -0
  48. package/package.json +6 -2
  49. package/server.json +2 -2
  50. package/dist/vendor/adapters/stub-direct-provider.d.ts +0 -8
  51. package/dist/vendor/adapters/stub-direct-provider.js +0 -12
@@ -20,6 +20,7 @@ export interface MockModelClient {
20
20
  findings: RedFinding[];
21
21
  tokensUsed: number;
22
22
  costUsd: number;
23
+ model?: string;
23
24
  }>;
24
25
  }
25
26
  export interface RunRedPhaseOptions {
@@ -47,7 +48,7 @@ export interface PatchInput {
47
48
  *
48
49
  * - baseline: programmatic probes only, no model call
49
50
  * - high_risk: paranoid programmatic scan, no model call
50
- * - release_critical: paranoid scan + one Haiku model call
51
+ * - release_critical: paranoid scan + one configured model-client call
51
52
  */
52
53
  export declare function runRedPhase(patch: PatchInput, tier: RiskTier, blueBudgetUsd: number, options?: RunRedPhaseOptions): Promise<RedFindings>;
53
54
  /**
@@ -1,4 +1,4 @@
1
- import { PROBE_COUNTS, RED_PHASE_MODEL, resolveRedBudgetPolicy } from "./risk-tiers.js";
1
+ import { PROBE_COUNTS, resolveRedBudgetPolicy } from "./risk-tiers.js";
2
2
  // ─── Programmatic probes ──────────────────────────────────────────────────────
3
3
  function runProgrammaticProbes(patch, paranoid) {
4
4
  const findings = [];
@@ -62,7 +62,7 @@ function runProgrammaticProbes(patch, paranoid) {
62
62
  *
63
63
  * - baseline: programmatic probes only, no model call
64
64
  * - high_risk: paranoid programmatic scan, no model call
65
- * - release_critical: paranoid scan + one Haiku model call
65
+ * - release_critical: paranoid scan + one configured model-client call
66
66
  */
67
67
  export async function runRedPhase(patch, tier, blueBudgetUsd, options = {}) {
68
68
  const policy = resolveRedBudgetPolicy(tier, blueBudgetUsd);
@@ -77,7 +77,7 @@ export async function runRedPhase(patch, tier, blueBudgetUsd, options = {}) {
77
77
  const result = await options.modelClient.complete(prompt);
78
78
  findings = [...findings, ...result.findings];
79
79
  modelCallMade = true;
80
- modelUsed = RED_PHASE_MODEL;
80
+ modelUsed = result.model;
81
81
  budgetUsedUsd += result.costUsd;
82
82
  }
83
83
  const result = {
@@ -4,7 +4,7 @@ export interface RedBudgetPolicy {
4
4
  blueBudgetUsd: number;
5
5
  /** Cap on Red phase spend: 30% / 100% / 150% of Blue */
6
6
  redBudgetCapUsd: number;
7
- /** Only release_critical permits a Haiku model call */
7
+ /** Only release_critical permits an optional configured model-client call. */
8
8
  modelCallAllowed: boolean;
9
9
  }
10
10
  /**
@@ -15,8 +15,6 @@ export declare function resolveRedBudgetPolicy(tier: RiskTier, blueBudgetUsd: nu
15
15
  * Probe counts per tier.
16
16
  * baseline = standard 6-probe sweep
17
17
  * high_risk = paranoid 12-probe sweep
18
- * release_critical = paranoid 12-probe sweep + model
18
+ * release_critical = paranoid 12-probe sweep + configured model client
19
19
  */
20
20
  export declare const PROBE_COUNTS: Record<RiskTier, number>;
21
- /** The only model ever permitted in the Red phase. */
22
- export declare const RED_PHASE_MODEL: "claude-haiku-4-5-20251001";
@@ -21,12 +21,10 @@ export function resolveRedBudgetPolicy(tier, blueBudgetUsd) {
21
21
  * Probe counts per tier.
22
22
  * baseline = standard 6-probe sweep
23
23
  * high_risk = paranoid 12-probe sweep
24
- * release_critical = paranoid 12-probe sweep + model
24
+ * release_critical = paranoid 12-probe sweep + configured model client
25
25
  */
26
26
  export const PROBE_COUNTS = {
27
27
  baseline: 6,
28
28
  high_risk: 12,
29
29
  release_critical: 12
30
30
  };
31
- /** The only model ever permitted in the Red phase. */
32
- export const RED_PHASE_MODEL = "claude-haiku-4-5-20251001";
@@ -15,14 +15,6 @@ export interface RouteDecision {
15
15
  blockedSteps: string[];
16
16
  compressed: boolean;
17
17
  compressionSummary?: string;
18
- /**
19
- * Recommended model tier based on task complexity and route.
20
- * Follows cost-aware routing: direct → haiku/sonnet, manager → sonnet,
21
- * consensus → opus. Never use a more expensive model than the task needs.
22
- */
23
- recommendedModelTier: "haiku" | "sonnet" | "opus";
24
- /** Estimated cost saving vs always using sonnet */
25
- estimatedSavingVsSonnetUsd: number;
26
18
  }
27
19
  export interface RouteClassificationInput {
28
20
  objective: string;
@@ -37,40 +29,6 @@ export interface RouteClassificationInput {
37
29
  /** Historical success rate for similar tasks with direct execution (0-1). */
38
30
  historicalDirectSuccessRate?: number;
39
31
  }
40
- /**
41
- * Resolve a tier to a concrete model ID for a specific engine.
42
- * Allows downstream adapters to switch models automatically.
43
- */
44
- export declare function resolveModelForTier(tier: RouteDecision["recommendedModelTier"], engine: "claude" | "codex" | "gemini" | "openai" | string): string;
45
- /**
46
- * Available engine registry — describes what's installed and its capability.
47
- * Populated by doctor/preflight from PATH and env var detection.
48
- */
49
- export interface AvailableEngine {
50
- id: string;
51
- available: boolean;
52
- /** Cost tier: cheap < mid < expensive */
53
- costTier: "cheap" | "mid" | "expensive";
54
- /** Capability tier: determines which task types this engine can handle */
55
- capabilityTier: "haiku" | "sonnet" | "opus";
56
- }
57
- /**
58
- * Select the best available engine for a given tier requirement.
59
- *
60
- * When multiple engines are available, picks the cheapest that meets
61
- * the capability requirement. Falls back gracefully:
62
- * - If required tier unavailable, upgrades to next tier of same engine
63
- * - If only one engine family available, uses its best equivalent
64
- *
65
- * @param requiredTier - Minimum capability tier needed
66
- * @param availableEngines - What's installed and authenticated
67
- * @returns Best engine ID and concrete model to use
68
- */
69
- export declare function selectBestEngine(requiredTier: RouteDecision["recommendedModelTier"], availableEngines: AvailableEngine[]): {
70
- engineId: string;
71
- model: string;
72
- reasoning: string;
73
- };
74
32
  export declare function classifyRoute(input: RouteClassificationInput): RouteDecision;
75
33
  /**
76
34
  * Checks whether a run's Pre Work Burn exceeds the routing policy limits.
@@ -84,144 +84,6 @@ function calculateDirectConfidence(signals, input) {
84
84
  // ---------------------------------------------------------------------------
85
85
  // Public API
86
86
  // ---------------------------------------------------------------------------
87
- // ---------------------------------------------------------------------------
88
- // Cost-aware model tier routing
89
- //
90
- // Engine-agnostic tier system — each engine maps tiers to real model IDs.
91
- // Never pay for reasoning/orchestration on simple tasks.
92
- //
93
- // Tier → concrete model per engine:
94
- // haiku → claude-haiku-4-5, gpt-4o-mini, gemini-2.5-flash, deepseek-chat, qwen3-8b, llama-3.1-8b
95
- // sonnet → claude-sonnet-4-6, gpt-4o, gemini-2.5-pro, deepseek-r1, qwen3-32b, llama-3.3-70b
96
- // opus → claude-opus-4-6, o3, gemini-2.5-ultra, deepseek-r1-671b, qwen3-235b, llama-3.1-405b
97
- //
98
- // Pricing reference (USD per 1M tokens, approx 2026):
99
- // haiku: $0.80 input / $4.00 output (~1x baseline)
100
- // sonnet: $3.00 input / $15.00 output (~4x haiku)
101
- // opus: $15.00 input / $75.00 output (~19x haiku)
102
- // ---------------------------------------------------------------------------
103
- const SONNET_AVG_COST_PER_USD_BUDGET = 0.35; // sonnet uses ~35% of budget on average
104
- const HAIKU_AVG_COST_PER_USD_BUDGET = 0.10; // haiku uses ~10% — ~3.5x cheaper
105
- function selectModelTier(mode) {
106
- // Route already encodes complexity. Direct = passed the simplicity check → haiku.
107
- if (mode === "direct")
108
- return "haiku";
109
- if (mode === "manager")
110
- return "sonnet";
111
- return "opus";
112
- }
113
- /**
114
- * Resolve a tier to a concrete model ID for a specific engine.
115
- * Allows downstream adapters to switch models automatically.
116
- */
117
- export function resolveModelForTier(tier, engine) {
118
- const matrix = {
119
- claude: {
120
- haiku: "claude-haiku-4-5-20251001",
121
- sonnet: "claude-sonnet-4-6",
122
- opus: "claude-opus-4-6"
123
- },
124
- codex: {
125
- haiku: "gpt-4o-mini",
126
- sonnet: "gpt-4.1",
127
- opus: "o3"
128
- },
129
- gemini: {
130
- haiku: "gemini-2.5-flash",
131
- sonnet: "gemini-2.5-pro",
132
- opus: "gemini-2.5-ultra"
133
- },
134
- openai: {
135
- haiku: "gpt-4o-mini",
136
- sonnet: "gpt-4o",
137
- opus: "o3"
138
- },
139
- deepseek: {
140
- haiku: "deepseek-chat",
141
- sonnet: "deepseek-r1",
142
- opus: "deepseek-r1"
143
- },
144
- qwen: {
145
- haiku: "qwen/qwen3-8b",
146
- sonnet: "qwen/qwen3-32b",
147
- opus: "qwen/qwen3-235b-a22b"
148
- },
149
- llama: {
150
- haiku: "meta-llama/llama-3.1-8b-instruct",
151
- sonnet: "meta-llama/llama-3.3-70b-instruct",
152
- opus: "meta-llama/llama-3.1-405b-instruct"
153
- }
154
- };
155
- const engineKey = engine.toLowerCase().split("-")[0] ?? engine;
156
- const claudeMatrix = matrix.claude;
157
- return matrix[engineKey]?.[tier] ?? claudeMatrix?.[tier] ?? "claude-sonnet-4-6";
158
- }
159
- function estimateSavingVsSonnet(tier, budgetUsd) {
160
- if (tier === "sonnet")
161
- return 0;
162
- if (tier === "haiku")
163
- return Math.round((SONNET_AVG_COST_PER_USD_BUDGET - HAIKU_AVG_COST_PER_USD_BUDGET) * budgetUsd * 100) / 100;
164
- // opus costs more than sonnet — negative saving
165
- return Math.round(-1 * SONNET_AVG_COST_PER_USD_BUDGET * budgetUsd * 0.5 * 100) / 100;
166
- }
167
- /**
168
- * Select the best available engine for a given tier requirement.
169
- *
170
- * When multiple engines are available, picks the cheapest that meets
171
- * the capability requirement. Falls back gracefully:
172
- * - If required tier unavailable, upgrades to next tier of same engine
173
- * - If only one engine family available, uses its best equivalent
174
- *
175
- * @param requiredTier - Minimum capability tier needed
176
- * @param availableEngines - What's installed and authenticated
177
- * @returns Best engine ID and concrete model to use
178
- */
179
- export function selectBestEngine(requiredTier, availableEngines) {
180
- const available = availableEngines.filter((e) => e.available);
181
- if (available.length === 0) {
182
- // Nothing detected — default to claude (user must install)
183
- return {
184
- engineId: "claude",
185
- model: resolveModelForTier(requiredTier, "claude"),
186
- reasoning: "No engines detected. Defaulting to claude. Run martin doctor for setup instructions."
187
- };
188
- }
189
- const tierRank = {
190
- haiku: 0, sonnet: 1, opus: 2
191
- };
192
- const requiredRank = tierRank[requiredTier];
193
- // Find the cheapest engine that meets or exceeds the required tier
194
- const eligible = available
195
- .filter((e) => tierRank[e.capabilityTier] >= requiredRank)
196
- .sort((a, b) => {
197
- // Sort by cost (cheap first), then by exact tier match
198
- const costOrder = { cheap: 0, mid: 1, expensive: 2 };
199
- if (costOrder[a.costTier] !== costOrder[b.costTier]) {
200
- return costOrder[a.costTier] - costOrder[b.costTier];
201
- }
202
- // Prefer exact tier match over overqualified
203
- const aExact = a.capabilityTier === requiredTier ? 0 : 1;
204
- const bExact = b.capabilityTier === requiredTier ? 0 : 1;
205
- return aExact - bExact;
206
- });
207
- if (eligible.length > 0) {
208
- const best = eligible[0];
209
- const model = resolveModelForTier(requiredTier, best.id);
210
- return {
211
- engineId: best.id,
212
- model,
213
- reasoning: `Selected ${best.id} (${model}) as cheapest available engine for ${requiredTier}-tier task.`
214
- };
215
- }
216
- // All available engines are below required tier — upgrade to best available
217
- const best = available.sort((a, b) => tierRank[b.capabilityTier] - tierRank[a.capabilityTier])[0];
218
- const model = resolveModelForTier(best.capabilityTier, best.id);
219
- return {
220
- engineId: best.id,
221
- model,
222
- reasoning: `No ${requiredTier}-tier engine available. Upgrading to ${best.id} (${model}) — best available.`
223
- };
224
- }
225
87
  export function classifyRoute(input) {
226
88
  const policy = { ...DEFAULT_ROUTING_POLICY, ...input.policy };
227
89
  const signals = extractComplexitySignals(input);
@@ -230,7 +92,6 @@ export function classifyRoute(input) {
230
92
  const blockedSteps = [];
231
93
  // Policy override: forced mode
232
94
  if (policy.mode === "direct") {
233
- const tier = selectModelTier("direct");
234
95
  return {
235
96
  selectedMode: "direct",
236
97
  confidence: 1,
@@ -239,13 +100,10 @@ export function classifyRoute(input) {
239
100
  reason: ["Policy forces direct execution mode."],
240
101
  blockedSteps: ["manager", "planner", "router", "consensus"],
241
102
  compressed: true,
242
- compressionSummary: "Policy: direct mode — skipping all orchestration.",
243
- recommendedModelTier: tier,
244
- estimatedSavingVsSonnetUsd: estimateSavingVsSonnet(tier, input.budgetUsd)
103
+ compressionSummary: "Policy: direct mode — skipping all orchestration."
245
104
  };
246
105
  }
247
106
  if (policy.mode === "consensus") {
248
- const tier = selectModelTier("consensus");
249
107
  return {
250
108
  selectedMode: "consensus",
251
109
  confidence: 0.5,
@@ -253,9 +111,7 @@ export function classifyRoute(input) {
253
111
  expectedPreworkBurnPct: 35,
254
112
  reason: ["Policy forces consensus mode."],
255
113
  blockedSteps: [],
256
- compressed: false,
257
- recommendedModelTier: tier,
258
- estimatedSavingVsSonnetUsd: estimateSavingVsSonnet(tier, input.budgetUsd)
114
+ compressed: false
259
115
  };
260
116
  }
261
117
  // Adaptive routing
@@ -268,7 +124,6 @@ export function classifyRoute(input) {
268
124
  if (signals.scopeRestricted)
269
125
  reason.push("Path scope is restricted.");
270
126
  blockedSteps.push("manager", "consensus");
271
- const tier = selectModelTier("direct");
272
127
  return {
273
128
  selectedMode: "direct",
274
129
  confidence: directConfidence,
@@ -277,9 +132,7 @@ export function classifyRoute(input) {
277
132
  reason,
278
133
  blockedSteps,
279
134
  compressed: true,
280
- compressionSummary: `Compressed from manager→planner→worker to direct worker. Estimated savings: ${String(Math.round(input.budgetUsd * 0.3 * 100) / 100)} USD.`,
281
- recommendedModelTier: tier,
282
- estimatedSavingVsSonnetUsd: estimateSavingVsSonnet(tier, input.budgetUsd)
135
+ compressionSummary: `Compressed from manager→planner→worker to direct worker. Estimated savings: ${String(Math.round(input.budgetUsd * 0.3 * 100) / 100)} USD.`
283
136
  };
284
137
  }
285
138
  // Complex task — needs orchestration
@@ -293,7 +146,6 @@ export function classifyRoute(input) {
293
146
  reason.push(`Touches ${String(signals.fileCount)}+ files.`);
294
147
  const needsConsensus = signals.hasSecurityKeywords && signals.hasMigrationKeywords;
295
148
  const mode = needsConsensus ? "consensus" : "manager";
296
- const tier = selectModelTier(mode);
297
149
  return {
298
150
  selectedMode: mode,
299
151
  confidence: directConfidence,
@@ -301,9 +153,7 @@ export function classifyRoute(input) {
301
153
  expectedPreworkBurnPct: needsConsensus ? 40 : 25,
302
154
  reason,
303
155
  blockedSteps: needsConsensus ? [] : ["consensus"],
304
- compressed: false,
305
- recommendedModelTier: tier,
306
- estimatedSavingVsSonnetUsd: estimateSavingVsSonnet(tier, input.budgetUsd)
156
+ compressed: false
307
157
  };
308
158
  }
309
159
  function estimateDirectCost(budgetUsd) {
@@ -1,4 +1,4 @@
1
- import type { LoopRecord, ReceiptIntegritySummary, TestIntegrityStatus, TestIntegrityVerdict, VerifiedHandoffOutcome, VerifiedHandoffRecoveryV1, VerifiedHandoffRequirementV1, VerifiedHandoffScopeV1, VerifiedHandoffTestIntegrityV1, VerifiedHandoffV1 } from "../contracts/index.js";
1
+ import type { ExecutionMode, LoopRecord, ReceiptIntegritySummary, TestIntegrityStatus, TestIntegrityVerdict, VerifiedHandoffOutcome, VerifiedHandoffRecoveryV1, VerifiedHandoffRequirementV1, VerifiedHandoffScopeV1, VerifiedHandoffTestIntegrityV1, VerifiedHandoffV1 } from "../contracts/index.js";
2
2
  export declare function toTestIntegrityVerdict(status: TestIntegrityStatus): TestIntegrityVerdict;
3
3
  export interface VerifierExecutionBinding {
4
4
  runId: string;
@@ -30,8 +30,13 @@ export declare function resolveVerifiedHandoffOutcome(input: {
30
30
  changedFileCount?: number;
31
31
  definitionOfDonePreSatisfied?: boolean;
32
32
  evidenceContradicted?: boolean;
33
+ governanceClaimEligible?: boolean;
33
34
  unresolvedWorkCount: number;
34
35
  }): VerifiedHandoffOutcome;
36
+ export declare function deriveVerifiedHandoffExecutionBoundary(loop: LoopRecord): {
37
+ executionMode: ExecutionMode;
38
+ governanceClaimEligible: boolean;
39
+ };
35
40
  export interface BuildVerifiedHandoffInput {
36
41
  loop: LoopRecord;
37
42
  generatedAt?: string;
@@ -57,6 +57,7 @@ export function resolveVerifiedHandoffOutcome(input) {
57
57
  return "STOPPED";
58
58
  }
59
59
  if (input.executionStatus === "failed" ||
60
+ input.governanceClaimEligible === false ||
60
61
  input.evidenceContradicted === true ||
61
62
  (input.mutationRequired === true &&
62
63
  (input.changedFileCount ?? 0) === 0 &&
@@ -80,6 +81,29 @@ export function resolveVerifiedHandoffOutcome(input) {
80
81
  }
81
82
  return "NEEDS_REVIEW";
82
83
  }
84
+ const SIMULATED_ADAPTER_PATTERN = /(?:^|:)(?:stub|fixture|synthetic)(?::|$)/u;
85
+ function isExecutionMode(value) {
86
+ return value === "governed" || value === "verification_only" || value === "simulated";
87
+ }
88
+ export function deriveVerifiedHandoffExecutionBoundary(loop) {
89
+ const adapterIds = loop.attempts.map((attempt) => attempt.adapterId);
90
+ const hasSimulatedAdapter = adapterIds.some((adapterId) => adapterId === "direct:proof:no-mutation" ||
91
+ SIMULATED_ADAPTER_PATTERN.test(adapterId));
92
+ const hasVerifierOnlyAdapter = adapterIds.some((adapterId) => adapterId === "direct:verifier:verify-only");
93
+ const persistedMode = isExecutionMode(loop.metadata["executionMode"])
94
+ ? loop.metadata["executionMode"]
95
+ : undefined;
96
+ const groundingEvidenceAvailable = loop.metadata["groundingEvidenceStatus"] !== "unavailable";
97
+ const executionMode = hasSimulatedAdapter
98
+ ? "simulated"
99
+ : hasVerifierOnlyAdapter
100
+ ? "verification_only"
101
+ : persistedMode ?? (adapterIds.length > 0 ? "governed" : "simulated");
102
+ return {
103
+ executionMode,
104
+ governanceClaimEligible: executionMode === "governed" && adapterIds.length > 0 && groundingEvidenceAvailable,
105
+ };
106
+ }
83
107
  function toEvidenceStatus(status) {
84
108
  switch (status) {
85
109
  case "passed":
@@ -132,6 +156,7 @@ export function buildVerifiedHandoff(input) {
132
156
  summary: input.testIntegrity?.summary ??
133
157
  "Test integrity was not evaluated for this run.",
134
158
  };
159
+ const executionBoundary = deriveVerifiedHandoffExecutionBoundary(input.loop);
135
160
  const outcome = resolveVerifiedHandoffOutcome({
136
161
  lifecycleState: input.loop.lifecycleState,
137
162
  executionStatus: input.executionStatus,
@@ -143,6 +168,7 @@ export function buildVerifiedHandoff(input) {
143
168
  changedFileCount: scope.changedFiles.length,
144
169
  definitionOfDonePreSatisfied: input.definitionOfDonePreSatisfied,
145
170
  evidenceContradicted: input.evidenceContradicted ?? input.verification.status === "contradicted",
171
+ governanceClaimEligible: executionBoundary.governanceClaimEligible,
146
172
  unresolvedWorkCount: unresolvedWork.length,
147
173
  });
148
174
  const handoffId = `vh_${createHash("sha256")
@@ -163,6 +189,8 @@ export function buildVerifiedHandoff(input) {
163
189
  verificationPlan: input.loop.task.verificationPlan,
164
190
  },
165
191
  outcome,
192
+ executionMode: executionBoundary.executionMode,
193
+ governanceClaimEligible: executionBoundary.governanceClaimEligible,
166
194
  sourceStatus: {
167
195
  status: input.loop.status,
168
196
  lifecycleState: input.loop.lifecycleState,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@martinloop/mcp",
3
- "version": "0.5.0",
3
+ "version": "0.5.3",
4
4
  "mcpName": "io.github.Keesan12/martin-loop",
5
5
  "private": false,
6
6
  "type": "module",
@@ -59,7 +59,10 @@
59
59
  "test": "vitest run --maxWorkers=1",
60
60
  "lint": "tsc -p tsconfig.json --noEmit",
61
61
  "start": "node dist/server.js",
62
- "inspect:live": "node ./scripts/inspect-live.mjs"
62
+ "inspect:live": "node ./scripts/inspect-live.mjs",
63
+ "mcpb:build": "node ./mcpb/build-mcpb.mjs",
64
+ "mcpb:validate": "mcpb validate ./dist-mcpb/martinloop",
65
+ "mcpb:smoke": "node ./mcpb/smoke-mcpb.mjs"
63
66
  },
64
67
  "dependencies": {
65
68
  "@modelcontextprotocol/sdk": "^1.29.0",
@@ -71,6 +74,7 @@
71
74
  "ts-morph": "^21.0.0"
72
75
  },
73
76
  "devDependencies": {
77
+ "@anthropic-ai/mcpb": "2.1.2",
74
78
  "@martin/contracts": "workspace:*"
75
79
  }
76
80
  }
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.5.0",
10
+ "version": "0.5.3",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@martinloop/mcp",
15
- "version": "0.5.0",
15
+ "version": "0.5.3",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }
@@ -1,8 +0,0 @@
1
- import type { MartinAdapter, MartinAdapterRequest, MartinAdapterResult } from "../core/index.js";
2
- export interface StubDirectProviderAdapterOptions {
3
- providerId: string;
4
- model: string;
5
- label?: string;
6
- responder?: (request: MartinAdapterRequest) => Promise<MartinAdapterResult> | MartinAdapterResult;
7
- }
8
- export declare function createStubDirectProviderAdapter(options: StubDirectProviderAdapterOptions): MartinAdapter;
@@ -1,12 +0,0 @@
1
- import { createDirectProviderAdapter } from "./direct-provider.js";
2
- export function createStubDirectProviderAdapter(options) {
3
- return createDirectProviderAdapter({
4
- providerId: options.providerId,
5
- model: options.model,
6
- label: options.label ?? `Stub direct provider (${options.providerId}/${options.model})`,
7
- capabilities: {
8
- workspaceMutations: false
9
- },
10
- responder: options.responder
11
- });
12
- }