@martinloop/mcp 0.3.8 → 0.3.9

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 (103) hide show
  1. package/README.md +148 -105
  2. package/dist/resources.js +148 -148
  3. package/dist/server.js +0 -0
  4. package/dist/tools/preflight.d.ts +0 -2
  5. package/dist/tools/preflight.js +2 -42
  6. package/dist/vendor/adapters/openai-compatible.js +6 -6
  7. package/dist/vendor/contracts/index.d.ts +0 -2
  8. package/dist/vendor/contracts/index.js +0 -2
  9. package/dist/vendor/core/index.d.ts +0 -2
  10. package/dist/vendor/core/index.js +0 -2
  11. package/package.json +86 -83
  12. package/server.json +21 -21
  13. package/LICENSE +0 -216
  14. package/dist/vendor/adapters/adapters/src/claude-cli.d.ts +0 -146
  15. package/dist/vendor/adapters/adapters/src/claude-cli.js +0 -1100
  16. package/dist/vendor/adapters/adapters/src/cli-bridge.d.ts +0 -103
  17. package/dist/vendor/adapters/adapters/src/cli-bridge.js +0 -569
  18. package/dist/vendor/adapters/adapters/src/codex-launcher.d.ts +0 -78
  19. package/dist/vendor/adapters/adapters/src/codex-launcher.js +0 -663
  20. package/dist/vendor/adapters/adapters/src/direct-provider.d.ts +0 -10
  21. package/dist/vendor/adapters/adapters/src/direct-provider.js +0 -40
  22. package/dist/vendor/adapters/adapters/src/index.d.ts +0 -8
  23. package/dist/vendor/adapters/adapters/src/index.js +0 -8
  24. package/dist/vendor/adapters/adapters/src/openai-compatible.d.ts +0 -62
  25. package/dist/vendor/adapters/adapters/src/openai-compatible.js +0 -306
  26. package/dist/vendor/adapters/adapters/src/runtime-support.d.ts +0 -17
  27. package/dist/vendor/adapters/adapters/src/runtime-support.js +0 -59
  28. package/dist/vendor/adapters/adapters/src/stub-agent-cli.d.ts +0 -8
  29. package/dist/vendor/adapters/adapters/src/stub-agent-cli.js +0 -40
  30. package/dist/vendor/adapters/adapters/src/stub-direct-provider.d.ts +0 -8
  31. package/dist/vendor/adapters/adapters/src/stub-direct-provider.js +0 -12
  32. package/dist/vendor/adapters/adapters/src/verifier-only.d.ts +0 -9
  33. package/dist/vendor/adapters/adapters/src/verifier-only.js +0 -63
  34. package/dist/vendor/adapters/contracts/src/context-graph.d.ts +0 -48
  35. package/dist/vendor/adapters/contracts/src/context-graph.js +0 -26
  36. package/dist/vendor/adapters/contracts/src/execution-policy.d.ts +0 -78
  37. package/dist/vendor/adapters/contracts/src/execution-policy.js +0 -43
  38. package/dist/vendor/adapters/contracts/src/governance.d.ts +0 -24
  39. package/dist/vendor/adapters/contracts/src/governance.js +0 -11
  40. package/dist/vendor/adapters/contracts/src/identity.d.ts +0 -29
  41. package/dist/vendor/adapters/contracts/src/identity.js +0 -30
  42. package/dist/vendor/adapters/contracts/src/index.d.ts +0 -412
  43. package/dist/vendor/adapters/contracts/src/index.js +0 -225
  44. package/dist/vendor/adapters/contracts/src/operator.d.ts +0 -19
  45. package/dist/vendor/adapters/contracts/src/operator.js +0 -11
  46. package/dist/vendor/adapters/contracts/src/trajectory.d.ts +0 -26
  47. package/dist/vendor/adapters/contracts/src/trajectory.js +0 -27
  48. package/dist/vendor/adapters/core/src/compiler.d.ts +0 -52
  49. package/dist/vendor/adapters/core/src/compiler.js +0 -52
  50. package/dist/vendor/adapters/core/src/context-graph.d.ts +0 -3
  51. package/dist/vendor/adapters/core/src/context-graph.js +0 -371
  52. package/dist/vendor/adapters/core/src/context-integrity.d.ts +0 -26
  53. package/dist/vendor/adapters/core/src/context-integrity.js +0 -80
  54. package/dist/vendor/adapters/core/src/grounding.d.ts +0 -38
  55. package/dist/vendor/adapters/core/src/grounding.js +0 -273
  56. package/dist/vendor/adapters/core/src/identity.d.ts +0 -20
  57. package/dist/vendor/adapters/core/src/identity.js +0 -86
  58. package/dist/vendor/adapters/core/src/index.d.ts +0 -186
  59. package/dist/vendor/adapters/core/src/index.js +0 -1495
  60. package/dist/vendor/adapters/core/src/leash.d.ts +0 -48
  61. package/dist/vendor/adapters/core/src/leash.js +0 -497
  62. package/dist/vendor/adapters/core/src/persistence/compiler.d.ts +0 -18
  63. package/dist/vendor/adapters/core/src/persistence/compiler.js +0 -34
  64. package/dist/vendor/adapters/core/src/persistence/index.d.ts +0 -10
  65. package/dist/vendor/adapters/core/src/persistence/index.js +0 -5
  66. package/dist/vendor/adapters/core/src/persistence/integrity.d.ts +0 -39
  67. package/dist/vendor/adapters/core/src/persistence/integrity.js +0 -252
  68. package/dist/vendor/adapters/core/src/persistence/ledger.d.ts +0 -23
  69. package/dist/vendor/adapters/core/src/persistence/ledger.js +0 -9
  70. package/dist/vendor/adapters/core/src/persistence/memory-store.d.ts +0 -63
  71. package/dist/vendor/adapters/core/src/persistence/memory-store.js +0 -115
  72. package/dist/vendor/adapters/core/src/persistence/runs-reader.d.ts +0 -66
  73. package/dist/vendor/adapters/core/src/persistence/runs-reader.js +0 -140
  74. package/dist/vendor/adapters/core/src/persistence/store.d.ts +0 -89
  75. package/dist/vendor/adapters/core/src/persistence/store.js +0 -125
  76. package/dist/vendor/adapters/core/src/persistence/trace-store.d.ts +0 -85
  77. package/dist/vendor/adapters/core/src/persistence/trace-store.js +0 -125
  78. package/dist/vendor/adapters/core/src/policy-compiler.d.ts +0 -2
  79. package/dist/vendor/adapters/core/src/policy-compiler.js +0 -195
  80. package/dist/vendor/adapters/core/src/policy.d.ts +0 -156
  81. package/dist/vendor/adapters/core/src/policy.js +0 -704
  82. package/dist/vendor/adapters/core/src/repo-analyzer.d.ts +0 -53
  83. package/dist/vendor/adapters/core/src/repo-analyzer.js +0 -263
  84. package/dist/vendor/adapters/core/src/rollback.d.ts +0 -15
  85. package/dist/vendor/adapters/core/src/rollback.js +0 -253
  86. package/dist/vendor/adapters/core/src/routing.d.ts +0 -82
  87. package/dist/vendor/adapters/core/src/routing.js +0 -336
  88. package/dist/vendor/adapters/core/src/test-integrity.d.ts +0 -64
  89. package/dist/vendor/adapters/core/src/test-integrity.js +0 -197
  90. package/dist/vendor/adapters/core/src/trajectory.d.ts +0 -18
  91. package/dist/vendor/adapters/core/src/trajectory.js +0 -295
  92. package/dist/vendor/contracts/delivery.d.ts +0 -47
  93. package/dist/vendor/contracts/delivery.js +0 -18
  94. package/dist/vendor/core/delivery/index.d.ts +0 -6
  95. package/dist/vendor/core/delivery/index.js +0 -4
  96. package/dist/vendor/core/delivery/message-client.d.ts +0 -18
  97. package/dist/vendor/core/delivery/message-client.js +0 -38
  98. package/dist/vendor/core/delivery/message-ledger.d.ts +0 -35
  99. package/dist/vendor/core/delivery/message-ledger.js +0 -97
  100. package/dist/vendor/core/delivery/message-schema.d.ts +0 -16
  101. package/dist/vendor/core/delivery/message-schema.js +0 -111
  102. package/dist/vendor/core/delivery/update-check.d.ts +0 -19
  103. package/dist/vendor/core/delivery/update-check.js +0 -107
@@ -1,27 +0,0 @@
1
- export function cloneTrajectoryAssessment(assessment) {
2
- return {
3
- status: assessment.status,
4
- confidence: assessment.confidence,
5
- summary: assessment.summary,
6
- ...(assessment.recommendedIntervention
7
- ? { recommendedIntervention: assessment.recommendedIntervention }
8
- : {}),
9
- signals: assessment.signals.map((signal) => ({
10
- code: signal.code,
11
- severity: signal.severity,
12
- summary: signal.summary,
13
- attemptIndexes: [...signal.attemptIndexes],
14
- ...(signal.matchedTerms ? { matchedTerms: [...signal.matchedTerms] } : {})
15
- }))
16
- };
17
- }
18
- export function cloneCircuitBreakDecision(decision) {
19
- return {
20
- shouldStop: decision.shouldStop,
21
- reason: decision.reason,
22
- assessment: cloneTrajectoryAssessment(decision.assessment),
23
- ...(decision.recommendedIntervention
24
- ? { recommendedIntervention: decision.recommendedIntervention }
25
- : {})
26
- };
27
- }
@@ -1,52 +0,0 @@
1
- import type { LoopAttempt, LoopTask } from "../../../contracts/index.js";
2
- /**
3
- * The subset of MartinAdapterRequest fields needed by the prompt packet
4
- * compiler. Defined here (not in index.ts) to avoid circular imports:
5
- * index.ts → ./persistence/index.js → ./persistence/compiler.js → ./compiler.js (no cycle)
6
- */
7
- export interface CompilerAdapterRequest {
8
- loopId: string;
9
- attemptId: string;
10
- context: {
11
- taskTitle: string;
12
- objective: string;
13
- verificationPlan: string[];
14
- verificationStack?: LoopTask["verificationStack"];
15
- mutationMode?: LoopTask["mutationMode"];
16
- repoRoot?: string;
17
- allowedPaths?: string[];
18
- deniedPaths?: string[];
19
- acceptanceCriteria?: string[];
20
- focus: string;
21
- remainingBudgetUsd: number;
22
- remainingIterations: number;
23
- remainingTokens: number;
24
- };
25
- previousAttempts: LoopAttempt[];
26
- }
27
- export interface PromptPacket {
28
- loopId: string;
29
- attemptNumber: number;
30
- contract: {
31
- objective: string;
32
- verificationPlan: string[];
33
- mutationMode?: LoopTask["mutationMode"];
34
- allowedPaths?: string[];
35
- deniedPaths?: string[];
36
- acceptanceCriteria?: string[];
37
- };
38
- /** Prior failure/intervention pairs as "failureClass:intervention" strings. */
39
- priorFailurePatterns: string[];
40
- guidance: string;
41
- budgetEnvelope: {
42
- remainingBudgetUsd: number;
43
- remainingIterations: number;
44
- remainingTokens: number;
45
- };
46
- }
47
- /**
48
- * Compiles a deterministic PromptPacket from a MartinAdapterRequest.
49
- * This is the context compiler — takes structured request state and produces
50
- * a reconstructable packet (no chat history required).
51
- */
52
- export declare function compilePromptPacket(request: CompilerAdapterRequest): PromptPacket;
@@ -1,52 +0,0 @@
1
- import { redactSecretsFromText } from "./leash.js";
2
- /**
3
- * Compiles a deterministic PromptPacket from a MartinAdapterRequest.
4
- * This is the context compiler — takes structured request state and produces
5
- * a reconstructable packet (no chat history required).
6
- */
7
- export function compilePromptPacket(request) {
8
- const priorFailurePatterns = request.previousAttempts
9
- .filter((a) => a.failureClass && a.intervention)
10
- .map((a) => `${a.failureClass}:${a.intervention}`);
11
- const guidanceParts = request.context.mutationMode === "verify_only"
12
- ? [
13
- "Do not modify files.",
14
- "Run the verifier only and report whether it passed."
15
- ]
16
- : [
17
- "Only modify files directly required to satisfy the contract.",
18
- "Do not touch files outside the allowed paths."
19
- ];
20
- if (request.context.allowedPaths && request.context.allowedPaths.length > 0) {
21
- guidanceParts.push(`Allowed paths: ${request.context.allowedPaths.join(", ")}.`);
22
- }
23
- if (request.context.deniedPaths && request.context.deniedPaths.length > 0) {
24
- guidanceParts.push(`Denied paths (never touch): ${request.context.deniedPaths.join(", ")}.`);
25
- }
26
- if (priorFailurePatterns.length > 0) {
27
- guidanceParts.push(`Prior failure patterns: ${priorFailurePatterns.join(", ")}. Adjust strategy accordingly.`);
28
- }
29
- return {
30
- loopId: request.loopId,
31
- attemptNumber: request.previousAttempts.length + 1,
32
- contract: {
33
- objective: redactSecretsFromText(request.context.objective),
34
- verificationPlan: request.context.verificationPlan,
35
- ...(request.context.mutationMode ? { mutationMode: request.context.mutationMode } : {}),
36
- ...(request.context.allowedPaths ? { allowedPaths: request.context.allowedPaths } : {}),
37
- ...(request.context.deniedPaths ? { deniedPaths: request.context.deniedPaths } : {}),
38
- ...(request.context.acceptanceCriteria
39
- ? {
40
- acceptanceCriteria: request.context.acceptanceCriteria.map((criterion) => redactSecretsFromText(criterion))
41
- }
42
- : {})
43
- },
44
- priorFailurePatterns,
45
- guidance: guidanceParts.join(" "),
46
- budgetEnvelope: {
47
- remainingBudgetUsd: request.context.remainingBudgetUsd,
48
- remainingIterations: request.context.remainingIterations,
49
- remainingTokens: request.context.remainingTokens
50
- }
51
- };
52
- }
@@ -1,3 +0,0 @@
1
- import { type ContextGraphBuildOptions, type ContextGraphHit, type ContextGraphSnapshot, type ContextQuery } from "../../../contracts/index.js";
2
- export declare function buildContextGraphSnapshot(repoRoot: string, options?: ContextGraphBuildOptions): Promise<ContextGraphSnapshot>;
3
- export declare function queryContextGraph(snapshot: ContextGraphSnapshot, query: ContextQuery): ContextGraphHit[];
@@ -1,371 +0,0 @@
1
- import { readdir, readFile } from "node:fs/promises";
2
- import { extname, join, posix, relative, resolve } from "node:path";
3
- import { cloneContextGraphSnapshot } from "../../../contracts/index.js";
4
- const TEXT_EXTENSIONS = new Set([
5
- ".ts",
6
- ".tsx",
7
- ".js",
8
- ".jsx",
9
- ".mjs",
10
- ".cjs",
11
- ".json",
12
- ".md",
13
- ".yaml",
14
- ".yml",
15
- ".txt",
16
- ".py",
17
- ".go",
18
- ".rs",
19
- ".java",
20
- ".sh"
21
- ]);
22
- const IGNORED_DIRS = new Set([
23
- ".git",
24
- "node_modules",
25
- "dist",
26
- "build",
27
- ".next",
28
- ".turbo",
29
- "coverage",
30
- ".pnpm-store",
31
- ".npm-cache"
32
- ]);
33
- export async function buildContextGraphSnapshot(repoRoot, options = {}) {
34
- const normalizedRoot = resolve(repoRoot);
35
- const maxFiles = options.maxFiles ?? 600;
36
- const maxFileBytes = options.maxFileBytes ?? 96_000;
37
- const nodes = [];
38
- const state = { count: 0, truncated: false };
39
- await walkContextGraph(normalizedRoot, normalizedRoot, nodes, state, { maxFiles, maxFileBytes });
40
- const edges = buildContextGraphEdges(nodes);
41
- return cloneContextGraphSnapshot({
42
- schemaVersion: "martin.context-graph.v1",
43
- repoRoot: normalizedRoot,
44
- createdAt: new Date().toISOString(),
45
- nodeCount: nodes.length,
46
- edgeCount: edges.length,
47
- truncated: state.truncated,
48
- nodes,
49
- edges
50
- });
51
- }
52
- export function queryContextGraph(snapshot, query) {
53
- const terms = normalizeQueryTerms(query);
54
- const pathPrefix = query.pathPrefix?.replace(/\\/gu, "/").replace(/^\.\/+/u, "");
55
- const limit = query.limit ?? 8;
56
- return snapshot.nodes
57
- .filter((node) => {
58
- if (query.kinds && !query.kinds.includes(node.kind)) {
59
- return false;
60
- }
61
- if (pathPrefix && !node.path.startsWith(pathPrefix)) {
62
- return false;
63
- }
64
- return true;
65
- })
66
- .map((node) => scoreContextNode(node, terms))
67
- .filter((hit) => hit !== undefined)
68
- .sort((left, right) => right.score - left.score || left.path.localeCompare(right.path))
69
- .slice(0, limit);
70
- }
71
- async function walkContextGraph(repoRoot, currentDir, nodes, state, options) {
72
- if (state.count >= options.maxFiles) {
73
- state.truncated = true;
74
- return;
75
- }
76
- const entries = await readdir(currentDir, { withFileTypes: true });
77
- for (const entry of entries) {
78
- if (state.count >= options.maxFiles) {
79
- state.truncated = true;
80
- break;
81
- }
82
- const absPath = join(currentDir, entry.name);
83
- if (entry.isDirectory()) {
84
- if (!IGNORED_DIRS.has(entry.name)) {
85
- await walkContextGraph(repoRoot, absPath, nodes, state, options);
86
- }
87
- continue;
88
- }
89
- if (!entry.isFile()) {
90
- continue;
91
- }
92
- const extension = extname(entry.name).toLowerCase();
93
- if (!TEXT_EXTENSIONS.has(extension)) {
94
- continue;
95
- }
96
- const content = await readFile(absPath, "utf8").catch(() => undefined);
97
- if (!content || content.length > options.maxFileBytes) {
98
- continue;
99
- }
100
- const relPath = relative(repoRoot, absPath).replace(/\\/gu, "/");
101
- nodes.push(createContextNode(relPath, content));
102
- state.count += 1;
103
- }
104
- }
105
- function createContextNode(path, content) {
106
- const kind = classifyContextNodeKind(path);
107
- return {
108
- nodeId: path,
109
- path,
110
- kind,
111
- ...(inferLanguage(path) ? { language: inferLanguage(path) } : {}),
112
- sizeBytes: Buffer.byteLength(content, "utf8"),
113
- headings: extractHeadings(path, content),
114
- symbols: extractSymbols(content),
115
- keywords: extractKeywords(path, content),
116
- imports: extractImports(path, content)
117
- };
118
- }
119
- function buildContextGraphEdges(nodes) {
120
- const paths = new Set(nodes.map((node) => node.path));
121
- const edges = [];
122
- for (const node of nodes) {
123
- for (const importTarget of node.imports) {
124
- const resolvedTarget = resolveContextImport(node.path, importTarget, paths);
125
- if (resolvedTarget) {
126
- edges.push({
127
- fromNodeId: node.nodeId,
128
- toNodeId: resolvedTarget,
129
- kind: "imports"
130
- });
131
- }
132
- }
133
- if (node.kind === "document") {
134
- const documentTargets = node.keywords
135
- .map((keyword) => nodes.find((candidate) => candidate.path !== node.path && candidate.path.includes(keyword)))
136
- .filter((candidate) => candidate !== undefined)
137
- .slice(0, 3);
138
- for (const target of documentTargets) {
139
- edges.push({
140
- fromNodeId: node.nodeId,
141
- toNodeId: target.nodeId,
142
- kind: "documents"
143
- });
144
- }
145
- }
146
- }
147
- return dedupeEdges(edges);
148
- }
149
- function scoreContextNode(node, terms) {
150
- if (terms.length === 0) {
151
- return {
152
- nodeId: node.nodeId,
153
- path: node.path,
154
- kind: node.kind,
155
- score: 1,
156
- matchedTerms: [],
157
- reasons: ["default_match"]
158
- };
159
- }
160
- let score = 0;
161
- const matchedTerms = new Set();
162
- const reasons = new Set();
163
- for (const term of terms) {
164
- if (node.path.toLowerCase().includes(term)) {
165
- score += 6;
166
- matchedTerms.add(term);
167
- reasons.add("path");
168
- }
169
- if (node.keywords.includes(term)) {
170
- score += 3;
171
- matchedTerms.add(term);
172
- reasons.add("keyword");
173
- }
174
- if (node.symbols.some((symbol) => symbol.toLowerCase().includes(term))) {
175
- score += 5;
176
- matchedTerms.add(term);
177
- reasons.add("symbol");
178
- }
179
- if (node.headings.some((heading) => heading.toLowerCase().includes(term))) {
180
- score += 4;
181
- matchedTerms.add(term);
182
- reasons.add("heading");
183
- }
184
- if (node.imports.some((importPath) => importPath.toLowerCase().includes(term))) {
185
- score += 2;
186
- matchedTerms.add(term);
187
- reasons.add("import");
188
- }
189
- }
190
- if (score === 0) {
191
- return undefined;
192
- }
193
- return {
194
- nodeId: node.nodeId,
195
- path: node.path,
196
- kind: node.kind,
197
- score,
198
- matchedTerms: [...matchedTerms],
199
- reasons: [...reasons]
200
- };
201
- }
202
- function normalizeQueryTerms(query) {
203
- const rawTerms = [
204
- ...(query.text ? tokenize(query.text) : []),
205
- ...((query.terms ?? []).flatMap((term) => tokenize(term)))
206
- ];
207
- return [...new Set(rawTerms)];
208
- }
209
- function classifyContextNodeKind(path) {
210
- const extension = extname(path).toLowerCase();
211
- if (path.endsWith("package.json") || path.endsWith("tsconfig.json")) {
212
- return "manifest";
213
- }
214
- if (extension === ".md" || extension === ".txt") {
215
- return "document";
216
- }
217
- if (extension === ".sh") {
218
- return "script";
219
- }
220
- if (extension === ".json" || extension === ".yaml" || extension === ".yml") {
221
- return "config";
222
- }
223
- return "source_file";
224
- }
225
- function inferLanguage(path) {
226
- const extension = extname(path).toLowerCase();
227
- switch (extension) {
228
- case ".ts":
229
- case ".tsx":
230
- return "typescript";
231
- case ".js":
232
- case ".jsx":
233
- case ".mjs":
234
- case ".cjs":
235
- return "javascript";
236
- case ".md":
237
- return "markdown";
238
- case ".json":
239
- return "json";
240
- case ".yaml":
241
- case ".yml":
242
- return "yaml";
243
- case ".py":
244
- return "python";
245
- case ".sh":
246
- return "shell";
247
- default:
248
- return undefined;
249
- }
250
- }
251
- function extractHeadings(path, content) {
252
- if (extname(path).toLowerCase() !== ".md") {
253
- return [];
254
- }
255
- return content
256
- .split(/\r?\n/gu)
257
- .map((line) => line.trim())
258
- .filter((line) => line.startsWith("#"))
259
- .map((line) => line.replace(/^#+\s*/u, "").trim())
260
- .filter(Boolean)
261
- .slice(0, 12);
262
- }
263
- function extractSymbols(content) {
264
- const out = new Set();
265
- const patterns = [
266
- /export\s+(?:async\s+)?function\s+([A-Za-z0-9_]+)/gu,
267
- /export\s+class\s+([A-Za-z0-9_]+)/gu,
268
- /export\s+const\s+([A-Za-z0-9_]+)/gu,
269
- /function\s+([A-Za-z0-9_]+)/gu,
270
- /class\s+([A-Za-z0-9_]+)/gu,
271
- /interface\s+([A-Za-z0-9_]+)/gu,
272
- /type\s+([A-Za-z0-9_]+)/gu
273
- ];
274
- for (const pattern of patterns) {
275
- for (const match of content.matchAll(pattern)) {
276
- if (match[1]) {
277
- out.add(match[1]);
278
- }
279
- }
280
- }
281
- return [...out].slice(0, 16);
282
- }
283
- function extractKeywords(path, content) {
284
- const out = new Set();
285
- for (const part of path.split(/[\/._-]+/u)) {
286
- if (part.length >= 3) {
287
- out.add(part.toLowerCase());
288
- }
289
- }
290
- for (const token of tokenize(content.split(/\r?\n/gu).slice(0, 16).join(" "))) {
291
- out.add(token);
292
- }
293
- return [...out].slice(0, 24);
294
- }
295
- function extractImports(path, content) {
296
- if (![".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(extname(path).toLowerCase())) {
297
- return [];
298
- }
299
- const imports = new Set();
300
- const patterns = [
301
- /from\s+["']([^"']+)["']/gu,
302
- /require\(\s*["']([^"']+)["']\s*\)/gu,
303
- /import\(\s*["']([^"']+)["']\s*\)/gu
304
- ];
305
- for (const pattern of patterns) {
306
- for (const match of content.matchAll(pattern)) {
307
- const candidate = match[1]?.trim();
308
- if (candidate) {
309
- imports.add(candidate);
310
- }
311
- }
312
- }
313
- return [...imports].slice(0, 24);
314
- }
315
- function resolveContextImport(fromPath, importPath, paths) {
316
- if (!importPath.startsWith(".")) {
317
- return undefined;
318
- }
319
- const fromDirectory = fromPath.includes("/") ? fromPath.slice(0, fromPath.lastIndexOf("/")) : "";
320
- const base = posix.normalize(posix.join(fromDirectory, importPath)).replace(/^\.\//u, "");
321
- const extensionlessBase = extname(base) ? base.slice(0, -extname(base).length) : base;
322
- const candidates = [
323
- base,
324
- extensionlessBase,
325
- `${extensionlessBase}.ts`,
326
- `${extensionlessBase}.tsx`,
327
- `${extensionlessBase}.js`,
328
- `${extensionlessBase}.jsx`,
329
- `${extensionlessBase}.md`,
330
- `${base}.ts`,
331
- `${base}.tsx`,
332
- `${base}.js`,
333
- `${base}.jsx`,
334
- `${base}.md`,
335
- `${base}/index.ts`,
336
- `${base}/index.tsx`,
337
- `${base}/index.js`
338
- ];
339
- return candidates.find((candidate) => paths.has(candidate));
340
- }
341
- function dedupeEdges(edges) {
342
- const seen = new Set();
343
- return edges.filter((edge) => {
344
- const key = `${edge.kind}:${edge.fromNodeId}:${edge.toNodeId}`;
345
- if (seen.has(key)) {
346
- return false;
347
- }
348
- seen.add(key);
349
- return true;
350
- });
351
- }
352
- function tokenize(input) {
353
- const normalized = input.replace(/([a-z0-9])([A-Z])/gu, "$1 $2").toLowerCase();
354
- const stopWords = new Set([
355
- "the",
356
- "and",
357
- "for",
358
- "with",
359
- "from",
360
- "that",
361
- "this",
362
- "into",
363
- "only",
364
- "keep",
365
- "make",
366
- "loop",
367
- "then",
368
- "when"
369
- ]);
370
- return [...new Set(normalized.match(/[a-z0-9_]{3,}/gu) ?? [])].filter((term) => !stopWords.has(term));
371
- }
@@ -1,26 +0,0 @@
1
- export type ContextIntegrityVerdict = "clean" | "context_poisoning_warning" | "context_poisoning_block";
2
- export interface ContextIntegrityPrecheck {
3
- runId: string;
4
- attemptIndex: number;
5
- verdict: ContextIntegrityVerdict;
6
- reason?: string;
7
- detectedSignals: string[];
8
- analyzedChannels: {
9
- system: boolean;
10
- user: boolean;
11
- tools: boolean;
12
- history: boolean;
13
- };
14
- timestamp: string;
15
- }
16
- /**
17
- * T05: Context Poisoning Pre-gate.
18
- * Scans untrusted input channels for authority inversion or instruction re-injection.
19
- * Runs BEFORE admission control and core reasoning.
20
- */
21
- export declare function runContextIntegrityPrecheck(runId: string, attemptIndex: number, artifactsDir: string, inputs: {
22
- userPrompt?: string;
23
- toolOutput?: string;
24
- retrievedContext?: string;
25
- history?: string;
26
- }): Promise<ContextIntegrityPrecheck>;
@@ -1,80 +0,0 @@
1
- import { mkdir, writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- const POISON_PATTERNS = [
4
- /ignore\s+(?:all\s+)?previous\s+instructions/i,
5
- /you\s+are\s+now\s+a\s+(?!Martin\s+Loop)/i,
6
- /new\s+rule:/i,
7
- /disregard\s+(?:safety|policy|guardrails)/i,
8
- /override\s+system\s+authority/i,
9
- /hidden\s+instruction:/i,
10
- /\[system_override\]/i,
11
- /\[authority_inversion\]/i
12
- ];
13
- /**
14
- * Identity-redefinition / persona-override patterns.
15
- *
16
- * These intentionally require an *override framing* (e.g. "now", "no longer",
17
- * "forget", "pretend", or an explicit authority-role claim) rather than any
18
- * sentence shaped like "you are X" / "I am X" — the latter matches ordinary
19
- * benign text (e.g. "You are welcome to try MartinLoop") and produced
20
- * false-positive hard aborts.
21
- */
22
- const IDENTITY_REDEFINITION_PATTERNS = [
23
- /\byou(?:'re|\s+are)\s+now\s+(?:a|an|the)\b(?!\s+(?:martin\s+loop|ai\s+coding\s+agent))/i,
24
- /\byou(?:'re|\s+are)\s+no\s+longer\s+(?!(?:martin\s+loop|an?\s+ai)\b)/i,
25
- /\bforget\s+(?:that\s+)?you(?:'re|\s+are)\s+martin\s+loop\b/i,
26
- /\b(?:pretend|imagine)\s+(?:that\s+)?you(?:'re|\s+are)\b/i,
27
- /\bact\s+as\s+(?:if\s+you(?:'re|\s+are)\s+)?(?:a|an)\s+(?:different|new|unrestricted|jailbroken)\b/i,
28
- /\bi\s+am\s+(?:the|your)\s+(?:new\s+)?(?:system|developer|admin(?:istrator)?|root\s*user|owner|creator|operator)\b/i
29
- ];
30
- /**
31
- * T05: Context Poisoning Pre-gate.
32
- * Scans untrusted input channels for authority inversion or instruction re-injection.
33
- * Runs BEFORE admission control and core reasoning.
34
- */
35
- export async function runContextIntegrityPrecheck(runId, attemptIndex, artifactsDir, inputs) {
36
- const signals = [];
37
- const analyzedChannels = {
38
- system: true,
39
- user: Boolean(inputs.userPrompt),
40
- tools: Boolean(inputs.toolOutput),
41
- history: Boolean(inputs.history)
42
- };
43
- const untrustedBuffer = [
44
- inputs.userPrompt,
45
- inputs.toolOutput,
46
- inputs.retrievedContext,
47
- inputs.history
48
- ]
49
- .filter(Boolean)
50
- .join("\n---\n");
51
- for (const pattern of POISON_PATTERNS) {
52
- if (pattern.test(untrustedBuffer)) {
53
- signals.push(`Detected poison pattern: ${pattern.toString()}`);
54
- }
55
- }
56
- for (const pattern of IDENTITY_REDEFINITION_PATTERNS) {
57
- if (pattern.test(untrustedBuffer)) {
58
- signals.push("Identity redefinition attempt detected.");
59
- break;
60
- }
61
- }
62
- const verdict = signals.length > 0 ? "context_poisoning_block" : "clean";
63
- const precheck = {
64
- runId,
65
- attemptIndex,
66
- verdict,
67
- reason: signals.length > 0 ? `Detected ${signals.length} poisoning signal(s).` : undefined,
68
- detectedSignals: signals,
69
- analyzedChannels,
70
- timestamp: new Date().toISOString()
71
- };
72
- try {
73
- await mkdir(artifactsDir, { recursive: true });
74
- await writeFile(join(artifactsDir, "context-integrity-precheck.json"), JSON.stringify(precheck, null, 2), "utf8");
75
- }
76
- catch {
77
- // non-fatal — artifact persistence is best-effort
78
- }
79
- return precheck;
80
- }
@@ -1,38 +0,0 @@
1
- export interface RepoGroundingFile {
2
- path: string;
3
- symbols: string[];
4
- keywords: string[];
5
- }
6
- export interface RepoGroundingIndex {
7
- schemaVersion: "martin.grounding.v1";
8
- repoRoot: string;
9
- createdAt: string;
10
- fileCount: number;
11
- files: RepoGroundingFile[];
12
- }
13
- export interface RepoGroundingHit {
14
- path: string;
15
- score: number;
16
- matchedTerms: string[];
17
- symbols: string[];
18
- }
19
- export declare function resolveGroundingRoot(env?: NodeJS.ProcessEnv): string;
20
- export declare function loadOrBuildRepoGroundingIndex(repoRoot: string): Promise<RepoGroundingIndex>;
21
- export declare function buildRepoGroundingIndex(repoRoot: string): Promise<RepoGroundingIndex>;
22
- export declare function queryRepoGroundingIndex(index: RepoGroundingIndex, query: string, limit?: number): RepoGroundingHit[];
23
- export type GroundingViolationKind = "file_not_found" | "symbol_not_found" | "import_not_found" | "patch_outside_allowed_paths";
24
- export interface GroundingViolation {
25
- kind: GroundingViolationKind;
26
- reference: string;
27
- sourceHint?: string;
28
- }
29
- export interface GroundingScanResult {
30
- violations: GroundingViolation[];
31
- scannedReferences: number;
32
- resolvedFiles: string[];
33
- /** True when the diff adds only comments, whitespace, or empty lines — no substantive code. */
34
- contentOnly: boolean;
35
- }
36
- export declare function scanPatchForGroundingViolations(diff: string, index: RepoGroundingIndex, options?: {
37
- allowedPaths?: string[];
38
- }): GroundingScanResult;