@martinloop/mcp 0.1.4 → 0.2.5

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 (74) hide show
  1. package/README.md +138 -135
  2. package/dist/discovery-metadata.d.ts +16 -0
  3. package/dist/discovery-metadata.js +62 -0
  4. package/dist/discovery-support.d.ts +62 -0
  5. package/dist/discovery-support.js +224 -0
  6. package/dist/package-version.d.ts +1 -0
  7. package/dist/package-version.js +3 -0
  8. package/dist/prompts.d.ts +13 -0
  9. package/dist/prompts.js +455 -0
  10. package/dist/resources.d.ts +29 -0
  11. package/dist/resources.js +575 -0
  12. package/dist/server-validation.d.ts +2 -3
  13. package/dist/server-validation.js +295 -87
  14. package/dist/server.d.ts +76 -7
  15. package/dist/server.js +1135 -247
  16. package/dist/tools/doctor.js +14 -6
  17. package/dist/tools/get-attempt.d.ts +15 -0
  18. package/dist/tools/get-attempt.js +15 -0
  19. package/dist/tools/get-run.d.ts +24 -0
  20. package/dist/tools/get-run.js +23 -0
  21. package/dist/tools/get-status.d.ts +11 -0
  22. package/dist/tools/get-status.js +12 -2
  23. package/dist/tools/get-verification-results.d.ts +14 -0
  24. package/dist/tools/get-verification-results.js +14 -0
  25. package/dist/tools/inspect-loop.d.ts +9 -0
  26. package/dist/tools/inspect-loop.js +11 -2
  27. package/dist/tools/list-runs.d.ts +29 -0
  28. package/dist/tools/list-runs.js +24 -0
  29. package/dist/tools/preflight.js +7 -2
  30. package/dist/tools/run-dossier.d.ts +41 -0
  31. package/dist/tools/run-dossier.js +41 -0
  32. package/dist/tools/run-loop.d.ts +19 -0
  33. package/dist/tools/run-loop.js +41 -3
  34. package/dist/tools/run-store.d.ts +57 -3
  35. package/dist/tools/run-store.js +404 -53
  36. package/dist/tools/tool-errors.d.ts +37 -0
  37. package/dist/tools/tool-errors.js +170 -0
  38. package/dist/tools/tool-response.d.ts +16 -0
  39. package/dist/tools/tool-response.js +34 -0
  40. package/dist/tools/tool-support.d.ts +92 -2
  41. package/dist/tools/tool-support.js +358 -63
  42. package/dist/tools/triage-runs.d.ts +33 -0
  43. package/dist/tools/triage-runs.js +138 -0
  44. package/dist/vendor/adapters/claude-cli.js +0 -1
  45. package/dist/vendor/adapters/cli-bridge.js +0 -1
  46. package/dist/vendor/adapters/direct-provider.js +0 -1
  47. package/dist/vendor/adapters/index.js +0 -1
  48. package/dist/vendor/adapters/runtime-support.js +0 -1
  49. package/dist/vendor/adapters/stub-agent-cli.js +0 -1
  50. package/dist/vendor/adapters/stub-direct-provider.js +0 -1
  51. package/dist/vendor/adapters/verifier-only.js +0 -1
  52. package/dist/vendor/contracts/governance.js +0 -1
  53. package/dist/vendor/contracts/index.d.ts +2 -0
  54. package/dist/vendor/contracts/index.js +1 -1
  55. package/dist/vendor/contracts/operator.d.ts +19 -0
  56. package/dist/vendor/contracts/operator.js +11 -0
  57. package/dist/vendor/core/compiler.js +0 -1
  58. package/dist/vendor/core/context-integrity.js +0 -1
  59. package/dist/vendor/core/grounding.js +0 -1
  60. package/dist/vendor/core/index.js +1 -2
  61. package/dist/vendor/core/leash.js +19 -12
  62. package/dist/vendor/core/persistence/compiler.js +0 -1
  63. package/dist/vendor/core/persistence/index.js +0 -1
  64. package/dist/vendor/core/persistence/ledger.js +0 -1
  65. package/dist/vendor/core/persistence/runs-reader.js +0 -1
  66. package/dist/vendor/core/persistence/store.js +0 -1
  67. package/dist/vendor/core/policy.js +0 -1
  68. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  69. package/dist/vendor/core/red-blue/red-phase.js +135 -0
  70. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  71. package/dist/vendor/core/red-blue/risk-tiers.js +32 -0
  72. package/dist/vendor/core/rollback.js +2 -3
  73. package/package.json +10 -3
  74. package/server.json +2 -2
@@ -0,0 +1,224 @@
1
+ import { resolveRunsRoot } from "./vendor/core/index.js";
2
+ import { resolveSafeRepoRoot, resolveSafeRunsRootPath } from "./server-validation.js";
3
+ import { loadLoopRecordForStatus } from "./tools/run-store.js";
4
+ import { buildLoopPreview } from "./tools/tool-support.js";
5
+ import { invalidArgumentsError } from "./tools/tool-errors.js";
6
+ export function resolveMartinDiscoveryContext(input = {}) {
7
+ const fallbackRunsRoot = resolveRunsRoot(process.env);
8
+ return {
9
+ runsRoot: resolveSafeRunsRootPath(input.runsDir, fallbackRunsRoot),
10
+ workingDirectory: resolveSafeRepoRoot(input.workingDirectory),
11
+ ...(input.engine ? { engine: input.engine } : {})
12
+ };
13
+ }
14
+ export async function loadPersistedLoopRecord(input) {
15
+ const resolved = await loadLoopRecordForStatus({
16
+ loopId: input.loopId,
17
+ ...(input.runsDir ? { runsDir: input.runsDir } : {})
18
+ });
19
+ return {
20
+ source: resolved.source,
21
+ loop: resolved.loop
22
+ };
23
+ }
24
+ export function buildAttemptSnapshot(loop, requestedAttemptIndex, ledgerEvents = []) {
25
+ const attempt = requestedAttemptIndex === undefined
26
+ ? loop.attempts.at(-1)
27
+ : loop.attempts.find((candidate) => candidate.index === requestedAttemptIndex);
28
+ if (!attempt) {
29
+ throw invalidArgumentsError(requestedAttemptIndex === undefined
30
+ ? `Loop '${loop.loopId}' has no attempts yet.`
31
+ : `Attempt ${requestedAttemptIndex} was not found for loop '${loop.loopId}'.`, requestedAttemptIndex === undefined
32
+ ? "Run martin_run first or inspect a loop that has executed at least one attempt."
33
+ : "Choose an attemptIndex that exists in the loop record.");
34
+ }
35
+ const verification = buildVerificationSnapshotForAttempt(loop, attempt, ledgerEvents);
36
+ const warnings = collectVerificationSnapshots(loop, ledgerEvents).warnings;
37
+ return {
38
+ loopId: loop.loopId,
39
+ attemptIndex: attempt.index,
40
+ loop: buildPersistedLoopPreview(loop),
41
+ attempt,
42
+ ...(verification ? { verification } : {}),
43
+ warnings
44
+ };
45
+ }
46
+ export function buildVerificationHistorySnapshot(loop, ledgerEvents = []) {
47
+ const collected = collectVerificationSnapshots(loop, ledgerEvents);
48
+ const verificationHistory = collected.snapshots;
49
+ const latestVerification = selectLatestVerificationSnapshot(verificationHistory, collected.warnings);
50
+ return {
51
+ loopId: loop.loopId,
52
+ loop: buildPersistedLoopPreview(loop),
53
+ verificationCount: verificationHistory.length,
54
+ ...(latestVerification ? { latestVerification } : {}),
55
+ verificationHistory,
56
+ warnings: collected.warnings
57
+ };
58
+ }
59
+ export function buildPersistedLoopPreview(loop) {
60
+ return buildLoopPreview(loop);
61
+ }
62
+ export function parseAttemptIndex(value) {
63
+ if (!/^\d+$/u.test(value)) {
64
+ throw invalidArgumentsError("Invalid attemptIndex.", "attemptIndex must be a positive integer taken from the loop's attempts array.");
65
+ }
66
+ const parsed = Number.parseInt(value, 10);
67
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
68
+ throw invalidArgumentsError("Invalid attemptIndex.", "attemptIndex must be a positive integer taken from the loop's attempts array.");
69
+ }
70
+ return parsed;
71
+ }
72
+ export function toPrettyJson(value) {
73
+ return `${JSON.stringify(value, null, 2)}\n`;
74
+ }
75
+ function buildVerificationSnapshotForAttempt(loop, attempt, ledgerEvents = []) {
76
+ const matching = collectVerificationSnapshots(loop, ledgerEvents).snapshots.filter((verification) => verification.attemptIndex === attempt.index ||
77
+ (attempt.attemptId !== undefined && verification.attemptId === attempt.attemptId));
78
+ if (hasConflictingStatuses(matching)) {
79
+ return undefined;
80
+ }
81
+ return matching.at(-1);
82
+ }
83
+ function getVerificationEvents(loop) {
84
+ return (loop.events ?? []).filter((event) => event.type === "verification.completed");
85
+ }
86
+ function toVerificationSnapshot(loop, event) {
87
+ if (!isTrustedVerificationTimestamp(event.timestamp)) {
88
+ return undefined;
89
+ }
90
+ const payload = isRecord(event.payload) ? event.payload : undefined;
91
+ const attemptId = typeof payload?.["attemptId"] === "string" ? payload["attemptId"] : undefined;
92
+ const attemptIndex = typeof payload?.["attemptIndex"] === "number" && Number.isInteger(payload["attemptIndex"])
93
+ ? payload["attemptIndex"]
94
+ : undefined;
95
+ const matchedAttempt = attemptId
96
+ ? loop.attempts.find((attempt) => attempt.attemptId === attemptId)
97
+ : attemptIndex !== undefined
98
+ ? loop.attempts.find((attempt) => attempt.index === attemptIndex)
99
+ : undefined;
100
+ if (!matchedAttempt) {
101
+ return undefined;
102
+ }
103
+ return {
104
+ ...(matchedAttempt.attemptId ? { attemptId: matchedAttempt.attemptId } : {}),
105
+ attemptIndex: matchedAttempt.index,
106
+ timestamp: event.timestamp,
107
+ lifecycleState: event.lifecycleState,
108
+ ...(typeof payload?.["passed"] === "boolean" ? { passed: payload["passed"] } : {}),
109
+ ...(typeof payload?.["summary"] === "string" ? { summary: payload["summary"] } : {})
110
+ };
111
+ }
112
+ function collectVerificationSnapshots(loop, ledgerEvents) {
113
+ const seen = new Set();
114
+ const snapshots = [];
115
+ const warnings = getLedgerWarnings(ledgerEvents);
116
+ const futureEvidenceCount = [
117
+ ...getVerificationEvents(loop).map((event) => event.timestamp),
118
+ ...ledgerEvents.filter((candidate) => candidate.kind === "verification.completed").map((event) => event.timestamp)
119
+ ].filter(isFutureVerificationTimestamp).length;
120
+ if (futureEvidenceCount > 0) {
121
+ warnings.push(`Ignored ${futureEvidenceCount} future-dated verification evidence item(s) that cannot be trusted yet.`);
122
+ }
123
+ for (const event of getVerificationEvents(loop)) {
124
+ const snapshot = toVerificationSnapshot(loop, event);
125
+ if (!snapshot) {
126
+ continue;
127
+ }
128
+ const key = verificationSnapshotKey(snapshot);
129
+ seen.add(key);
130
+ snapshots.push(snapshot);
131
+ }
132
+ for (const event of ledgerEvents.filter((candidate) => candidate.kind === "verification.completed")) {
133
+ const snapshot = ledgerEventToVerificationSnapshot(loop, event);
134
+ if (!snapshot) {
135
+ continue;
136
+ }
137
+ const key = verificationSnapshotKey(snapshot);
138
+ if (seen.has(key)) {
139
+ continue;
140
+ }
141
+ seen.add(key);
142
+ snapshots.push(snapshot);
143
+ }
144
+ snapshots.sort((left, right) => new Date(left.timestamp).getTime() - new Date(right.timestamp).getTime());
145
+ if (hasConflictingStatusesForLatestAttempt(snapshots)) {
146
+ warnings.push("Verification evidence conflicts for the latest attempt; reporting status as unavailable.");
147
+ }
148
+ return { snapshots, warnings };
149
+ }
150
+ function ledgerEventToVerificationSnapshot(loop, event) {
151
+ if (!isTrustedVerificationTimestamp(event.timestamp)) {
152
+ return undefined;
153
+ }
154
+ const payload = isRecord(event.payload) ? event.payload : undefined;
155
+ const matchedAttempt = event.attemptIndex !== undefined
156
+ ? loop.attempts.find((attempt) => attempt.index === event.attemptIndex)
157
+ : undefined;
158
+ if (!matchedAttempt) {
159
+ return undefined;
160
+ }
161
+ return {
162
+ ...(matchedAttempt?.attemptId ? { attemptId: matchedAttempt.attemptId } : {}),
163
+ ...(event.attemptIndex !== undefined ? { attemptIndex: event.attemptIndex } : {}),
164
+ timestamp: event.timestamp,
165
+ lifecycleState: loop.lifecycleState,
166
+ ...(typeof payload?.["passed"] === "boolean" ? { passed: payload["passed"] } : {}),
167
+ ...(typeof payload?.["summary"] === "string" ? { summary: payload["summary"] } : {})
168
+ };
169
+ }
170
+ function verificationSnapshotKey(snapshot) {
171
+ return [
172
+ snapshot.attemptIndex !== undefined ? `attempt:${snapshot.attemptIndex}` : `attempt-id:${snapshot.attemptId ?? ""}`,
173
+ `timestamp:${snapshot.timestamp}`,
174
+ `summary:${snapshot.summary ?? ""}`,
175
+ `passed:${String(snapshot.passed)}`
176
+ ].join(":");
177
+ }
178
+ function selectLatestVerificationSnapshot(snapshots, warnings) {
179
+ if (warnings.includes("Verification evidence conflicts for the latest attempt; reporting status as unavailable.")) {
180
+ return undefined;
181
+ }
182
+ return snapshots.at(-1);
183
+ }
184
+ function hasConflictingStatusesForLatestAttempt(snapshots) {
185
+ const latest = snapshots.at(-1);
186
+ if (!latest) {
187
+ return false;
188
+ }
189
+ const latestAttemptSnapshots = snapshots.filter((candidate) => latest.attemptId
190
+ ? candidate.attemptId === latest.attemptId
191
+ : latest.attemptIndex !== undefined
192
+ ? candidate.attemptIndex === latest.attemptIndex
193
+ : false);
194
+ return hasConflictingStatuses(latestAttemptSnapshots);
195
+ }
196
+ function hasConflictingStatuses(snapshots) {
197
+ const statuses = new Set(snapshots
198
+ .map((snapshot) => snapshot.passed)
199
+ .filter((status) => typeof status === "boolean"));
200
+ return statuses.size > 1;
201
+ }
202
+ function isTrustedVerificationTimestamp(value) {
203
+ const timestamp = new Date(value).getTime();
204
+ if (!Number.isFinite(timestamp)) {
205
+ return false;
206
+ }
207
+ return timestamp <= Date.now() + 5 * 60_000;
208
+ }
209
+ function isFutureVerificationTimestamp(value) {
210
+ const timestamp = new Date(value).getTime();
211
+ if (!Number.isFinite(timestamp)) {
212
+ return false;
213
+ }
214
+ return timestamp > Date.now() + 5 * 60_000;
215
+ }
216
+ function getLedgerWarnings(ledgerEvents) {
217
+ const diagnostics = ledgerEvents;
218
+ return Array.isArray(diagnostics.warnings)
219
+ ? diagnostics.warnings.filter((warning) => typeof warning === "string")
220
+ : [];
221
+ }
222
+ function isRecord(value) {
223
+ return typeof value === "object" && value !== null;
224
+ }
@@ -0,0 +1 @@
1
+ export declare const MARTIN_MCP_PACKAGE_VERSION = "0.2.5";
@@ -0,0 +1,3 @@
1
+ // Keep this aligned with packages/mcp/package.json during version bumps so the
2
+ // runtime does not depend on package.json being present in every hosted bundle.
3
+ export const MARTIN_MCP_PACKAGE_VERSION = "0.2.5";
@@ -0,0 +1,13 @@
1
+ import type { GetPromptResult, Prompt } from "@modelcontextprotocol/sdk/types.js";
2
+ export declare const MARTIN_PROMPTS: Prompt[];
3
+ export interface MartinGetPromptInput {
4
+ name: string;
5
+ arguments?: Record<string, string>;
6
+ runsDir?: string;
7
+ workingDirectory?: string;
8
+ engine?: "claude" | "codex";
9
+ }
10
+ export declare function listMartinPrompts(): {
11
+ prompts: Prompt[];
12
+ };
13
+ export declare function getMartinPrompt(input: MartinGetPromptInput): Promise<GetPromptResult>;