ghagga-core 2.9.1 → 3.0.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 (175) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agents/consensus.d.ts.map +1 -1
  3. package/dist/agents/consensus.js +7 -2
  4. package/dist/agents/consensus.js.map +1 -1
  5. package/dist/agents/diagnostic.d.ts.map +1 -1
  6. package/dist/agents/diagnostic.js +7 -2
  7. package/dist/agents/diagnostic.js.map +1 -1
  8. package/dist/agents/fan-out-lenses.d.ts.map +1 -1
  9. package/dist/agents/fan-out-lenses.js +7 -2
  10. package/dist/agents/fan-out-lenses.js.map +1 -1
  11. package/dist/agents/prompts.d.ts +49 -1
  12. package/dist/agents/prompts.d.ts.map +1 -1
  13. package/dist/agents/prompts.js +133 -5
  14. package/dist/agents/prompts.js.map +1 -1
  15. package/dist/agents/simple.d.ts +1 -1
  16. package/dist/agents/simple.d.ts.map +1 -1
  17. package/dist/agents/simple.js +6 -4
  18. package/dist/agents/simple.js.map +1 -1
  19. package/dist/agents/workflow.d.ts.map +1 -1
  20. package/dist/agents/workflow.js +13 -4
  21. package/dist/agents/workflow.js.map +1 -1
  22. package/dist/critique/critique.d.ts.map +1 -1
  23. package/dist/critique/critique.js +14 -6
  24. package/dist/critique/critique.js.map +1 -1
  25. package/dist/diff/index.d.ts +12 -0
  26. package/dist/diff/index.d.ts.map +1 -0
  27. package/dist/diff/index.js +11 -0
  28. package/dist/diff/index.js.map +1 -0
  29. package/dist/diff/parse.d.ts +41 -0
  30. package/dist/diff/parse.d.ts.map +1 -0
  31. package/dist/diff/parse.js +303 -0
  32. package/dist/diff/parse.js.map +1 -0
  33. package/dist/diff/types.d.ts +106 -0
  34. package/dist/diff/types.d.ts.map +1 -0
  35. package/dist/diff/types.js +23 -0
  36. package/dist/diff/types.js.map +1 -0
  37. package/dist/embed.d.ts +5 -2
  38. package/dist/embed.d.ts.map +1 -1
  39. package/dist/embed.js +7 -3
  40. package/dist/embed.js.map +1 -1
  41. package/dist/enhance/prompt.d.ts +5 -1
  42. package/dist/enhance/prompt.d.ts.map +1 -1
  43. package/dist/enhance/prompt.js +9 -2
  44. package/dist/enhance/prompt.js.map +1 -1
  45. package/dist/format.d.ts +31 -0
  46. package/dist/format.d.ts.map +1 -1
  47. package/dist/format.js +256 -15
  48. package/dist/format.js.map +1 -1
  49. package/dist/index.d.ts +2 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/memory/pageindex/index.d.ts +2 -2
  54. package/dist/memory/pageindex/index.d.ts.map +1 -1
  55. package/dist/memory/pageindex/index.js.map +1 -1
  56. package/dist/memory/pageindex/service.d.ts +10 -1
  57. package/dist/memory/pageindex/service.d.ts.map +1 -1
  58. package/dist/memory/pageindex/service.js +2 -2
  59. package/dist/memory/pageindex/service.js.map +1 -1
  60. package/dist/memory/persist.d.ts.map +1 -1
  61. package/dist/memory/persist.js +10 -3
  62. package/dist/memory/persist.js.map +1 -1
  63. package/dist/memory/privacy.d.ts.map +1 -1
  64. package/dist/memory/privacy.js +45 -6
  65. package/dist/memory/privacy.js.map +1 -1
  66. package/dist/memory/sqlite.d.ts +1 -13
  67. package/dist/memory/sqlite.d.ts.map +1 -1
  68. package/dist/memory/sqlite.js +45 -27
  69. package/dist/memory/sqlite.js.map +1 -1
  70. package/dist/memory/taxonomy.d.ts.map +1 -1
  71. package/dist/memory/taxonomy.js +6 -1
  72. package/dist/memory/taxonomy.js.map +1 -1
  73. package/dist/pipeline/degrade.d.ts +61 -0
  74. package/dist/pipeline/degrade.d.ts.map +1 -0
  75. package/dist/pipeline/degrade.js +58 -0
  76. package/dist/pipeline/degrade.js.map +1 -0
  77. package/dist/pipeline/enrich.d.ts +29 -0
  78. package/dist/pipeline/enrich.d.ts.map +1 -0
  79. package/dist/pipeline/enrich.js +271 -0
  80. package/dist/pipeline/enrich.js.map +1 -0
  81. package/dist/pipeline/execute.d.ts +22 -0
  82. package/dist/pipeline/execute.d.ts.map +1 -0
  83. package/dist/pipeline/execute.js +250 -0
  84. package/dist/pipeline/execute.js.map +1 -0
  85. package/dist/pipeline/finalize.d.ts +26 -0
  86. package/dist/pipeline/finalize.d.ts.map +1 -0
  87. package/dist/pipeline/finalize.js +52 -0
  88. package/dist/pipeline/finalize.js.map +1 -0
  89. package/dist/pipeline/gather-context.d.ts +25 -0
  90. package/dist/pipeline/gather-context.d.ts.map +1 -0
  91. package/dist/pipeline/gather-context.js +169 -0
  92. package/dist/pipeline/gather-context.js.map +1 -0
  93. package/dist/pipeline/gather-safe.d.ts +39 -0
  94. package/dist/pipeline/gather-safe.d.ts.map +1 -0
  95. package/dist/pipeline/gather-safe.js +127 -0
  96. package/dist/pipeline/gather-safe.js.map +1 -0
  97. package/dist/pipeline/prepare-graph.d.ts +54 -0
  98. package/dist/pipeline/prepare-graph.d.ts.map +1 -0
  99. package/dist/pipeline/prepare-graph.js +174 -0
  100. package/dist/pipeline/prepare-graph.js.map +1 -0
  101. package/dist/pipeline/prepare.d.ts +40 -0
  102. package/dist/pipeline/prepare.d.ts.map +1 -0
  103. package/dist/pipeline/prepare.js +233 -0
  104. package/dist/pipeline/prepare.js.map +1 -0
  105. package/dist/pipeline/providers.d.ts +54 -0
  106. package/dist/pipeline/providers.d.ts.map +1 -0
  107. package/dist/pipeline/providers.js +163 -0
  108. package/dist/pipeline/providers.js.map +1 -0
  109. package/dist/pipeline/results.d.ts +35 -0
  110. package/dist/pipeline/results.d.ts.map +1 -0
  111. package/dist/pipeline/results.js +122 -0
  112. package/dist/pipeline/results.js.map +1 -0
  113. package/dist/pipeline/state.d.ts +92 -0
  114. package/dist/pipeline/state.d.ts.map +1 -0
  115. package/dist/pipeline/state.js +13 -0
  116. package/dist/pipeline/state.js.map +1 -0
  117. package/dist/pipeline.d.ts +10 -9
  118. package/dist/pipeline.d.ts.map +1 -1
  119. package/dist/pipeline.js +36 -1213
  120. package/dist/pipeline.js.map +1 -1
  121. package/dist/providers/gateway.d.ts.map +1 -1
  122. package/dist/providers/gateway.js +8 -0
  123. package/dist/providers/gateway.js.map +1 -1
  124. package/dist/recursive/index.d.ts +1 -0
  125. package/dist/recursive/index.d.ts.map +1 -1
  126. package/dist/recursive/index.js +7 -3
  127. package/dist/recursive/index.js.map +1 -1
  128. package/dist/recursive/patch-extractor.d.ts +58 -6
  129. package/dist/recursive/patch-extractor.d.ts.map +1 -1
  130. package/dist/recursive/patch-extractor.js +207 -26
  131. package/dist/recursive/patch-extractor.js.map +1 -1
  132. package/dist/sanitize.d.ts +51 -0
  133. package/dist/sanitize.d.ts.map +1 -0
  134. package/dist/sanitize.js +90 -0
  135. package/dist/sanitize.js.map +1 -0
  136. package/dist/scope/diff-mapper.d.ts +12 -0
  137. package/dist/scope/diff-mapper.d.ts.map +1 -1
  138. package/dist/scope/diff-mapper.js +25 -18
  139. package/dist/scope/diff-mapper.js.map +1 -1
  140. package/dist/scope/entity-diff.d.ts +21 -4
  141. package/dist/scope/entity-diff.d.ts.map +1 -1
  142. package/dist/scope/entity-diff.js +132 -34
  143. package/dist/scope/entity-diff.js.map +1 -1
  144. package/dist/scope/types.d.ts +10 -0
  145. package/dist/scope/types.d.ts.map +1 -1
  146. package/dist/semantic-diff/index.d.ts +25 -2
  147. package/dist/semantic-diff/index.d.ts.map +1 -1
  148. package/dist/semantic-diff/index.js +147 -53
  149. package/dist/semantic-diff/index.js.map +1 -1
  150. package/dist/tools/gitleaks-config.toml +35 -0
  151. package/dist/tools/plugins/gitleaks.d.ts +10 -0
  152. package/dist/tools/plugins/gitleaks.d.ts.map +1 -1
  153. package/dist/tools/plugins/gitleaks.js +29 -2
  154. package/dist/tools/plugins/gitleaks.js.map +1 -1
  155. package/dist/tools/plugins/semgrep.d.ts +11 -0
  156. package/dist/tools/plugins/semgrep.d.ts.map +1 -1
  157. package/dist/tools/plugins/semgrep.js +30 -1
  158. package/dist/tools/plugins/semgrep.js.map +1 -1
  159. package/dist/tools/semgrep-rules.yml +305 -0
  160. package/dist/types.d.ts +51 -1
  161. package/dist/types.d.ts.map +1 -1
  162. package/dist/types.js.map +1 -1
  163. package/dist/utils/diff.d.ts +22 -2
  164. package/dist/utils/diff.d.ts.map +1 -1
  165. package/dist/utils/diff.js +36 -40
  166. package/dist/utils/diff.js.map +1 -1
  167. package/package.json +21 -22
  168. package/dist/providers/fallback.d.ts +0 -54
  169. package/dist/providers/fallback.d.ts.map +0 -1
  170. package/dist/providers/fallback.js +0 -102
  171. package/dist/providers/fallback.js.map +0 -1
  172. package/dist/providers/index.d.ts +0 -49
  173. package/dist/providers/index.d.ts.map +0 -1
  174. package/dist/providers/index.js +0 -146
  175. package/dist/providers/index.js.map +0 -1
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Sanitization for LLM/tool-derived content before it is posted to GitHub.
3
+ *
4
+ * Review output (summary, finding messages, file paths, model labels, ...)
5
+ * is ultimately derived from attacker-controlled PR diffs, so a malicious PR
6
+ * can steer the LLM into emitting payloads that the bot would then post
7
+ * verbatim under its own identity:
8
+ *
9
+ * - "@org/everyone" mention spam (notification bombing)
10
+ * - hidden HTML comments (prompt-injection payloads for OTHER bots
11
+ * reading the thread)
12
+ * - raw HTML (<script>, <img>, hidden text)
13
+ * - table-breaking pipes/newlines that smuggle content outside its cell
14
+ * - megabyte-scale output (comment flooding)
15
+ *
16
+ * These helpers neutralize untrusted VARIABLES only. Trusted markdown
17
+ * literals produced by the formatters themselves (headers, table syntax,
18
+ * the ghagga comment marker) must NOT be passed through these functions.
19
+ */
20
+ /** Default cap for free-form markdown text (e.g. review summary). */
21
+ export declare const SANITIZE_DEFAULT_TEXT_MAX = 2000;
22
+ /** Default cap for a single markdown table cell. */
23
+ export declare const SANITIZE_DEFAULT_CELL_MAX = 500;
24
+ /**
25
+ * Sanitize untrusted text for inclusion in a GitHub markdown comment.
26
+ *
27
+ * - strips HTML comments (`<!-- ... -->`, including unterminated openers)
28
+ * - escapes `<` as `&lt;` (blocks raw HTML / hidden payloads)
29
+ * - inserts a zero-width space after every `@` (neutralizes mentions
30
+ * without visibly altering the text)
31
+ * - collapses control characters (except \n and \t) to spaces
32
+ * - enforces a maximum length, appending an ellipsis when truncated
33
+ */
34
+ export declare function sanitizeMarkdownText(input: string, maxLength?: number): string;
35
+ /**
36
+ * Sanitize untrusted text for a single GitHub markdown table cell.
37
+ *
38
+ * Applies `sanitizeMarkdownText` and additionally escapes pipes and
39
+ * replaces newlines with spaces so the content cannot break out of its
40
+ * cell (or out of the table entirely).
41
+ */
42
+ export declare function sanitizeTableCell(input: string, maxLength?: number): string;
43
+ /**
44
+ * Validate a GitHub login (username) before @-mentioning it.
45
+ *
46
+ * GitHub rules: 1-39 chars, alphanumeric or single hyphens, may not start
47
+ * or end with a hyphen. Anything else (e.g. "org/everyone", "x[bot]") is
48
+ * rejected — callers should OMIT the mention rather than escape it.
49
+ */
50
+ export declare function isValidGithubLogin(login: string): boolean;
51
+ //# sourceMappingURL=sanitize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAOH,qEAAqE;AACrE,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,oDAAoD;AACpD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAI7C;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAkC,GAC5C,MAAM,CAqCR;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAkC,GAC5C,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Sanitization for LLM/tool-derived content before it is posted to GitHub.
3
+ *
4
+ * Review output (summary, finding messages, file paths, model labels, ...)
5
+ * is ultimately derived from attacker-controlled PR diffs, so a malicious PR
6
+ * can steer the LLM into emitting payloads that the bot would then post
7
+ * verbatim under its own identity:
8
+ *
9
+ * - "@org/everyone" mention spam (notification bombing)
10
+ * - hidden HTML comments (prompt-injection payloads for OTHER bots
11
+ * reading the thread)
12
+ * - raw HTML (<script>, <img>, hidden text)
13
+ * - table-breaking pipes/newlines that smuggle content outside its cell
14
+ * - megabyte-scale output (comment flooding)
15
+ *
16
+ * These helpers neutralize untrusted VARIABLES only. Trusted markdown
17
+ * literals produced by the formatters themselves (headers, table syntax,
18
+ * the ghagga comment marker) must NOT be passed through these functions.
19
+ */
20
+ // ─── Constants ──────────────────────────────────────────────────
21
+ /** Zero-width space — breaks GitHub @-mention linkification invisibly. */
22
+ const ZERO_WIDTH_SPACE = '\u200b';
23
+ /** Default cap for free-form markdown text (e.g. review summary). */
24
+ export const SANITIZE_DEFAULT_TEXT_MAX = 2000;
25
+ /** Default cap for a single markdown table cell. */
26
+ export const SANITIZE_DEFAULT_CELL_MAX = 500;
27
+ // ─── Sanitizers ─────────────────────────────────────────────────
28
+ /**
29
+ * Sanitize untrusted text for inclusion in a GitHub markdown comment.
30
+ *
31
+ * - strips HTML comments (`<!-- ... -->`, including unterminated openers)
32
+ * - escapes `<` as `&lt;` (blocks raw HTML / hidden payloads)
33
+ * - inserts a zero-width space after every `@` (neutralizes mentions
34
+ * without visibly altering the text)
35
+ * - collapses control characters (except \n and \t) to spaces
36
+ * - enforces a maximum length, appending an ellipsis when truncated
37
+ */
38
+ export function sanitizeMarkdownText(input, maxLength = SANITIZE_DEFAULT_TEXT_MAX) {
39
+ let s = input;
40
+ // HTML comments can carry hidden instructions for other bots/agents
41
+ // reading the thread. Strip closed comments, then any unterminated opener.
42
+ s = s.replace(/<!--[\s\S]*?-->/g, '');
43
+ s = s.replace(/<!--/g, '');
44
+ // Escape raw HTML. This also neutralizes any comment-like remnants.
45
+ s = s.replace(/</g, '&lt;');
46
+ // Neutralize @-mentions: a zero-width space after '@' prevents GitHub
47
+ // from linkifying/notifying while keeping the text readable.
48
+ s = s.replace(/@/g, `@${ZERO_WIDTH_SPACE}`);
49
+ // Collapse control chars (C0 except \t, \n, \r — plus DEL) to spaces;
50
+ // carriage returns are normalized to plain newlines.
51
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: deliberate control-char filter
52
+ s = s.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ' ');
53
+ s = s.replace(/\r\n?/g, '\n');
54
+ if (s.length > maxLength) {
55
+ let truncated = s.slice(0, maxLength);
56
+ // s.slice operates on UTF-16 code units, so the cut can land in the
57
+ // MIDDLE of a surrogate pair (e.g. an emoji like 😀 = U+1F600), leaving a
58
+ // lone high surrogate at the end. A lone surrogate renders as � and can
59
+ // corrupt downstream consumers. If the last code unit is a high surrogate
60
+ // (0xD800–0xDBFF) with no matching low surrogate following, drop it before
61
+ // appending the ellipsis.
62
+ const lastUnit = truncated.charCodeAt(truncated.length - 1);
63
+ if (lastUnit >= 0xd800 && lastUnit <= 0xdbff) {
64
+ truncated = truncated.slice(0, -1);
65
+ }
66
+ s = `${truncated}…`;
67
+ }
68
+ return s;
69
+ }
70
+ /**
71
+ * Sanitize untrusted text for a single GitHub markdown table cell.
72
+ *
73
+ * Applies `sanitizeMarkdownText` and additionally escapes pipes and
74
+ * replaces newlines with spaces so the content cannot break out of its
75
+ * cell (or out of the table entirely).
76
+ */
77
+ export function sanitizeTableCell(input, maxLength = SANITIZE_DEFAULT_CELL_MAX) {
78
+ return sanitizeMarkdownText(input, maxLength).replace(/\|/g, '\\|').replace(/\n/g, ' ');
79
+ }
80
+ /**
81
+ * Validate a GitHub login (username) before @-mentioning it.
82
+ *
83
+ * GitHub rules: 1-39 chars, alphanumeric or single hyphens, may not start
84
+ * or end with a hyphen. Anything else (e.g. "org/everyone", "x[bot]") is
85
+ * rejected — callers should OMIT the mention rather than escape it.
86
+ */
87
+ export function isValidGithubLogin(login) {
88
+ return /^[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}$/.test(login);
89
+ }
90
+ //# sourceMappingURL=sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,mEAAmE;AAEnE,0EAA0E;AAC1E,MAAM,gBAAgB,GAAG,QAAQ,CAAC;AAElC,qEAAqE;AACrE,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAE9C,oDAAoD;AACpD,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAE7C,mEAAmE;AAEnE;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAa,EACb,YAAoB,yBAAyB;IAE7C,IAAI,CAAC,GAAG,KAAK,CAAC;IAEd,oEAAoE;IACpE,2EAA2E;IAC3E,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAE3B,oEAAoE;IACpE,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAE5B,sEAAsE;IACtE,6DAA6D;IAC7D,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,gBAAgB,EAAE,CAAC,CAAC;IAE5C,sEAAsE;IACtE,qDAAqD;IACrD,0FAA0F;IAC1F,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAC;IACxD,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAE9B,IAAI,CAAC,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QACzB,IAAI,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QACtC,oEAAoE;QACpE,0EAA0E;QAC1E,wEAAwE;QACxE,0EAA0E;QAC1E,2EAA2E;QAC3E,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC5D,IAAI,QAAQ,IAAI,MAAM,IAAI,QAAQ,IAAI,MAAM,EAAE,CAAC;YAC7C,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACrC,CAAC;QACD,CAAC,GAAG,GAAG,SAAS,GAAG,CAAC;IACtB,CAAC;IAED,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,YAAoB,yBAAyB;IAE7C,OAAO,oBAAoB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC1F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,qDAAqD,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC"}
@@ -10,6 +10,18 @@ import type { AffectedSymbol, DiffHunk, SymbolInfo } from './types.js';
10
10
  /**
11
11
  * Parse unified diff content to extract hunk line ranges.
12
12
  *
13
+ * Thin adapter over the unified parser (`src/diff/parse.ts`): model hunks
14
+ * are mapped down to the 4 captures this module has always returned. Bare
15
+ * hunk fragments WITHOUT any `diff --git` header (a documented input shape —
16
+ * "raw unified diff content for a single file") land in the model's
17
+ * `preamble`, so those lines are scanned with the shared `matchHunkHeader`
18
+ * (THE single hunk-header regex of core, spec R8).
19
+ *
20
+ * KNOWN synthetic-only divergence vs the pre-adapter regex (`\s+`
21
+ * separators): headers with tabs or multiple spaces (`@@ -1,2 +3,4 @@`)
22
+ * no longer match. git only ever emits the single-space form; pinned in
23
+ * `src/diff/__tests__/parity-parse-hunks.test.ts`.
24
+ *
13
25
  * @param diffContent - Raw unified diff content for a single file
14
26
  * @returns Array of DiffHunk objects with line ranges
15
27
  */
@@ -1 +1 @@
1
- {"version":3,"file":"diff-mapper.d.ts","sourceRoot":"","sources":["../../src/scope/diff-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAevE;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,EAAE,CAiB1D;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAqB3F"}
1
+ {"version":3,"file":"diff-mapper.d.ts","sourceRoot":"","sources":["../../src/scope/diff-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIvE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,EAAE,CAqB1D;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAqB3F"}
@@ -6,34 +6,41 @@
6
6
  *
7
7
  * Pure functions with no side effects.
8
8
  */
9
+ import { matchHunkHeader, parseUnifiedDiff } from '../diff/index.js';
9
10
  // ─── Hunk Parsing ──────────────────────────────────────────────
10
- /**
11
- * Regex to match unified diff hunk headers.
12
- * Format: @@ -oldStart[,oldCount] +newStart[,newCount] @@
13
- *
14
- * Examples:
15
- * @@ -10,5 +10,7 @@
16
- * @@ -1 +1,3 @@
17
- * @@ -0,0 +1,20 @@
18
- */
19
- const HUNK_HEADER_RE = /^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@/;
20
11
  /**
21
12
  * Parse unified diff content to extract hunk line ranges.
22
13
  *
14
+ * Thin adapter over the unified parser (`src/diff/parse.ts`): model hunks
15
+ * are mapped down to the 4 captures this module has always returned. Bare
16
+ * hunk fragments WITHOUT any `diff --git` header (a documented input shape —
17
+ * "raw unified diff content for a single file") land in the model's
18
+ * `preamble`, so those lines are scanned with the shared `matchHunkHeader`
19
+ * (THE single hunk-header regex of core, spec R8).
20
+ *
21
+ * KNOWN synthetic-only divergence vs the pre-adapter regex (`\s+`
22
+ * separators): headers with tabs or multiple spaces (`@@ -1,2 +3,4 @@`)
23
+ * no longer match. git only ever emits the single-space form; pinned in
24
+ * `src/diff/__tests__/parity-parse-hunks.test.ts`.
25
+ *
23
26
  * @param diffContent - Raw unified diff content for a single file
24
27
  * @returns Array of DiffHunk objects with line ranges
25
28
  */
26
29
  export function parseHunks(diffContent) {
30
+ const { preamble, files } = parseUnifiedDiff(diffContent);
27
31
  const hunks = [];
28
- const lines = diffContent.split('\n');
29
- for (const line of lines) {
30
- const match = HUNK_HEADER_RE.exec(line);
31
- if (match) {
32
+ for (const line of preamble) {
33
+ const match = matchHunkHeader(line);
34
+ if (match)
35
+ hunks.push(match);
36
+ }
37
+ for (const file of files) {
38
+ for (const hunk of file.hunks) {
32
39
  hunks.push({
33
- oldStart: Number.parseInt(match[1], 10),
34
- oldCount: match[2] !== undefined ? Number.parseInt(match[2], 10) : 1,
35
- newStart: Number.parseInt(match[3], 10),
36
- newCount: match[4] !== undefined ? Number.parseInt(match[4], 10) : 1,
40
+ oldStart: hunk.oldStart,
41
+ oldCount: hunk.oldCount,
42
+ newStart: hunk.newStart,
43
+ newCount: hunk.newCount,
37
44
  });
38
45
  }
39
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"diff-mapper.js","sourceRoot":"","sources":["../../src/scope/diff-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,kEAAkE;AAElE;;;;;;;;GAQG;AACH,MAAM,cAAc,GAAG,mDAAmD,CAAC;AAE3E;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,WAAmB;IAC5C,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC;gBACT,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC;gBACxC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC;gBACxC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;aACrE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kEAAkE;AAElE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB,EAAE,OAAqB;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE1D,MAAM,QAAQ,GAAqB,EAAE,CAAC;IAEtC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACxC,aAAa,CACX,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,EAC9C,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,OAAO,CACf,CACF,CAAC;QAEF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,kEAAkE;AAElE;;;;;GAKG;AACH,SAAS,aAAa,CAAC,MAAc,EAAE,IAAY,EAAE,MAAc,EAAE,IAAY;IAC/E,OAAO,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"diff-mapper.js","sourceRoot":"","sources":["../../src/scope/diff-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGrE,kEAAkE;AAElE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,UAAU,CAAC,WAAmB;IAC5C,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAe,EAAE,CAAC;IAE7B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC;gBACT,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kEAAkE;AAElE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB,EAAE,OAAqB;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE1D,MAAM,QAAQ,GAAqB,EAAE,CAAC;IAEtC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACxC,aAAa,CACX,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,EAC9C,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,OAAO,CACf,CACF,CAAC;QAEF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,kEAAkE;AAElE;;;;;GAKG;AACH,SAAS,aAAa,CAAC,MAAc,EAAE,IAAY,EAAE,MAAc,EAAE,IAAY;IAC/E,OAAO,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC;AAC1C,CAAC"}
@@ -12,11 +12,28 @@
12
12
  import type { AffectedSymbol, EntityChange, EntityDiffOptions, RenameMatch, SymbolInfo } from './types.js';
13
13
  /**
14
14
  * Extract the actual addition/deletion lines from a diff that fall
15
- * within a symbol's line range (new-side lines for +, old-side for -).
15
+ * within a symbol's line range. Symbol ranges are NEW-side line numbers
16
+ * (contract of mapDiffToSymbols: symbols come from the NEW version of the
17
+ * file), so BOTH additions and deletions are attributed by the live
18
+ * new-side position — for a deletion, the new-side line where the removal
19
+ * happened (CORE-M9 fix; previously deletions were compared by their
20
+ * OLD-side line number against the new-side ranges, mis-attributing them
21
+ * whenever earlier hunks made old/new drift apart).
16
22
  *
17
- * Walks the diff content, tracking the current new-side line number
18
- * via hunk headers and line prefixes, and collects lines that overlap
19
- * with the symbol's [startLine, endLine] range.
23
+ * Thin adapter over the unified parser (`src/diff/parse.ts`): the walk runs
24
+ * over the byte-exact line stream `[...preamble, ...files[].rawLines]`
25
+ * (≡ `diffContent.split('\n')` by the model's R2 reconstruction invariant),
26
+ * NOT over `hunk.lines` — preserving the historical behaviors the structured
27
+ * hunks do not carry: bare hunk fragments without a `diff --git` header
28
+ * (kept in `preamble`), orphan +/- tails after a genuine empty line
29
+ * mid-hunk, and metadata lines counted as context. Hunk headers are
30
+ * detected with the shared `matchHunkHeader` (THE single hunk-header regex
31
+ * of core, spec R8).
32
+ *
33
+ * KNOWN synthetic-only divergence vs the pre-adapter `\s+` regex: hunk
34
+ * headers with tabs/multiple spaces no longer reset the position counter
35
+ * (git only emits the single-space form); pinned in
36
+ * `src/diff/__tests__/parity-extract-entity-diff-lines.test.ts`.
20
37
  *
21
38
  * @param diffContent - Raw unified diff content for a single file
22
39
  * @param symbol - The symbol whose range to extract lines for
@@ -1 +1 @@
1
- {"version":3,"file":"entity-diff.d.ts","sourceRoot":"","sources":["../../src/scope/entity-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,UAAU,EACX,MAAM,YAAY,CAAC;AAuBpB;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAyCxF;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,cAAc,EAAE,EACjC,WAAW,EAAE,MAAM,GAClB,YAAY,EAAE,CAqBhB;AA6CD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,UAAU,EAAE,EAC5B,YAAY,EAAE,UAAU,EAAE,EAC1B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,WAAW,EAAE,CAiDf;AAID;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAE1E"}
1
+ {"version":3,"file":"entity-diff.d.ts","sourceRoot":"","sources":["../../src/scope/entity-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,UAAU,EACX,MAAM,YAAY,CAAC;AAuBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAoCxF;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,cAAc,EAAE,EACjC,WAAW,EAAE,MAAM,GAClB,YAAY,EAAE,CAqBhB;AAoID;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,cAAc,EAAE,UAAU,EAAE,EAC5B,YAAY,EAAE,UAAU,EAAE,EAC1B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,WAAW,EAAE,CAuDf;AAID;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAE1E"}
@@ -9,6 +9,7 @@
9
9
  *
10
10
  * Pure functions with no side effects.
11
11
  */
12
+ import { matchHunkHeader, parseUnifiedDiff } from '../diff/index.js';
12
13
  import { ENTITY_CHANGE_KIND } from './types.js';
13
14
  // ─── Constants ────────────────────────────────────────────────
14
15
  const DEFAULT_SIMILARITY_THRESHOLD = 0.9;
@@ -28,52 +29,65 @@ const COSMETIC_LINE_PATTERNS = [
28
29
  // ─── Extract Diff Lines ───────────────────────────────────────
29
30
  /**
30
31
  * Extract the actual addition/deletion lines from a diff that fall
31
- * within a symbol's line range (new-side lines for +, old-side for -).
32
+ * within a symbol's line range. Symbol ranges are NEW-side line numbers
33
+ * (contract of mapDiffToSymbols: symbols come from the NEW version of the
34
+ * file), so BOTH additions and deletions are attributed by the live
35
+ * new-side position — for a deletion, the new-side line where the removal
36
+ * happened (CORE-M9 fix; previously deletions were compared by their
37
+ * OLD-side line number against the new-side ranges, mis-attributing them
38
+ * whenever earlier hunks made old/new drift apart).
32
39
  *
33
- * Walks the diff content, tracking the current new-side line number
34
- * via hunk headers and line prefixes, and collects lines that overlap
35
- * with the symbol's [startLine, endLine] range.
40
+ * Thin adapter over the unified parser (`src/diff/parse.ts`): the walk runs
41
+ * over the byte-exact line stream `[...preamble, ...files[].rawLines]`
42
+ * (≡ `diffContent.split('\n')` by the model's R2 reconstruction invariant),
43
+ * NOT over `hunk.lines` — preserving the historical behaviors the structured
44
+ * hunks do not carry: bare hunk fragments without a `diff --git` header
45
+ * (kept in `preamble`), orphan +/- tails after a genuine empty line
46
+ * mid-hunk, and metadata lines counted as context. Hunk headers are
47
+ * detected with the shared `matchHunkHeader` (THE single hunk-header regex
48
+ * of core, spec R8).
49
+ *
50
+ * KNOWN synthetic-only divergence vs the pre-adapter `\s+` regex: hunk
51
+ * headers with tabs/multiple spaces no longer reset the position counter
52
+ * (git only emits the single-space form); pinned in
53
+ * `src/diff/__tests__/parity-extract-entity-diff-lines.test.ts`.
36
54
  *
37
55
  * @param diffContent - Raw unified diff content for a single file
38
56
  * @param symbol - The symbol whose range to extract lines for
39
57
  * @returns Array of raw diff lines (with +/- prefix) within the symbol range
40
58
  */
41
59
  export function extractEntityDiffLines(diffContent, symbol) {
42
- const lines = diffContent.split('\n');
60
+ const { preamble, files } = parseUnifiedDiff(diffContent);
61
+ const stream = [...preamble, ...files.flatMap((f) => f.rawLines)];
43
62
  const result = [];
44
63
  let currentNewLine = 0;
45
- let currentOldLine = 0;
46
64
  let inHunk = false;
47
- const HUNK_RE = /^@@\s+-(\d+)(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s+@@/;
48
- for (const line of lines) {
49
- const hunkMatch = HUNK_RE.exec(line);
50
- if (hunkMatch) {
51
- currentOldLine = Number.parseInt(hunkMatch[1], 10);
52
- currentNewLine = Number.parseInt(hunkMatch[2], 10);
65
+ for (const line of stream) {
66
+ const hunk = matchHunkHeader(line);
67
+ if (hunk) {
68
+ currentNewLine = hunk.newStart;
53
69
  inHunk = true;
54
70
  continue;
55
71
  }
56
72
  if (!inHunk)
57
73
  continue;
58
74
  if (line.startsWith('+') && !line.startsWith('+++')) {
59
- // Addition — tracked on new-side line number
75
+ // Addition — lives at the current new-side line, then advances it.
60
76
  if (currentNewLine >= symbol.startLine && currentNewLine <= symbol.endLine) {
61
77
  result.push(line);
62
78
  }
63
79
  currentNewLine++;
64
80
  }
65
81
  else if (line.startsWith('-') && !line.startsWith('---')) {
66
- // Deletion — tracked on old-side line number but we include if
67
- // it maps to the symbol's range (using new-side context position)
68
- if (currentOldLine >= symbol.startLine && currentOldLine <= symbol.endLine) {
82
+ // Deletion — attributed to the new-side position where the removal
83
+ // happened (CORE-M9). A deletion does NOT advance the new side.
84
+ if (currentNewLine >= symbol.startLine && currentNewLine <= symbol.endLine) {
69
85
  result.push(line);
70
86
  }
71
- currentOldLine++;
72
87
  }
73
88
  else {
74
- // Context line — advances both counters
89
+ // Context (or any other) line — advances the new-side position.
75
90
  currentNewLine++;
76
- currentOldLine++;
77
91
  }
78
92
  }
79
93
  return result;
@@ -134,28 +148,106 @@ function normalizeBody(body) {
134
148
  .trim();
135
149
  }
136
150
  /**
137
- * Compute similarity ratio between two strings (0.0–1.0).
138
- * Uses a simple longest-common-subsequence ratio approach.
151
+ * Defensive cap on LCS input size (CORE-M8). Inputs are normalized entity
152
+ * bodies from a diff, typically well under this. The DP below is O(n*m)
153
+ * time, so two pathological ~100 KB bodies (e.g. a class spanning a whole
154
+ * generated file) would cost ~10^10 cell updates. Beyond the cap the DP
155
+ * compares the first MAX chars of each body only, but the similarity
156
+ * denominator keeps the ORIGINAL lengths — so two giant bodies identical
157
+ * only within the capped prefix score at most `cap / max(original lens)`
158
+ * (e.g. two 15k bodies sharing only their first 10k score ≤ 0.67), never a
159
+ * false 1.0. Trade-off: similarity living past the cap is invisible to the
160
+ * DP, so genuinely-similar giant pairs may be under-scored and missed —
161
+ * degraded detection on pathological inputs, never unbounded CPU.
162
+ */
163
+ const MAX_SIMILARITY_INPUT_LENGTH = 10_000;
164
+ /**
165
+ * Default total LCS DP work budget for one detectRenames call (CORE-M8
166
+ * fix-forward). detectRenames compares every removed×added pair and each
167
+ * pair that reaches the DP costs n*m cell updates (n, m ≤ the 10k input
168
+ * cap, so a single worst-case pair is 10^8 cells ≈ a few hundred ms).
169
+ * 200M cells keeps the absolute worst case at low single-digit seconds on
170
+ * commodity hardware while covering thousands of realistic pairs (~2000
171
+ * pairs of 300×300-char bodies). Trade-off (same spirit as the input cap):
172
+ * once the budget is exhausted, remaining pairs are reported as
173
+ * not-similar (no rename) — renames can go undetected on pathological
174
+ * diffs, CPU is never unbounded. Overridable per call via
175
+ * `EntityDiffOptions.lcsDpCellBudget`.
176
+ */
177
+ const DEFAULT_LCS_DP_CELL_BUDGET = 200_000_000;
178
+ /**
179
+ * Compute similarity ratio between two strings (0.0–1.0) as
180
+ * `LCS(a, b) / max(len(a), len(b))` — a real longest-common-subsequence
181
+ * ratio (CORE-M8; the previous implementation claimed LCS but compared
182
+ * characters at the same positions, so a one-char shift like "Xabcde" vs
183
+ * "abcde" scored near 0 instead of high).
184
+ *
185
+ * Classic two-row dynamic programming: O(n*m) time, O(min(n, m)) memory.
139
186
  * Returns 1.0 for identical strings, 0.0 for completely different.
187
+ *
188
+ * Guards, in evaluation order (CORE-M8 fix-forward):
189
+ * 1. Identity fast-path on the ORIGINAL strings → exact 1.0 (never on the
190
+ * capped slices — equal capped prefixes of differing bodies are NOT 1.0).
191
+ * 2. Free prefilter: LCS(a, b) ≤ min(len(a), len(b), cap), so when that
192
+ * bound over `max(len)` is already below `threshold` the pair cannot
193
+ * match — skip the O(n*m) DP entirely. Returns 0.0; the caller only
194
+ * compares the result against `threshold`, so any sub-threshold value
195
+ * is equivalent (reported similarities of accepted matches are exact).
196
+ * 3. Capped-identity shortcut: equal capped prefixes have LCS exactly
197
+ * equal to the capped length — score `cap / max(original lens)` with
198
+ * no DP.
199
+ * 4. DP cell budget: each DP run consumes n*m cells from `budget`; a pair
200
+ * that would exceed what remains returns 0.0 (treated as not similar)
201
+ * instead of running.
140
202
  */
141
- function computeSimilarity(a, b) {
203
+ function computeSimilarity(a, b, threshold, budget) {
142
204
  if (a === b)
143
205
  return 1.0;
144
206
  if (a.length === 0 || b.length === 0)
145
207
  return 0.0;
208
+ // Denominator over ORIGINAL lengths: a post-cap denominator let two >10k
209
+ // bodies with identical capped prefixes score a false 1.0.
146
210
  const maxLen = Math.max(a.length, b.length);
147
- // For performance, use character-level comparison for short strings
148
- // and a simplified ratio for longer ones
149
- let matches = 0;
150
- const shorter = a.length <= b.length ? a : b;
151
- const longer = a.length > b.length ? a : b;
152
- // Count matching characters at same positions
153
- const minLen = shorter.length;
154
- for (let i = 0; i < minLen; i++) {
155
- if (shorter[i] === longer[i])
156
- matches++;
211
+ const lcsUpperBound = Math.min(a.length, b.length, MAX_SIMILARITY_INPUT_LENGTH);
212
+ if (lcsUpperBound / maxLen < threshold)
213
+ return 0.0;
214
+ const s = a.length > MAX_SIMILARITY_INPUT_LENGTH ? a.slice(0, MAX_SIMILARITY_INPUT_LENGTH) : a;
215
+ const t = b.length > MAX_SIMILARITY_INPUT_LENGTH ? b.slice(0, MAX_SIMILARITY_INPUT_LENGTH) : b;
216
+ if (s === t)
217
+ return s.length / maxLen;
218
+ const cells = s.length * t.length;
219
+ if (cells > budget.cellsRemaining)
220
+ return 0.0;
221
+ budget.cellsRemaining -= cells;
222
+ return lcsLength(s, t) / maxLen;
223
+ }
224
+ /**
225
+ * Length of the longest common subsequence of two strings.
226
+ * Two-row DP over the shorter string to keep memory at O(min(n, m)).
227
+ */
228
+ function lcsLength(a, b) {
229
+ const longer = a.length >= b.length ? a : b;
230
+ const shorter = a.length >= b.length ? b : a;
231
+ const cols = shorter.length;
232
+ let prev = new Uint32Array(cols + 1);
233
+ let curr = new Uint32Array(cols + 1);
234
+ for (let i = 1; i <= longer.length; i++) {
235
+ const charCode = longer.charCodeAt(i - 1);
236
+ for (let j = 1; j <= cols; j++) {
237
+ if (charCode === shorter.charCodeAt(j - 1)) {
238
+ curr[j] = prev[j - 1] + 1;
239
+ }
240
+ else {
241
+ const fromTop = prev[j];
242
+ const fromLeft = curr[j - 1];
243
+ curr[j] = fromTop >= fromLeft ? fromTop : fromLeft;
244
+ }
245
+ }
246
+ const swap = prev;
247
+ prev = curr;
248
+ curr = swap;
157
249
  }
158
- return matches / maxLen;
250
+ return prev[cols];
159
251
  }
160
252
  /**
161
253
  * Detect renamed entities by matching removed symbols with added symbols
@@ -170,6 +262,12 @@ function computeSimilarity(a, b) {
170
262
  */
171
263
  export function detectRenames(removedSymbols, addedSymbols, oldSource, newSource, options) {
172
264
  const threshold = options?.similarityThreshold ?? DEFAULT_SIMILARITY_THRESHOLD;
265
+ // Shared across ALL removed×added pairs of this call (CORE-M8 fix-forward):
266
+ // bounds total DP work so a pathological diff (many large bodies) degrades
267
+ // to missed renames instead of unbounded CPU.
268
+ const budget = {
269
+ cellsRemaining: options?.lcsDpCellBudget ?? DEFAULT_LCS_DP_CELL_BUDGET,
270
+ };
173
271
  const renames = [];
174
272
  const matchedAdded = new Set();
175
273
  const oldLines = oldSource.split('\n');
@@ -193,7 +291,7 @@ export function detectRenames(removedSymbols, addedSymbols, oldSource, newSource
193
291
  const newBody = normalizeBody(newLines.slice(newBodyStart, added.endLine).join('\n'));
194
292
  if (newBody.length === 0)
195
293
  continue;
196
- const similarity = computeSimilarity(oldBody, newBody);
294
+ const similarity = computeSimilarity(oldBody, newBody, threshold, budget);
197
295
  if (similarity >= threshold && (!bestMatch || similarity > bestMatch.similarity)) {
198
296
  bestMatch = { index: i, similarity, symbol: added };
199
297
  }
@@ -1 +1 @@
1
- {"version":3,"file":"entity-diff.js","sourceRoot":"","sources":["../../src/scope/entity-diff.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity-diff.js","sourceRoot":"","sources":["../../src/scope/entity-diff.ts"],"names":[],"mappings":"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"}
@@ -74,6 +74,16 @@ export interface RenameMatch {
74
74
  export interface EntityDiffOptions {
75
75
  /** Minimum body similarity to consider a rename (0.0–1.0). Default: 0.9 */
76
76
  similarityThreshold?: number;
77
+ /**
78
+ * Total LCS dynamic-programming cell budget (n*m per compared pair of
79
+ * normalized bodies) for one `detectRenames` call. Once exhausted,
80
+ * remaining pairs are treated as not similar (no rename) instead of
81
+ * running the O(n*m) DP — bounds CPU on pathological diffs with many
82
+ * large removed/added bodies. Identity matches and pairs resolved by
83
+ * the cheap prefilter consume no budget. Default: 200_000_000
84
+ * (~low single-digit seconds worst case).
85
+ */
86
+ lcsDpCellBudget?: number;
77
87
  }
78
88
  /** A file with its affected symbols and source content, ready for scoped review. */
79
89
  export interface ScopedFile {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,2DAA2D;AAC3D,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,QAAQ,GAAG,IAAI,CAAC;AAI1E,iDAAiD;AACjD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEvE,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IAEb,qBAAqB;IACrB,IAAI,EAAE,UAAU,CAAC;IAEjB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAElB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAEhB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAElB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAEhB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,yCAAyC;AACzC,MAAM,WAAW,QAAQ;IACvB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IAEjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IAEjB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IAEjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,MAAM,EAAE,UAAU,CAAC;IAEnB,2CAA2C;IAC3C,gBAAgB,EAAE,QAAQ,EAAE,CAAC;CAC9B;AAID,mDAAmD;AACnD,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE5F,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,0BAA0B;IAC1B,MAAM,EAAE,UAAU,CAAC;IAEnB,mCAAmC;IACnC,IAAI,EAAE,gBAAgB,CAAC;IAEvB,sDAAsD;IACtD,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAEhB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAEhB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IAEnB,mCAAmC;IACnC,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAID,oFAAoF;AACpF,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IAEjB,gDAAgD;IAChD,OAAO,EAAE,cAAc,EAAE,CAAC;IAE1B,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,2DAA2D;AAC3D,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,QAAQ,GAAG,IAAI,CAAC;AAI1E,iDAAiD;AACjD,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEvE,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IAEb,qBAAqB;IACrB,IAAI,EAAE,UAAU,CAAC;IAEjB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAElB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAEhB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAElB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAEhB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,yCAAyC;AACzC,MAAM,WAAW,QAAQ;IACvB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IAEjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IAEjB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IAEjB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,MAAM,EAAE,UAAU,CAAC;IAEnB,2CAA2C;IAC3C,gBAAgB,EAAE,QAAQ,EAAE,CAAC;CAC9B;AAID,mDAAmD;AACnD,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE5F,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,0BAA0B;IAC1B,MAAM,EAAE,UAAU,CAAC;IAEnB,mCAAmC;IACnC,IAAI,EAAE,gBAAgB,CAAC;IAEvB,sDAAsD;IACtD,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAEhB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAEhB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IAEnB,mCAAmC;IACnC,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAID,oFAAoF;AACpF,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IAEjB,gDAAgD;IAChD,OAAO,EAAE,cAAc,EAAE,CAAC;IAE1B,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB"}