martin-loop 0.1.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/CODE_OF_CONDUCT.md +32 -0
  2. package/LICENSE +21 -21
  3. package/README.md +307 -398
  4. package/demo/seeded-workspace/README.md +35 -35
  5. package/demo/seeded-workspace/TASKS.md +29 -29
  6. package/demo/seeded-workspace/martin.config.yaml +11 -11
  7. package/demo/seeded-workspace/package.json +8 -8
  8. package/demo/seeded-workspace/src/invoice-summary.js +11 -11
  9. package/demo/seeded-workspace/test/invoice-summary.test.js +20 -20
  10. package/dist/bin/martin-loop.js +0 -0
  11. package/dist/vendor/adapters/counter.d.ts +1 -0
  12. package/dist/vendor/adapters/counter.js +4 -0
  13. package/dist/vendor/adapters/git-baseline.d.ts +50 -0
  14. package/dist/vendor/adapters/git-baseline.js +233 -0
  15. package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
  16. package/dist/vendor/adapters/openrouter-adapter.js +302 -0
  17. package/dist/vendor/adapters/usage.d.ts +48 -0
  18. package/dist/vendor/adapters/usage.js +66 -0
  19. package/dist/vendor/cli/bin/exit.d.ts +12 -0
  20. package/dist/vendor/cli/bin/exit.js +28 -0
  21. package/dist/vendor/cli/commands/analyze.d.ts +5 -0
  22. package/dist/vendor/cli/commands/analyze.js +58 -0
  23. package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
  24. package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
  25. package/dist/vendor/cli/commands/audit.d.ts +8 -0
  26. package/dist/vendor/cli/commands/audit.js +199 -0
  27. package/dist/vendor/cli/commands/corpus.d.ts +5 -0
  28. package/dist/vendor/cli/commands/corpus.js +60 -0
  29. package/dist/vendor/cli/commands/doctor.d.ts +8 -0
  30. package/dist/vendor/cli/commands/doctor.js +219 -0
  31. package/dist/vendor/cli/commands/explain.d.ts +17 -0
  32. package/dist/vendor/cli/commands/explain.js +176 -0
  33. package/dist/vendor/cli/commands/export.d.ts +5 -0
  34. package/dist/vendor/cli/commands/export.js +60 -0
  35. package/dist/vendor/cli/commands/governance.d.ts +8 -0
  36. package/dist/vendor/cli/commands/governance.js +95 -0
  37. package/dist/vendor/cli/commands/improve.d.ts +18 -0
  38. package/dist/vendor/cli/commands/improve.js +396 -0
  39. package/dist/vendor/cli/commands/init.d.ts +8 -0
  40. package/dist/vendor/cli/commands/init.js +281 -0
  41. package/dist/vendor/cli/commands/migration.d.ts +8 -0
  42. package/dist/vendor/cli/commands/migration.js +67 -0
  43. package/dist/vendor/cli/commands/prior.d.ts +23 -0
  44. package/dist/vendor/cli/commands/prior.js +145 -0
  45. package/dist/vendor/cli/commands/resume.d.ts +21 -0
  46. package/dist/vendor/cli/commands/resume.js +73 -0
  47. package/dist/vendor/cli/commands/verify.d.ts +6 -0
  48. package/dist/vendor/cli/commands/verify.js +43 -0
  49. package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
  50. package/dist/vendor/cli/research/public-corpus.js +151 -0
  51. package/dist/vendor/cli/ui/error-card.d.ts +38 -0
  52. package/dist/vendor/cli/ui/error-card.js +103 -0
  53. package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
  54. package/dist/vendor/cli/ui/mission-brief.js +173 -0
  55. package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
  56. package/dist/vendor/cli/ui/summary-card.js +102 -0
  57. package/dist/vendor/contracts/audit.d.ts +46 -0
  58. package/dist/vendor/contracts/audit.js +360 -0
  59. package/dist/vendor/contracts/post-phase15.d.ts +240 -0
  60. package/dist/vendor/contracts/post-phase15.js +166 -0
  61. package/dist/vendor/core/agent/mandates.d.ts +46 -0
  62. package/dist/vendor/core/agent/mandates.js +178 -0
  63. package/dist/vendor/core/agent/receipts.d.ts +38 -0
  64. package/dist/vendor/core/agent/receipts.js +131 -0
  65. package/dist/vendor/core/agent/signing.d.ts +17 -0
  66. package/dist/vendor/core/agent/signing.js +91 -0
  67. package/dist/vendor/core/attestation/sign.d.ts +25 -0
  68. package/dist/vendor/core/attestation/sign.js +216 -0
  69. package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
  70. package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
  71. package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
  72. package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
  73. package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
  74. package/dist/vendor/core/autonomy/envelope.js +27 -0
  75. package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
  76. package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
  77. package/dist/vendor/core/autonomy/resume.d.ts +15 -0
  78. package/dist/vendor/core/autonomy/resume.js +23 -0
  79. package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
  80. package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
  81. package/dist/vendor/core/context-distillation.d.ts +3 -0
  82. package/dist/vendor/core/context-distillation.js +44 -0
  83. package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
  84. package/dist/vendor/core/context-flow/compile-context.js +111 -0
  85. package/dist/vendor/core/context-flow/entities.d.ts +2 -0
  86. package/dist/vendor/core/context-flow/entities.js +44 -0
  87. package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
  88. package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
  89. package/dist/vendor/core/context-flow/index.d.ts +11 -0
  90. package/dist/vendor/core/context-flow/index.js +24 -0
  91. package/dist/vendor/core/context-flow/labels.d.ts +3 -0
  92. package/dist/vendor/core/context-flow/labels.js +17 -0
  93. package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
  94. package/dist/vendor/core/context-flow/normalizer.js +69 -0
  95. package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
  96. package/dist/vendor/core/context-flow/profiles.js +36 -0
  97. package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
  98. package/dist/vendor/core/context-flow/redaction.js +6 -0
  99. package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
  100. package/dist/vendor/core/context-flow/sensitivity.js +27 -0
  101. package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
  102. package/dist/vendor/core/context-flow/sync-preview.js +22 -0
  103. package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
  104. package/dist/vendor/core/context-flow/token-estimator.js +13 -0
  105. package/dist/vendor/core/context-flow/types.d.ts +91 -0
  106. package/dist/vendor/core/context-flow/types.js +2 -0
  107. package/dist/vendor/core/context-utility.d.ts +47 -0
  108. package/dist/vendor/core/context-utility.js +405 -0
  109. package/dist/vendor/core/cost/pipeline.d.ts +92 -0
  110. package/dist/vendor/core/cost/pipeline.js +141 -0
  111. package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
  112. package/dist/vendor/core/cost/tagged-cost.js +55 -0
  113. package/dist/vendor/core/cost-governor.d.ts +2 -0
  114. package/dist/vendor/core/cost-governor.js +50 -0
  115. package/dist/vendor/core/cve/cve-check.d.ts +80 -0
  116. package/dist/vendor/core/cve/cve-check.js +172 -0
  117. package/dist/vendor/core/digital-twin/index.d.ts +27 -0
  118. package/dist/vendor/core/digital-twin/index.js +90 -0
  119. package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
  120. package/dist/vendor/core/drift/drift-graph.js +100 -0
  121. package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
  122. package/dist/vendor/core/drift/objective-lock.js +88 -0
  123. package/dist/vendor/core/drift/scope.d.ts +46 -0
  124. package/dist/vendor/core/drift/scope.js +102 -0
  125. package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
  126. package/dist/vendor/core/drift/signature-lock.js +202 -0
  127. package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
  128. package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
  129. package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
  130. package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
  131. package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
  132. package/dist/vendor/core/evidence/claim-audit.js +89 -0
  133. package/dist/vendor/core/exit-intelligence.d.ts +2 -0
  134. package/dist/vendor/core/exit-intelligence.js +58 -0
  135. package/dist/vendor/core/explain/formatter.d.ts +42 -0
  136. package/dist/vendor/core/explain/formatter.js +171 -0
  137. package/dist/vendor/core/explain/timeline.d.ts +29 -0
  138. package/dist/vendor/core/explain/timeline.js +213 -0
  139. package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
  140. package/dist/vendor/core/failure-taxonomy.js +76 -0
  141. package/dist/vendor/core/gateway/index.d.ts +10 -0
  142. package/dist/vendor/core/gateway/index.js +12 -0
  143. package/dist/vendor/core/gateway/registry.d.ts +40 -0
  144. package/dist/vendor/core/gateway/registry.js +97 -0
  145. package/dist/vendor/core/gateway/transport.d.ts +31 -0
  146. package/dist/vendor/core/gateway/transport.js +82 -0
  147. package/dist/vendor/core/gateway/vault.d.ts +19 -0
  148. package/dist/vendor/core/gateway/vault.js +29 -0
  149. package/dist/vendor/core/graph/adapters.d.ts +43 -0
  150. package/dist/vendor/core/graph/adapters.js +91 -0
  151. package/dist/vendor/core/graph/hotspots.d.ts +22 -0
  152. package/dist/vendor/core/graph/hotspots.js +30 -0
  153. package/dist/vendor/core/graph/index.d.ts +1 -0
  154. package/dist/vendor/core/graph/index.js +2 -0
  155. package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
  156. package/dist/vendor/core/honey/honey-tokens.js +44 -0
  157. package/dist/vendor/core/index.d.ts +2 -2
  158. package/dist/vendor/core/index.js +38 -12
  159. package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
  160. package/dist/vendor/core/learning/bayesian-update.js +60 -0
  161. package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
  162. package/dist/vendor/core/learning/prior-sets.js +111 -0
  163. package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
  164. package/dist/vendor/core/learning/promotion-gate.js +23 -0
  165. package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
  166. package/dist/vendor/core/leash/blast-radius.js +156 -0
  167. package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
  168. package/dist/vendor/core/leash/policy-leash.js +117 -0
  169. package/dist/vendor/core/memo/memo.d.ts +63 -0
  170. package/dist/vendor/core/memo/memo.js +97 -0
  171. package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
  172. package/dist/vendor/core/memory/learning-pipeline.js +391 -0
  173. package/dist/vendor/core/memory/palace.d.ts +84 -0
  174. package/dist/vendor/core/memory/palace.js +379 -0
  175. package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
  176. package/dist/vendor/core/merge/ast-merge.js +350 -0
  177. package/dist/vendor/core/merge/text-merge.d.ts +12 -0
  178. package/dist/vendor/core/merge/text-merge.js +182 -0
  179. package/dist/vendor/core/otel/tracer.d.ts +45 -0
  180. package/dist/vendor/core/otel/tracer.js +116 -0
  181. package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
  182. package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
  183. package/dist/vendor/core/parallel/scorer.d.ts +24 -0
  184. package/dist/vendor/core/parallel/scorer.js +65 -0
  185. package/dist/vendor/core/pattern-detection.d.ts +64 -0
  186. package/dist/vendor/core/pattern-detection.js +108 -0
  187. package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
  188. package/dist/vendor/core/persistence/checkpoint.js +156 -0
  189. package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
  190. package/dist/vendor/core/persistence/cleanup.js +131 -0
  191. package/dist/vendor/core/persistence/index.d.ts +2 -0
  192. package/dist/vendor/core/persistence/index.js +1 -0
  193. package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
  194. package/dist/vendor/core/persistence/runs-reader.js +84 -0
  195. package/dist/vendor/core/persistence/store.d.ts +6 -1
  196. package/dist/vendor/core/persistence/store.js +5 -0
  197. package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
  198. package/dist/vendor/core/policy/file-touch-quota.js +105 -0
  199. package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
  200. package/dist/vendor/core/policy/policy-loader.js +170 -0
  201. package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
  202. package/dist/vendor/core/policy/policy-schema.js +78 -0
  203. package/dist/vendor/core/probe/probe.d.ts +49 -0
  204. package/dist/vendor/core/probe/probe.js +115 -0
  205. package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
  206. package/dist/vendor/core/proof/patch-proof.js +84 -0
  207. package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
  208. package/dist/vendor/core/proof/semantic-probe.js +82 -0
  209. package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
  210. package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
  211. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  212. package/dist/vendor/core/red-blue/red-phase.js +141 -0
  213. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  214. package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
  215. package/dist/vendor/core/replay/replay.d.ts +85 -0
  216. package/dist/vendor/core/replay/replay.js +109 -0
  217. package/dist/vendor/core/router/engine.d.ts +54 -0
  218. package/dist/vendor/core/router/engine.js +131 -0
  219. package/dist/vendor/core/router/index.d.ts +1 -0
  220. package/dist/vendor/core/router/index.js +2 -0
  221. package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
  222. package/dist/vendor/core/router/trust-calibration.js +127 -0
  223. package/dist/vendor/core/run-martin.d.ts +2 -0
  224. package/dist/vendor/core/run-martin.js +287 -0
  225. package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
  226. package/dist/vendor/core/security/cve-scanner.js +178 -0
  227. package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
  228. package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
  229. package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
  230. package/dist/vendor/core/sentinel/progress-guard.js +46 -0
  231. package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
  232. package/dist/vendor/core/siem/siem-emitter.js +157 -0
  233. package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
  234. package/dist/vendor/core/strategy/attempt-brief.js +89 -0
  235. package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
  236. package/dist/vendor/core/summarize/diff-summary.js +204 -0
  237. package/dist/vendor/core/surface-signals.d.ts +21 -0
  238. package/dist/vendor/core/surface-signals.js +139 -0
  239. package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
  240. package/dist/vendor/core/truth/truth-wall.js +69 -0
  241. package/dist/vendor/core/truth-spine.d.ts +26 -0
  242. package/dist/vendor/core/truth-spine.js +62 -0
  243. package/dist/vendor/core/types.d.ts +115 -0
  244. package/dist/vendor/core/types.js +2 -0
  245. package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
  246. package/dist/vendor/core/verification/tiered-verify.js +29 -0
  247. package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
  248. package/dist/vendor/core/verifier-pyramid.js +111 -0
  249. package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
  250. package/dist/vendor/core/workflow-artifacts.js +668 -0
  251. package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
  252. package/dist/vendor/core/wrap/supervised-run.js +178 -0
  253. package/docs/assets/cli-animated.svg +139 -0
  254. package/docs/assets/cli-static.svg +34 -0
  255. package/docs/assets/github-hero-v2.svg +23 -0
  256. package/docs/assets/martin-raplph.png.jpg +0 -0
  257. package/docs/assets/martinloop-logo.png +0 -0
  258. package/docs/assets/nvidia-inception-program-light.png +0 -0
  259. package/docs/assets/nvidia-inception-program.png +0 -0
  260. package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
  261. package/docs/assets/side-by-side.svg +134 -0
  262. package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -142
  263. package/docs/oss/EXAMPLES.md +134 -134
  264. package/docs/oss/OSS-BOUNDARY-REPORT.json +1 -1
  265. package/docs/oss/OSS-BOUNDARY-REPORT.md +1 -1
  266. package/docs/oss/QUICKSTART.md +170 -165
  267. package/docs/oss/RALPH-LOOP-SAFETY.md +113 -113
  268. package/docs/oss/README.md +96 -96
  269. package/docs/oss/RELEASE-SURFACE-REPORT.json +2 -1
  270. package/docs/oss/RELEASE-SURFACE-REPORT.md +2 -1
  271. package/package.json +130 -58
  272. package/docs/distribution/DIRECTORY-SUBMISSIONS.md +0 -89
  273. package/docs/distribution/INTEGRATION-OUTREACH.md +0 -61
  274. package/docs/distribution/UNDER-3-CHALLENGE.md +0 -65
@@ -0,0 +1,350 @@
1
+ // ─── AST 3-Way Merge ──────────────────────────────────────────────────────────
2
+ //
3
+ // Deterministic merge of two unified-diff patches against a common base.
4
+ // For .ts/.tsx files: uses ts-morph to detect structural conflicts.
5
+ // For all other file types: falls back to 3-way text merge immediately.
6
+ // ts-morph errors are caught; on failure tsMorphError=true and text merge is used.
7
+ import { Project } from "ts-morph";
8
+ import { textMerge } from "./text-merge.js";
9
+ // ─── Patch parsing / application ─────────────────────────────────────────────
10
+ /** Apply a unified diff patch to a source string (line-level, best-effort). */
11
+ function applyPatch(source, patch) {
12
+ const lines = source.split("\n");
13
+ const result = [];
14
+ let lineIdx = 0;
15
+ const patchLines = patch.split("\n");
16
+ let patchIdx = 0;
17
+ // Skip file headers
18
+ while (patchIdx < patchLines.length &&
19
+ ((patchLines[patchIdx]?.startsWith("---") ?? false) ||
20
+ (patchLines[patchIdx]?.startsWith("+++") ?? false) ||
21
+ (patchLines[patchIdx]?.startsWith("diff") ?? false))) {
22
+ patchIdx++;
23
+ }
24
+ while (patchIdx < patchLines.length) {
25
+ const line = patchLines[patchIdx] ?? "";
26
+ if (line.startsWith("@@")) {
27
+ const match = line.match(/@@ -(\d+)(?:,\d+)? \+\d+(?:,\d+)? @@/);
28
+ if (match) {
29
+ const oldStart = parseInt(match[1] ?? "1", 10) - 1; // 0-based
30
+ while (lineIdx < oldStart && lineIdx < lines.length) {
31
+ result.push(lines[lineIdx] ?? "");
32
+ lineIdx++;
33
+ }
34
+ }
35
+ patchIdx++;
36
+ continue;
37
+ }
38
+ if (line.startsWith("-") && !line.startsWith("---")) {
39
+ lineIdx++;
40
+ patchIdx++;
41
+ }
42
+ else if (line.startsWith("+") && !line.startsWith("+++")) {
43
+ result.push(line.slice(1));
44
+ patchIdx++;
45
+ }
46
+ else if (line.startsWith(" ")) {
47
+ if (lineIdx < lines.length) {
48
+ result.push(lines[lineIdx] ?? "");
49
+ lineIdx++;
50
+ }
51
+ patchIdx++;
52
+ }
53
+ else {
54
+ patchIdx++;
55
+ }
56
+ }
57
+ while (lineIdx < lines.length) {
58
+ result.push(lines[lineIdx] ?? "");
59
+ lineIdx++;
60
+ }
61
+ return result.join("\n");
62
+ }
63
+ // ─── LCS helpers for computeDiff ─────────────────────────────────────────────
64
+ function lcsLines(a, b) {
65
+ const m = a.length;
66
+ const n = b.length;
67
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
68
+ for (let i = 1; i <= m; i++) {
69
+ for (let j = 1; j <= n; j++) {
70
+ const aLine = a[i - 1];
71
+ const bLine = b[j - 1];
72
+ if (aLine !== undefined && bLine !== undefined && aLine === bLine) {
73
+ dp[i][j] = (dp[i - 1][j - 1] ?? 0) + 1;
74
+ }
75
+ else {
76
+ dp[i][j] = Math.max(dp[i - 1][j] ?? 0, dp[i][j - 1] ?? 0);
77
+ }
78
+ }
79
+ }
80
+ const result = [];
81
+ let i = m;
82
+ let j = n;
83
+ while (i > 0 && j > 0) {
84
+ const aLine = a[i - 1];
85
+ const bLine = b[j - 1];
86
+ if (aLine !== undefined && bLine !== undefined && aLine === bLine) {
87
+ result.unshift(aLine);
88
+ i--;
89
+ j--;
90
+ }
91
+ else if ((dp[i - 1][j] ?? 0) >= (dp[i][j - 1] ?? 0))
92
+ i--;
93
+ else
94
+ j--;
95
+ }
96
+ return result;
97
+ }
98
+ /** Compute a minimal unified diff string from original → modified using LCS. */
99
+ function computeDiff(original, modified, filePath) {
100
+ if (original === modified)
101
+ return "";
102
+ const oLines = original.split("\n");
103
+ const mLines = modified.split("\n");
104
+ const hunks = [`--- a/${filePath}`, `+++ b/${filePath}`];
105
+ const common = lcsLines(oLines, mLines);
106
+ const ops = [];
107
+ let oi = 0;
108
+ let mi = 0;
109
+ let ci = 0;
110
+ while (oi < oLines.length || mi < mLines.length) {
111
+ const commonLine = common[ci];
112
+ const originalLine = oLines[oi];
113
+ const modifiedLine = mLines[mi];
114
+ if (commonLine !== undefined &&
115
+ originalLine !== undefined &&
116
+ modifiedLine !== undefined &&
117
+ originalLine === commonLine &&
118
+ modifiedLine === commonLine) {
119
+ ops.push({ type: "keep", line: commonLine });
120
+ oi++;
121
+ mi++;
122
+ ci++;
123
+ }
124
+ else if (modifiedLine !== undefined && (commonLine === undefined || modifiedLine !== commonLine)) {
125
+ ops.push({ type: "ins", line: modifiedLine });
126
+ mi++;
127
+ }
128
+ else {
129
+ ops.push({ type: "del", line: originalLine ?? "" });
130
+ oi++;
131
+ }
132
+ }
133
+ const changed = ops.map((op, idx) => (op.type !== "keep" ? idx : -1)).filter((x) => x >= 0);
134
+ if (changed.length === 0)
135
+ return "";
136
+ const ctx = 3;
137
+ const start = Math.max(0, changed[0] - ctx);
138
+ const end = Math.min(ops.length - 1, changed[changed.length - 1] + ctx);
139
+ const slice = ops.slice(start, end + 1);
140
+ const delCount = slice.filter((o) => o.type !== "ins").length;
141
+ const insCount = slice.filter((o) => o.type !== "del").length;
142
+ hunks.push(`@@ -${start + 1},${delCount} +${start + 1},${insCount} @@`);
143
+ for (const op of slice) {
144
+ if (op.type === "keep")
145
+ hunks.push(` ${op.line}`);
146
+ else if (op.type === "del")
147
+ hunks.push(`-${op.line}`);
148
+ else
149
+ hunks.push(`+${op.line}`);
150
+ }
151
+ return hunks.join("\n");
152
+ }
153
+ // ─── Symbol extraction ────────────────────────────────────────────────────────
154
+ function extractSymbols(sf) {
155
+ const symbols = new Map();
156
+ for (const fn of sf.getFunctions()) {
157
+ const name = fn.getName();
158
+ if (name)
159
+ symbols.set(name, fn.getBodyText() ?? "");
160
+ }
161
+ for (const cls of sf.getClasses()) {
162
+ const name = cls.getName();
163
+ if (name)
164
+ symbols.set(name, cls.getText());
165
+ }
166
+ for (const decl of sf.getVariableDeclarations()) {
167
+ symbols.set(decl.getName(), decl.getInitializer()?.getText() ?? "");
168
+ }
169
+ return symbols;
170
+ }
171
+ function extractImports(sf) {
172
+ const result = new Set();
173
+ for (const imp of sf.getImportDeclarations()) {
174
+ result.add(imp.getText().trim());
175
+ }
176
+ return result;
177
+ }
178
+ // ─── AST-level conflict detection & merge ────────────────────────────────────
179
+ function runAstMerge(baseSource, patchA, patchB, filePath) {
180
+ const sourceA = applyPatch(baseSource, patchA);
181
+ const sourceB = applyPatch(baseSource, patchB);
182
+ const project = new Project({ useInMemoryFileSystem: true });
183
+ const sfBase = project.createSourceFile("base.ts", baseSource);
184
+ const sfA = project.createSourceFile("a.ts", sourceA);
185
+ const sfB = project.createSourceFile("b.ts", sourceB);
186
+ const baseSymbols = extractSymbols(sfBase);
187
+ const aSymbols = extractSymbols(sfA);
188
+ const bSymbols = extractSymbols(sfB);
189
+ const baseImports = extractImports(sfBase);
190
+ const aImports = extractImports(sfA);
191
+ const bImports = extractImports(sfB);
192
+ // Detect changed/deleted symbols per patch
193
+ const aChanged = new Set();
194
+ const aDeleted = new Set();
195
+ for (const [name, body] of baseSymbols) {
196
+ if (!aSymbols.has(name))
197
+ aDeleted.add(name);
198
+ else if (aSymbols.get(name) !== body)
199
+ aChanged.add(name);
200
+ }
201
+ const bChanged = new Set();
202
+ const bDeleted = new Set();
203
+ for (const [name, body] of baseSymbols) {
204
+ if (!bSymbols.has(name))
205
+ bDeleted.add(name);
206
+ else if (bSymbols.get(name) !== body)
207
+ bChanged.add(name);
208
+ }
209
+ // delete_vs_modify conflicts
210
+ for (const name of aDeleted) {
211
+ if (bChanged.has(name)) {
212
+ return {
213
+ conflict: true,
214
+ conflictType: "delete_vs_modify",
215
+ conflictReport: `Symbol '${name}' was deleted by patchA but modified by patchB.`,
216
+ mergeType: "ast"
217
+ };
218
+ }
219
+ }
220
+ for (const name of bDeleted) {
221
+ if (aChanged.has(name)) {
222
+ return {
223
+ conflict: true,
224
+ conflictType: "delete_vs_modify",
225
+ conflictReport: `Symbol '${name}' was deleted by patchB but modified by patchA.`,
226
+ mergeType: "ast"
227
+ };
228
+ }
229
+ }
230
+ // same_function_body conflicts
231
+ for (const name of aChanged) {
232
+ if (bChanged.has(name) && aSymbols.get(name) !== bSymbols.get(name)) {
233
+ return {
234
+ conflict: true,
235
+ conflictType: "same_function_body",
236
+ conflictReport: `Symbol '${name}' was modified by both patchA and patchB with different bodies.`,
237
+ mergeType: "ast"
238
+ };
239
+ }
240
+ }
241
+ // Build merged source: start from sourceA, apply B's non-overlapping changes
242
+ let mergedSource = sourceA;
243
+ for (const name of bChanged) {
244
+ if (!aChanged.has(name)) {
245
+ const aBody = aSymbols.get(name) ?? baseSymbols.get(name);
246
+ const bBody = bSymbols.get(name);
247
+ if (aBody !== undefined && bBody !== undefined) {
248
+ mergedSource = mergedSource.replace(aBody, bBody);
249
+ }
250
+ }
251
+ }
252
+ // Merge imports: union of A + B, deduplicate
253
+ const addedByB = [...bImports].filter((imp) => !baseImports.has(imp) && !aImports.has(imp));
254
+ if (addedByB.length > 0) {
255
+ const sorted = [...addedByB].sort();
256
+ const m = mergedSource.match(/^(import[^\n]+\n)+/m);
257
+ if (m) {
258
+ const importBlock = m[0] ?? "";
259
+ const insertPos = (m.index ?? 0) + importBlock.length;
260
+ mergedSource =
261
+ mergedSource.slice(0, insertPos) +
262
+ sorted.join("\n") +
263
+ "\n" +
264
+ mergedSource.slice(insertPos);
265
+ }
266
+ }
267
+ // Deduplicate identical import lines
268
+ const seen = new Set();
269
+ mergedSource = mergedSource
270
+ .split("\n")
271
+ .filter((line) => {
272
+ if (line.trimStart().startsWith("import ")) {
273
+ if (seen.has(line))
274
+ return false;
275
+ seen.add(line);
276
+ }
277
+ return true;
278
+ })
279
+ .join("\n");
280
+ const mergedDiff = computeDiff(baseSource, mergedSource, filePath);
281
+ return { conflict: false, mergeType: "ast", mergedDiff };
282
+ }
283
+ // ─── Patch overlap helpers ────────────────────────────────────────────────────
284
+ function getPatchFilePath(patch) {
285
+ const match = patch.match(/^(?:---|\+\+\+) [ab]\/(.+)$/m);
286
+ return match?.[1] ?? undefined;
287
+ }
288
+ function patchesAreTriviallyNonOverlapping(patchA, patchB) {
289
+ const pathA = getPatchFilePath(patchA);
290
+ const pathB = getPatchFilePath(patchB);
291
+ if (!pathA || !pathB)
292
+ return false;
293
+ return pathA !== pathB;
294
+ }
295
+ function isTsFile(filePath) {
296
+ if (!filePath)
297
+ return false;
298
+ return filePath.endsWith(".ts") || filePath.endsWith(".tsx");
299
+ }
300
+ // ─── Text fallback helper ─────────────────────────────────────────────────────
301
+ function textFallback(baseSource, patchA, patchB, filePath, tsMorphError) {
302
+ const sourceA = applyPatch(baseSource, patchA);
303
+ const sourceB = applyPatch(baseSource, patchB);
304
+ const tr = textMerge(baseSource, sourceA, sourceB);
305
+ const mergedDiff = tr.hasConflicts
306
+ ? undefined
307
+ : computeDiff(baseSource, tr.merged, filePath);
308
+ return {
309
+ conflict: tr.hasConflicts,
310
+ mergeType: "text",
311
+ mergedDiff,
312
+ conflictReport: tr.hasConflicts
313
+ ? `Text merge produced ${tr.conflictMarkers} conflict marker(s).`
314
+ : undefined,
315
+ ...(tsMorphError ? { tsMorphError: true } : {})
316
+ };
317
+ }
318
+ // ─── Public API ───────────────────────────────────────────────────────────────
319
+ export function astMerge(baseSource, patchA, patchB, options) {
320
+ const filePath = options?.filePath ?? "file.ts";
321
+ // Trivial: patches are for different files
322
+ if (patchesAreTriviallyNonOverlapping(patchA, patchB)) {
323
+ return {
324
+ conflict: false,
325
+ mergeType: "trivial",
326
+ mergedDiff: patchA + "\n" + patchB
327
+ };
328
+ }
329
+ // Non-TS: text merge immediately
330
+ if (!isTsFile(filePath)) {
331
+ return textFallback(baseSource, patchA, patchB, filePath);
332
+ }
333
+ // TS: attempt AST merge; wrap ALL ts-morph calls in try/catch
334
+ try {
335
+ return runAstMerge(baseSource, patchA, patchB, filePath);
336
+ }
337
+ catch {
338
+ return textFallback(baseSource, patchA, patchB, filePath, true);
339
+ }
340
+ }
341
+ export function buildMergeArtifact(runId, patchA, patchB, result) {
342
+ return {
343
+ runId,
344
+ patchA,
345
+ patchB,
346
+ result,
347
+ producedAt: new Date().toISOString()
348
+ };
349
+ }
350
+ //# sourceMappingURL=ast-merge.js.map
@@ -0,0 +1,12 @@
1
+ export interface TextMergeResult {
2
+ merged: string;
3
+ hasConflicts: boolean;
4
+ conflictMarkers: number;
5
+ }
6
+ /**
7
+ * Standard 3-way text merge.
8
+ * Applies diffs from base→sideA and base→sideB to reconstruct merged output.
9
+ * Emits `<<<<<<<` / `=======` / `>>>>>>>` markers only where both sides
10
+ * changed the same region differently.
11
+ */
12
+ export declare function textMerge(base: string, sideA: string, sideB: string): TextMergeResult;
@@ -0,0 +1,182 @@
1
+ // ─── 3-Way Text Merge ─────────────────────────────────────────────────────────
2
+ //
3
+ // Standard Myers / patience-style 3-way merge.
4
+ // Produces <<<<<< / ====== / >>>>>> conflict markers only on overlapping hunks.
5
+ // ─── LCS helpers ─────────────────────────────────────────────────────────────
6
+ /** Longest common subsequence of two string arrays. Returns the LCS array. */
7
+ function lcs(a, b) {
8
+ const m = a.length;
9
+ const n = b.length;
10
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
11
+ for (let i = 1; i <= m; i++) {
12
+ for (let j = 1; j <= n; j++) {
13
+ const aLine = a[i - 1];
14
+ const bLine = b[j - 1];
15
+ if (aLine !== undefined && bLine !== undefined && aLine === bLine) {
16
+ dp[i][j] = (dp[i - 1][j - 1] ?? 0) + 1;
17
+ }
18
+ else {
19
+ dp[i][j] = Math.max(dp[i - 1][j] ?? 0, dp[i][j - 1] ?? 0);
20
+ }
21
+ }
22
+ }
23
+ // Backtrack
24
+ const result = [];
25
+ let i = m;
26
+ let j = n;
27
+ while (i > 0 && j > 0) {
28
+ const aLine = a[i - 1];
29
+ const bLine = b[j - 1];
30
+ if (aLine !== undefined && bLine !== undefined && aLine === bLine) {
31
+ result.unshift(aLine);
32
+ i--;
33
+ j--;
34
+ }
35
+ else if ((dp[i - 1][j] ?? 0) >= (dp[i][j - 1] ?? 0)) {
36
+ i--;
37
+ }
38
+ else {
39
+ j--;
40
+ }
41
+ }
42
+ return result;
43
+ }
44
+ /** Compute line-level diff from base → target using LCS. */
45
+ function lineDiff(base, target) {
46
+ const common = lcs(base, target);
47
+ const ops = [];
48
+ let bi = 0;
49
+ let ti = 0;
50
+ let ci = 0;
51
+ while (bi < base.length || ti < target.length) {
52
+ const commonLine = common[ci];
53
+ const baseLine = base[bi];
54
+ const targetLine = target[ti];
55
+ if (commonLine !== undefined &&
56
+ baseLine !== undefined &&
57
+ targetLine !== undefined &&
58
+ baseLine === commonLine &&
59
+ targetLine === commonLine) {
60
+ ops.push({ type: "keep", line: commonLine });
61
+ bi++;
62
+ ti++;
63
+ ci++;
64
+ }
65
+ else if (targetLine !== undefined &&
66
+ (commonLine === undefined || targetLine !== commonLine)) {
67
+ ops.push({ type: "insert", line: targetLine });
68
+ ti++;
69
+ }
70
+ else {
71
+ ops.push({ type: "delete", line: baseLine ?? "" });
72
+ bi++;
73
+ }
74
+ }
75
+ return ops;
76
+ }
77
+ // ─── 3-Way merge ─────────────────────────────────────────────────────────────
78
+ /**
79
+ * Standard 3-way text merge.
80
+ * Applies diffs from base→sideA and base→sideB to reconstruct merged output.
81
+ * Emits `<<<<<<<` / `=======` / `>>>>>>>` markers only where both sides
82
+ * changed the same region differently.
83
+ */
84
+ export function textMerge(base, sideA, sideB) {
85
+ const baseLines = base.split("\n");
86
+ const aLines = sideA.split("\n");
87
+ const bLines = sideB.split("\n");
88
+ const aDiff = lineDiff(baseLines, aLines);
89
+ const bDiff = lineDiff(baseLines, bLines);
90
+ // Walk both diffs in parallel, tracking which lines each side changed
91
+ const merged = [];
92
+ let conflictMarkers = 0;
93
+ let ai = 0;
94
+ let bi = 0;
95
+ while (ai < aDiff.length || bi < bDiff.length) {
96
+ const aOp = aDiff[ai];
97
+ const bOp = bDiff[bi];
98
+ if (!aOp && bOp) {
99
+ if (bOp.type !== "delete")
100
+ merged.push(bOp.line);
101
+ bi++;
102
+ continue;
103
+ }
104
+ if (aOp && !bOp) {
105
+ if (aOp.type !== "delete")
106
+ merged.push(aOp.line);
107
+ ai++;
108
+ continue;
109
+ }
110
+ if (!aOp && !bOp)
111
+ break;
112
+ const currentA = aOp;
113
+ const currentB = bOp;
114
+ // Both sides keep the same line
115
+ if (currentA.type === "keep" && currentB.type === "keep" && currentA.line === currentB.line) {
116
+ merged.push(currentA.line);
117
+ ai++;
118
+ bi++;
119
+ continue;
120
+ }
121
+ // Only A changed
122
+ if (currentA.type !== "keep" && currentB.type === "keep") {
123
+ if (currentA.type === "insert")
124
+ merged.push(currentA.line);
125
+ // delete: skip
126
+ ai++;
127
+ continue;
128
+ }
129
+ // Only B changed
130
+ if (currentA.type === "keep" && currentB.type !== "keep") {
131
+ if (currentB.type === "insert")
132
+ merged.push(currentB.line);
133
+ // delete: skip
134
+ bi++;
135
+ continue;
136
+ }
137
+ // Both sides changed — collect diverging hunks
138
+ const aHunk = [];
139
+ const bHunk = [];
140
+ // Drain inserts from A
141
+ while (ai < aDiff.length && aDiff[ai].type === "insert") {
142
+ aHunk.push(aDiff[ai].line);
143
+ ai++;
144
+ }
145
+ // Skip deletes from A (base lines removed on A side)
146
+ while (ai < aDiff.length && aDiff[ai].type === "delete") {
147
+ ai++;
148
+ }
149
+ // Drain inserts from B
150
+ while (bi < bDiff.length && bDiff[bi].type === "insert") {
151
+ bHunk.push(bDiff[bi].line);
152
+ bi++;
153
+ }
154
+ // Skip deletes from B
155
+ while (bi < bDiff.length && bDiff[bi].type === "delete") {
156
+ bi++;
157
+ }
158
+ if (aHunk.length === 0 && bHunk.length === 0) {
159
+ // Pure deletes on both sides — nothing to emit
160
+ continue;
161
+ }
162
+ // If both hunks are identical, emit once (no conflict)
163
+ if (JSON.stringify(aHunk) === JSON.stringify(bHunk)) {
164
+ merged.push(...aHunk);
165
+ continue;
166
+ }
167
+ // Real conflict
168
+ conflictMarkers++;
169
+ merged.push("<<<<<<< sideA");
170
+ merged.push(...aHunk);
171
+ merged.push("=======");
172
+ merged.push(...bHunk);
173
+ merged.push(">>>>>>> sideB");
174
+ }
175
+ const mergedStr = merged.join("\n");
176
+ return {
177
+ merged: mergedStr,
178
+ hasConflicts: conflictMarkers > 0,
179
+ conflictMarkers
180
+ };
181
+ }
182
+ //# sourceMappingURL=text-merge.js.map
@@ -0,0 +1,45 @@
1
+ export interface SpanContext {
2
+ spanId: string;
3
+ traceId: string;
4
+ startTime: string;
5
+ endTime?: string;
6
+ attributes: Record<string, string | number | boolean>;
7
+ name: SpanName;
8
+ status: "OK" | "ERROR" | "UNSET";
9
+ }
10
+ export type SpanName = "martin.run" | "martin.attempt" | "martin.adapter_call" | "martin.patch_proof" | "martin.leash_check" | "martin.grounding_scan" | "martin.rollback" | "martin.gateway_policy" | "martin.router_decision";
11
+ export declare const VALID_SPAN_NAMES: readonly SpanName[];
12
+ export interface Tracer {
13
+ startSpan(name: SpanName, attributes?: Record<string, string | number | boolean>): SpanContext;
14
+ endSpan(span: SpanContext, status?: "OK" | "ERROR"): SpanContext;
15
+ recordException(span: SpanContext, error: Error): SpanContext;
16
+ }
17
+ /**
18
+ * Returns all spans collected in the current process.
19
+ * Useful for test assertions.
20
+ */
21
+ export declare function getCollectedSpans(): SpanContext[];
22
+ /**
23
+ * Resets the in-memory span array.
24
+ * Call this in test beforeEach/afterEach to isolate tests.
25
+ */
26
+ export declare function clearCollectedSpans(): void;
27
+ export interface TracerOptions {
28
+ endpoint?: string;
29
+ }
30
+ /**
31
+ * Creates a tracer instance. When endpoint is undefined or empty, uses the
32
+ * noop implementation (stores spans in memory, no network calls).
33
+ * When endpoint is set, the same noop is used — actual OTLP export is a
34
+ * future enhancement.
35
+ */
36
+ export declare function createTracer(options?: TracerOptions): Tracer;
37
+ /**
38
+ * Returns the default module-level tracer singleton.
39
+ * Initializes with a noop tracer if not already set.
40
+ */
41
+ export declare function getTracer(): Tracer;
42
+ /**
43
+ * Resets the default tracer singleton (for testing).
44
+ */
45
+ export declare function resetDefaultTracer(): void;