@codraoss/core 0.9.4 → 0.9.12
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.
- package/dist/{chunk-XD6CGYHO.js → chunk-4XEMXRNG.js} +62 -39
- package/dist/chunk-4XEMXRNG.js.map +1 -0
- package/dist/{chunk-ILZUCIZH.js → chunk-EQL5OOEF.js} +7 -7
- package/dist/chunk-EQL5OOEF.js.map +1 -0
- package/dist/{chunk-RQ5S67XB.js → chunk-FKRCBUWZ.js} +8 -2
- package/dist/chunk-FKRCBUWZ.js.map +1 -0
- package/dist/{chunk-JC74XH7Q.js → chunk-GDX2ZEBA.js} +1 -1
- package/dist/chunk-GDX2ZEBA.js.map +1 -0
- package/dist/{chunk-OIQRTDYR.js → chunk-I5S3NBFL.js} +20 -4
- package/dist/chunk-I5S3NBFL.js.map +1 -0
- package/dist/{chunk-U3VKVDZ7.js → chunk-IKVUW4WC.js} +92 -19
- package/dist/chunk-IKVUW4WC.js.map +1 -0
- package/dist/{chunk-R2HRWFRH.js → chunk-JAF7JIZQ.js} +1 -1
- package/dist/chunk-JAF7JIZQ.js.map +1 -0
- package/dist/{chunk-V3RF6GQW.js → chunk-KTDBDKL5.js} +1 -1
- package/dist/chunk-KTDBDKL5.js.map +1 -0
- package/dist/{chunk-MMVQPGK3.js → chunk-ONJZXUQT.js} +112 -24
- package/dist/chunk-ONJZXUQT.js.map +1 -0
- package/dist/{chunk-PVXFURDR.js → chunk-QPPLO2YV.js} +2 -2
- package/dist/chunk-QPPLO2YV.js.map +1 -0
- package/dist/{chunk-FGXA7JNR.js → chunk-W3RY755V.js} +14 -2
- package/dist/chunk-W3RY755V.js.map +1 -0
- package/dist/{chunk-NSTV4TRP.js → chunk-XPYVLHSX.js} +1 -1
- package/dist/chunk-XPYVLHSX.js.map +1 -0
- package/dist/{chunk-WSK45HW4.js → chunk-Z5B5X7QP.js} +1 -1
- package/dist/chunk-Z5B5X7QP.js.map +1 -0
- package/dist/claim-checks.d.ts +8 -13
- package/dist/claim-checks.js +2 -2
- package/dist/diff/index.d.ts +3 -3
- package/dist/diff/index.js +2 -1
- package/dist/fingerprint.js +1 -1
- package/dist/formatter-CDFpqbDP.d.ts +24 -0
- package/dist/formatter.d.ts +29 -0
- package/dist/formatter.js +111 -0
- package/dist/formatter.js.map +1 -0
- package/dist/{index-BTDWXDck.d.ts → index-CrtHz4vN.d.ts} +3 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.js +239 -62
- package/dist/index.js.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/{model-gRDTk8yk.d.ts → model-BkqfyVh9.d.ts} +27 -6
- package/dist/model-output/index.d.ts +2 -2
- package/dist/model-output/index.js +9 -9
- package/dist/ports/index.d.ts +22 -18
- package/dist/ports/index.js +1 -1
- package/dist/{position-h_jdn6ZH.d.ts → position-zZl8KGVu.d.ts} +1 -1
- package/dist/prompts/file-review.d.ts +3 -3
- package/dist/prompts/file-review.js +11 -5
- package/dist/prompts/languages.d.ts +2 -1
- package/dist/prompts/languages.js +5 -3
- package/dist/prompts/verify.d.ts +1 -1
- package/dist/prompts/verify.js +1 -1
- package/dist/rules/detect.d.ts +1 -1
- package/dist/rules/detect.js +5 -5
- package/dist/rules/table.js +1 -1
- package/dist/token-tracker.js +2 -2
- package/package.json +10 -2
- package/dist/chunk-FGXA7JNR.js.map +0 -1
- package/dist/chunk-ILZUCIZH.js.map +0 -1
- package/dist/chunk-JC74XH7Q.js.map +0 -1
- package/dist/chunk-MMVQPGK3.js.map +0 -1
- package/dist/chunk-NSTV4TRP.js.map +0 -1
- package/dist/chunk-OIQRTDYR.js.map +0 -1
- package/dist/chunk-PVXFURDR.js.map +0 -1
- package/dist/chunk-R2HRWFRH.js.map +0 -1
- package/dist/chunk-RQ5S67XB.js.map +0 -1
- package/dist/chunk-U3VKVDZ7.js.map +0 -1
- package/dist/chunk-V3RF6GQW.js.map +0 -1
- package/dist/chunk-WSK45HW4.js.map +0 -1
- package/dist/chunk-XD6CGYHO.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/token-tracker.ts"],"sourcesContent":["import { logger } from './logger';\r\n\r\nexport interface TokenUsage {\r\n input: number;\r\n output: number;\r\n}\r\n\r\nexport interface ModelUsage extends TokenUsage {\r\n model: string;\r\n calls: number;\r\n}\r\n\r\nexport type WastedAttemptReason = 'rate-limited' | 'error';\r\n\r\nexport interface WastedUsage {\r\n attempts: number;\r\n estimatedInput: number;\r\n skips: number;\r\n byReason: Record<string, number>;\r\n}\r\n\r\nexport class TokenTracker {\r\n private usage: Map<string, ModelUsage> = new Map();\r\n private wasted = { attempts: 0, estimatedInput: 0, skips: 0 };\r\n private wastedByReason: Map<string, number> = new Map();\r\n private subrequests = 0;\r\n private readonly MAX_SUBREQUESTS = 50;\r\n private readonly SAFE_MARGIN = 25;\r\n\r\n incrementSubrequests(count = 1) {\r\n this.subrequests += count;\r\n }\r\n\r\n getSubrequestCount() {\r\n return this.subrequests;\r\n }\r\n\r\n hasRemainingSubrequests(needed = 1) {\r\n return this.subrequests + needed <= this.MAX_SUBREQUESTS;\r\n }\r\n\r\n isNearLimit() {\r\n return this.subrequests >= this.MAX_SUBREQUESTS - this.SAFE_MARGIN;\r\n }\r\n\r\n remainingSafeBudget() {\r\n return Math.max(0, this.MAX_SUBREQUESTS - this.SAFE_MARGIN - this.subrequests);\r\n }\r\n\r\n record(model: string, input: number, output: number) {\r\n const existing = this.usage.get(model) || { model, input: 0, output: 0, calls: 0 };\r\n \r\n this.usage.set(model, {\r\n model,\r\n input: existing.input + input,\r\n output: existing.output + output,\r\n calls: existing.calls + 1,\r\n });\r\n\r\n logger.debug(`Token usage recorded for ${model}`, { \r\n input, \r\n output, \r\n totalInput: existing.input + input,\r\n totalOutput: existing.output + output\r\n });\r\n }\r\n\r\n recordFailedAttempt(model: string, estimatedInputTokens: number, reason: WastedAttemptReason) {\r\n this.wasted.attempts += 1;\r\n this.wasted.estimatedInput += estimatedInputTokens;\r\n this.wastedByReason.set(reason, (this.wastedByReason.get(reason) ?? 0) + 1);\r\n\r\n logger.debug(`Wasted model attempt on ${model}`, { estimatedInput: estimatedInputTokens, reason });\r\n }\r\n\r\n recordSkippedCall(model: string, reason: string) {\r\n this.wasted.skips += 1;\r\n\r\n logger.debug(`Skipped model call on ${model}`, { reason });\r\n }\r\n\r\n getWasted(): WastedUsage {\r\n return { ...this.wasted, byReason: Object.fromEntries(this.wastedByReason) };\r\n }\r\n\r\n getTotalUsage(): TokenUsage {\r\n let input = 0;\r\n let output = 0;\r\n for (const modelUsage of this.usage.values()) {\r\n input += modelUsage.input;\r\n output += modelUsage.output;\r\n }\r\n return { input, output };\r\n }\r\n\r\n getBreakdown(): ModelUsage[] {\r\n return Array.from(this.usage.values());\r\n }\r\n\r\n merge(other: TokenTracker) {\r\n for (const usage of other.getBreakdown()) {\r\n this.record(usage.model, usage.input, usage.output);\r\n }\r\n\r\n const otherWasted = other.getWasted();\r\n this.wasted.attempts += otherWasted.attempts;\r\n this.wasted.estimatedInput += otherWasted.estimatedInput;\r\n this.wasted.skips += otherWasted.skips;\r\n for (const [reason, count] of Object.entries(otherWasted.byReason)) {\r\n this.wastedByReason.set(reason, (this.wastedByReason.get(reason) ?? 0) + count);\r\n }\r\n }\r\n\r\n reset() {\r\n this.usage.clear();\r\n this.wasted = { attempts: 0, estimatedInput: 0, skips: 0 };\r\n this.wastedByReason.clear();\r\n }\r\n}\r\n"],"mappings":";;;;;AAqBO,IAAM,eAAN,MAAmB;AAAA,EAChB,QAAiC,oBAAI,IAAI;AAAA,EACzC,SAAS,EAAE,UAAU,GAAG,gBAAgB,GAAG,OAAO,EAAE;AAAA,EACpD,iBAAsC,oBAAI,IAAI;AAAA,EAC9C,cAAc;AAAA,EACL,kBAAkB;AAAA,EAClB,cAAc;AAAA,EAE/B,qBAAqB,QAAQ,GAAG;AAC9B,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,qBAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,wBAAwB,SAAS,GAAG;AAClC,WAAO,KAAK,cAAc,UAAU,KAAK;AAAA,EAC3C;AAAA,EAEA,cAAc;AACZ,WAAO,KAAK,eAAe,KAAK,kBAAkB,KAAK;AAAA,EACzD;AAAA,EAEA,sBAAsB;AACpB,WAAO,KAAK,IAAI,GAAG,KAAK,kBAAkB,KAAK,cAAc,KAAK,WAAW;AAAA,EAC/E;AAAA,EAEA,OAAO,OAAe,OAAe,QAAgB;AACnD,UAAM,WAAW,KAAK,MAAM,IAAI,KAAK,KAAK,EAAE,OAAO,OAAO,GAAG,QAAQ,GAAG,OAAO,EAAE;AAEjF,SAAK,MAAM,IAAI,OAAO;AAAA,MACpB;AAAA,MACA,OAAO,SAAS,QAAQ;AAAA,MACxB,QAAQ,SAAS,SAAS;AAAA,MAC1B,OAAO,SAAS,QAAQ;AAAA,IAC1B,CAAC;AAED,WAAO,MAAM,4BAA4B,KAAK,IAAI;AAAA,MAChD;AAAA,MACA;AAAA,MACA,YAAY,SAAS,QAAQ;AAAA,MAC7B,aAAa,SAAS,SAAS;AAAA,IACjC,CAAC;AAAA,EACH;AAAA,EAEA,oBAAoB,OAAe,sBAA8B,QAA6B;AAC5F,SAAK,OAAO,YAAY;AACxB,SAAK,OAAO,kBAAkB;AAC9B,SAAK,eAAe,IAAI,SAAS,KAAK,eAAe,IAAI,MAAM,KAAK,KAAK,CAAC;AAE1E,WAAO,MAAM,2BAA2B,KAAK,IAAI,EAAE,gBAAgB,sBAAsB,OAAO,CAAC;AAAA,EACnG;AAAA,EAEA,kBAAkB,OAAe,QAAgB;AAC/C,SAAK,OAAO,SAAS;AAErB,WAAO,MAAM,yBAAyB,KAAK,IAAI,EAAE,OAAO,CAAC;AAAA,EAC3D;AAAA,EAEA,YAAyB;AACvB,WAAO,EAAE,GAAG,KAAK,QAAQ,UAAU,OAAO,YAAY,KAAK,cAAc,EAAE;AAAA,EAC7E;AAAA,EAEA,gBAA4B;AAC1B,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,eAAW,cAAc,KAAK,MAAM,OAAO,GAAG;AAC5C,eAAS,WAAW;AACpB,gBAAU,WAAW;AAAA,IACvB;AACA,WAAO,EAAE,OAAO,OAAO;AAAA,EACzB;AAAA,EAEA,eAA6B;AAC3B,WAAO,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC;AAAA,EACvC;AAAA,EAEA,MAAM,OAAqB;AACzB,eAAW,SAAS,MAAM,aAAa,GAAG;AACxC,WAAK,OAAO,MAAM,OAAO,MAAM,OAAO,MAAM,MAAM;AAAA,IACpD;AAEA,UAAM,cAAc,MAAM,UAAU;AACpC,SAAK,OAAO,YAAY,YAAY;AACpC,SAAK,OAAO,kBAAkB,YAAY;AAC1C,SAAK,OAAO,SAAS,YAAY;AACjC,eAAW,CAAC,QAAQ,KAAK,KAAK,OAAO,QAAQ,YAAY,QAAQ,GAAG;AAClE,WAAK,eAAe,IAAI,SAAS,KAAK,eAAe,IAAI,MAAM,KAAK,KAAK,KAAK;AAAA,IAChF;AAAA,EACF;AAAA,EAEA,QAAQ;AACN,SAAK,MAAM,MAAM;AACjB,SAAK,SAAS,EAAE,UAAU,GAAG,gBAAgB,GAAG,OAAO,EAAE;AACzD,SAAK,eAAe,MAAM;AAAA,EAC5B;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/rules/table.ts"],"sourcesContent":["import type { ClaimType, reviewSeverities } from '@codraoss/schema';\r\n\r\ntype ReviewSeverity = typeof reviewSeverities[number];\r\n\r\nexport type Rule = {\r\n id: string;\r\n claimType: ClaimType;\r\n severity: ReviewSeverity;\r\n title: string;\r\n body: string;\r\n triggers: readonly string[];\r\n pattern: RegExp;\r\n rejectRaw?: RegExp;\r\n extensions?: readonly string[];\r\n enabled: boolean;\r\n};\r\n\r\nconst ts = ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs'] as const;\r\n\r\nexport const RULES: readonly Rule[] = [\r\n {\r\n id: 'empty-catch',\r\n claimType: 'swallowed_error',\r\n severity: 'P2',\r\n title: 'Empty catch block swallows the error',\r\n body: 'This `catch` has no body, so the error is discarded with no log, no rethrow and no recovery. '\r\n + 'A failure here becomes silent. If the error is genuinely expected, say so in a comment inside the block.',\r\n triggers: ['catch'],\r\n pattern: /\\bcatch\\s*(\\([^)]*\\))?\\s*\\{\\s*\\}/,\r\n rejectRaw: /\\bcatch\\s*(\\([^)]*\\))?\\s*\\{\\s*(?:\\/\\/|\\/\\*)/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'debugger-statement',\r\n claimType: 'other',\r\n severity: 'P1',\r\n title: '`debugger` statement left in the diff',\r\n body: 'A `debugger` statement halts execution whenever devtools are open. This is almost always '\r\n + 'a leftover from local debugging.',\r\n triggers: ['debugger'],\r\n pattern: /^\\s*debugger\\s*;?\\s*$/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'focused-test',\r\n claimType: 'other',\r\n severity: 'P1',\r\n title: 'Focused test will skip the rest of the suite',\r\n body: 'A focused test (`.only`) silently prevents every other test in the file from running, so '\r\n + 'CI stays green while covering almost nothing.',\r\n triggers: ['.only'],\r\n pattern: /\\b(?:describe|it|test|context|suite)\\s*\\.\\s*only\\s*\\(/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'dynamic-code-exec',\r\n claimType: 'unsafe_dynamic_code',\r\n severity: 'P1',\r\n title: 'Dynamic code execution',\r\n body: '`eval` and the `Function` constructor execute arbitrary strings as code. If any part of '\r\n + 'that string can be influenced by input, this is remote code execution.',\r\n triggers: ['eval(', 'Function('],\r\n pattern: /(?:^|[^.\\w])eval\\s*\\(|new\\s+Function\\s*\\(/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'dynamic-html-sink',\r\n claimType: 'unsafe_dom_sink',\r\n severity: 'P1',\r\n title: 'Unsanitized value assigned to an HTML sink',\r\n body: 'Assigning a non-literal to `innerHTML`/`outerHTML` (or passing one to `insertAdjacentHTML`) '\r\n + 'executes any markup it contains. If the value can carry user input this is XSS.',\r\n triggers: ['innerHTML', 'outerHTML', 'insertAdjacentHTML'],\r\n pattern: /\\.(?:inner|outer)HTML\\s*=\\s*[A-Za-z_$][\\w$.[\\]()]*|insertAdjacentHTML\\s*\\([^)]*,\\s*[A-Za-z_$]/,\r\n extensions: ts,\r\n enabled: true,\r\n },\r\n {\r\n id: 'mutable-default-arg',\r\n claimType: 'mutable_default_arg',\r\n severity: 'P2',\r\n title: 'Mutable default argument',\r\n body: 'Python evaluates a default argument once, at definition time, so this list/dict/set is '\r\n + 'shared by every call. Mutating it leaks state between invocations. Use `None` and build the '\r\n + 'value inside the function.',\r\n triggers: ['def '],\r\n pattern: /\\bdef\\s+\\w+\\s*\\([^)]*=\\s*(?:\\[\\s*\\]|\\{\\s*\\}|set\\s*\\(\\s*\\)|dict\\s*\\(\\s*\\)|list\\s*\\(\\s*\\))/,\r\n extensions: ['py'],\r\n enabled: true,\r\n },\r\n {\r\n id: 'destructive-migration',\r\n claimType: 'destructive_migration',\r\n severity: 'P1',\r\n title: 'Destructive migration statement',\r\n body: 'This statement discards data irreversibly. On a forward-only migration chain there is no '\r\n + 'rollback: confirm the column/table is genuinely unused and that a backup exists.',\r\n triggers: ['DROP', 'TRUNCATE', 'drop', 'truncate'],\r\n pattern: /\\b(?:drop\\s+(?:column|table)|truncate\\s+table|truncate\\s+\\w)/i,\r\n extensions: ['sql'],\r\n enabled: true,\r\n },\r\n\r\n\r\n {\r\n id: 'hardcoded-secret',\r\n claimType: 'hardcoded_secret',\r\n severity: 'P0',\r\n title: 'Possible hardcoded credential',\r\n body: 'This looks like a literal credential committed to the repository. If it is real, rotate it '\r\n + 'and move it to a secret binding.',\r\n triggers: ['sk-', 'AIza', 'ghp_', 'AKIA'],\r\n pattern: /\\b(?:sk-[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{30,}|gh[pousr]_[A-Za-z0-9]{30,}|AKIA[0-9A-Z]{16})\\b/,\r\n enabled: false,\r\n },\r\n {\r\n id: 'insecure-random',\r\n claimType: 'insecure_randomness',\r\n severity: 'P2',\r\n title: '`Math.random()` used for a security-sensitive value',\r\n body: '`Math.random()` is not cryptographically secure and its output is predictable. Use '\r\n + '`crypto.getRandomValues()` for tokens, ids or anything an attacker should not guess.',\r\n triggers: ['Math.random'],\r\n pattern: /\\b(?:token|secret|key|nonce|salt|password|session|id)\\w*\\s*=[^=]*Math\\.random\\s*\\(/i,\r\n extensions: ts,\r\n enabled: false,\r\n },\r\n];\r\n\r\n"],"mappings":";AAiBA,IAAM,KAAK,CAAC,MAAM,OAAO,MAAM,OAAO,OAAO,KAAK;AAE3C,IAAM,QAAyB;AAAA,EACpC;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,OAAO;AAAA,IAClB,SAAS;AAAA,IACT,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,UAAU;AAAA,IACrB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,OAAO;AAAA,IAClB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,SAAS,WAAW;AAAA,IAC/B,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,aAAa,aAAa,oBAAoB;AAAA,IACzD,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAGN,UAAU,CAAC,MAAM;AAAA,IACjB,SAAS;AAAA,IACT,YAAY,CAAC,IAAI;AAAA,IACjB,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,QAAQ,YAAY,QAAQ,UAAU;AAAA,IACjD,SAAS;AAAA,IACT,YAAY,CAAC,KAAK;AAAA,IAClB,SAAS;AAAA,EACX;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,OAAO,QAAQ,QAAQ,MAAM;AAAA,IACxC,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,IACP,MAAM;AAAA,IAEN,UAAU,CAAC,aAAa;AAAA,IACxB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/prompts/languages.ts"],"sourcesContent":["export type LanguageGuideline = {\r\n language: string;\r\n extensions: string[];\r\n guidelines: string[];\r\n persona?: string;\r\n};\r\n\r\nconst LANGUAGE_GUIDELINES: LanguageGuideline[] = [\r\n {\r\n language: 'TypeScript/JavaScript',\r\n persona: 'an expert TypeScript engineer focused on correctness and safe async code',\r\n extensions: ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs'],\r\n guidelines: [\r\n 'Flag unhandled promise rejections, missing await, or async errors that can crash or silently drop work.',\r\n 'Flag resource leaks that cause real bugs (uncleared timers/intervals/listeners on a path that runs repeatedly).',\r\n 'Flag security pitfalls such as eval() on untrusted input or ReDoS-prone regexes.',\r\n 'Flag runtime-breaking null/undefined access introduced by the diff.',\r\n ],\r\n },\r\n {\r\n language: 'Python',\r\n persona: 'a Python engineer focused on correctness',\r\n extensions: ['py'],\r\n guidelines: [\r\n 'Flag mutable default arguments that cause shared-state bugs.',\r\n 'Flag bare \"except:\" that swallows errors and hides failures.',\r\n 'Flag incorrect exception handling or resource handling (files/sockets not closed).',\r\n ],\r\n },\r\n {\r\n language: 'CSS/SCSS/Less',\r\n persona: 'a frontend engineer',\r\n extensions: ['css', 'scss', 'sass', 'less'],\r\n guidelines: [\r\n 'Flag only rules that break layout or rendering; do not report stylistic preferences.',\r\n ],\r\n },\r\n {\r\n language: 'SQL',\r\n persona: 'a database engineer focused on query safety and correctness',\r\n extensions: ['sql'],\r\n guidelines: [\r\n 'Flag SQL injection risks (unparameterized/interpolated user input).',\r\n 'Flag destructive or non-atomic migrations that risk data loss.',\r\n ],\r\n },\r\n {\r\n language: 'Markdown',\r\n persona: 'a technical writer',\r\n extensions: ['md', 'mdx'],\r\n guidelines: [\r\n 'Flag only broken links/images or factually incorrect content; do not report style or grammar nits.',\r\n ],\r\n },\r\n {\r\n language: 'HTML',\r\n persona: 'a web engineer',\r\n extensions: ['html', 'htm'],\r\n guidelines: [\r\n 'Flag only markup that is broken or functionally inaccessible; do not report SEO or style preferences.',\r\n ],\r\n },\r\n {\r\n language: 'JSON/Config',\r\n persona: 'a DevOps engineer',\r\n extensions: ['json', 'jsonc', 'yaml', 'yml', 'toml'],\r\n guidelines: [\r\n 'Flag invalid syntax/schema or hardcoded secrets; do not report naming-convention preferences.',\r\n ],\r\n },\r\n];\r\n\r\nexport function getLanguageForFile(path: string): LanguageGuideline | undefined {\r\n const ext = path.split('.').pop()?.toLowerCase();\r\n if (!ext) return undefined;\r\n\r\n const matches = LANGUAGE_GUIDELINES.filter((g) => g.extensions.includes(ext));\r\n \r\n if (matches.length === 0) return undefined;\r\n\r\n if (matches.length > 1) {\r\n return matches.reduce((best, candidate) =>\r\n candidate.extensions.length < best.extensions.length ? candidate : best,\r\n );\r\n }\r\n\r\n return matches[0];\r\n}\r\n"],"mappings":";AAOA,IAAM,sBAA2C;AAAA,EAC/C;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,MAAM,OAAO,MAAM,OAAO,OAAO,KAAK;AAAA,IACnD,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,IAAI;AAAA,IACjB,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,OAAO,QAAQ,QAAQ,MAAM;AAAA,IAC1C,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,KAAK;AAAA,IAClB,YAAY;AAAA,MACV;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,MAAM,KAAK;AAAA,IACxB,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,QAAQ,KAAK;AAAA,IAC1B,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY,CAAC,QAAQ,SAAS,QAAQ,OAAO,MAAM;AAAA,IACnD,YAAY;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,MAA6C;AAC9E,QAAM,MAAM,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY;AAC/C,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,UAAU,oBAAoB,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,GAAG,CAAC;AAE5E,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,QAAQ;AAAA,MAAO,CAAC,MAAM,cAC3B,UAAU,WAAW,SAAS,KAAK,WAAW,SAAS,YAAY;AAAA,IACrE;AAAA,EACF;AAEA,SAAO,QAAQ,CAAC;AAClB;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/claim-checks.ts"],"sourcesContent":["// SOUNDNESS, binding on every change: `refuted` asserts only that \"X does not appear\" is FALSE. There is no `confirmed` verdict, since a check that can confirm findings manufactures them. Losing a refutation is free; a wrong one silences a real defect.\r\nimport type { DiffLine, FileDiff } from './diff';\r\nimport { normalizeDiffText } from './fingerprint';\r\n\r\nconst PROXIMITY_WINDOW_LINES = 25;\r\n\r\nconst MIN_IDENTIFIER_LENGTH = 3;\r\n\r\nconst ABSENCE_PATTERNS: readonly RegExp[] = [\r\n /\\b(?:never|not|no longer)\\s+(?:being\\s+)?(?:passed|provided|supplied|forwarded|included|used|called|invoked|awaited|checked|set|declared|defined|imported)\\b/i,\r\n /\\bdoes not\\s+(?:pass|include|call|use|await|check|set|import)\\b/i,\r\n /\\bfails to\\s+(?:pass|include|call|await|check|import)\\b/i,\r\n /\\bwithout\\s+(?:passing|including|calling|awaiting|checking|importing)\\b/i,\r\n /\\b(?:missing|omitted|absent)\\b/i,\r\n /\\bis not defined\\b/i,\r\n];\r\n\r\nconst IDENTIFIER_STOPLIST = new Set([\r\n 'await', 'async', 'if', 'else', 'try', 'catch', 'finally', 'return', 'throw', 'new', 'const',\r\n 'let', 'var', 'function', 'class', 'this', 'super', 'import', 'export', 'from', 'default',\r\n 'null', 'undefined', 'true', 'false', 'void', 'typeof', 'instanceof', 'delete', 'yield',\r\n 'props', 'state', 'error', 'err', 'data', 'value', 'key', 'id', 'type', 'name', 'index',\r\n 'result', 'response', 'request', 'req', 'res', 'params', 'options', 'config', 'args',\r\n]);\r\n\r\nconst VERSION_CLAIM_PATTERNS: readonly RegExp[] = [\r\n /\\b(?:does not|doesn't|do not|don't)\\s+exist\\b/i,\r\n /\\b(?:non-?existent|nonexistent)\\b/i,\r\n /\\bis not a valid\\b/i,\r\n /\\blatest (?:major )?version\\b/i,\r\n /\\bno such (?:version|tag|release)\\b/i,\r\n /\\bnot a valid (?:configuration )?(?:option|key|property)\\b/i,\r\n /\\b(?:does not|doesn't|do not|don't)\\s+(?:expose|provide|have|support|include|offer)\\b/i,\r\n /\\bno such (?:function|method|export|property|api|field)\\b/i,\r\n /\\bis not (?:exposed|exported|available) (?:by|from|in)\\b/i,\r\n];\r\n\r\n// Same soundness rule as the absence checker above: a refutation asserts only that the claim cannot be\r\n\r\nconst CROSS_FILE_SUBJECT = /\\b(?:other|another|external|downstream|consuming|importing|dependent|calling)\\s+(?:module|file|component|caller|package|consumer|import)s?\\b/i;\r\nconst CROSS_FILE_CONSEQUENCE = /\\b(?:break|breaks|breaking|broken|fail|fails|failing|error|errors|cannot import|can't import|unable to|compilation|compile|prevent|prevents|preventing|block|blocks|blocking)\\b/i;\r\n\r\nconst ENVIRONMENT_HEDGE = /\\b(?:depending on|might not|may not|could be undefined|if (?:this|the|it)\\b[^.]{0,60}\\b(?:is )?(?:rendered|run|executed|used)\\b)/i;\r\nconst ENVIRONMENT_SUBJECT = /\\b(?:older|legacy|earlier|some)\\s+(?:node(?:\\.js)?|browsers?|runtimes?|environments?|engines?|versions?)\\b|\\bserver[- ]side\\b|\\bSSR\\b|\\bhydration\\b|\\bpolyfill\\b|\\bis not defined on the server\\b/i;\r\n\r\nconst CALLEE_FAILURE_CONDITION = /\\b(?:if|when|should|were)\\b(?:(?!\\.\\s)[^;!?]){0,62}\\b(?:fails?|failing|rejects?|rejecting|throws?|throwing|errors? out)\\b/i;\r\nconst CALLEE_CALL_SHAPE = /[\\w.$]{1,50}\\s*\\(\\s*\\)|`[\\w.$]{1,50}\\(/;\r\nconst CALLEE_UNHANDLED_OUTCOME = /\\bunhandled\\b|\\bunhandled promise\\b|\\bnot (?:caught|handled)\\b|\\bno (?:\\.)?catch\\b|\\bwithout (?:a )?(?:try|catch)\\b|\\bcrash\\b/i;\r\n\r\nexport type UndecidableClaimReason = 'cross-file' | 'environment' | 'callee-errors';\r\n\r\n/**\r\n * Refutes a claim whose truth lives outside the diff, returning the family it belongs to or null.\r\n *\r\n * Deliberately requires TWO independent signals per family -- a subject and a consequence -- because\r\n * either alone is ordinary review language. \"This breaks the build\" is a normal thing to say about\r\n * code in the diff; \"other modules import this\" is a normal aside. Only together do they describe a\r\n * consequence in a file nobody showed the model.\r\n */\r\nexport function refuteUndecidableClaim(input: { title: string; body: string }): UndecidableClaimReason | null {\r\n const text = `${input.title}\\n${input.body}`;\r\n\r\n if (CROSS_FILE_SUBJECT.test(text) && CROSS_FILE_CONSEQUENCE.test(text)) return 'cross-file';\r\n if (ENVIRONMENT_HEDGE.test(text) && ENVIRONMENT_SUBJECT.test(text)) return 'environment';\r\n if (CALLEE_FAILURE_CONDITION.test(text) && CALLEE_CALL_SHAPE.test(text) && CALLEE_UNHANDLED_OUTCOME.test(text)) {\r\n return 'callee-errors';\r\n }\r\n\r\n return null;\r\n}\r\n\r\nconst FULL_SHA_PATTERN = /\\b[0-9a-f]{40}\\b/;\r\n\r\nexport function looksLikeExternalVersionClaim(title: string, body: string): boolean {\r\n const text = `${title}\\n${body}`;\r\n return VERSION_CLAIM_PATTERNS.some((pattern) => pattern.test(text));\r\n}\r\n\r\nexport function isVersionClaimRefutedByPin(input: { title: string; body: string; anchorContent: string }): boolean {\r\n if (!looksLikeExternalVersionClaim(input.title, input.body)) return false;\r\n return FULL_SHA_PATTERN.test(input.anchorContent);\r\n}\r\n\r\ntype PresenceEntry = { line: DiffLine; hunkIndex: number; code: string };\r\n\r\nexport type PresenceIndex = {\r\n byToken: Map<string, PresenceEntry[]>;\r\n entries: PresenceEntry[];\r\n hunkByLine: Map<number, number>;\r\n};\r\n\r\nexport type AbsenceClaimVerdict =\r\n | {\r\n status: 'unknown';\r\n reason:\r\n | 'not_absence_shaped'\r\n | 'no_identifier'\r\n | 'ambiguous_identifier'\r\n | 'stoplisted'\r\n | 'not_present'\r\n | 'out_of_window';\r\n }\r\n | { status: 'refuted'; identifier: string; line: DiffLine };\r\n\r\ntype CommentSyntax = { line: readonly string[]; block: boolean };\r\n\r\nexport function commentSyntaxFor(path: string): CommentSyntax {\r\n const ext = path.toLowerCase().split('.').pop() ?? '';\r\n if (ext === 'py' || ext === 'rb' || ext === 'sh' || ext === 'yaml' || ext === 'yml' || ext === 'toml') {\r\n return { line: ['#'], block: false };\r\n }\r\n if (ext === 'sql') return { line: ['--'], block: true };\r\n return { line: ['//'], block: true };\r\n}\r\n\r\nexport function stripCommentsAndStrings(input: string, syntax: CommentSyntax): string | null {\r\n let out = '';\r\n let i = 0;\r\n\r\n while (i < input.length) {\r\n const rest = input.slice(i);\r\n\r\n if (syntax.line.some((token) => rest.startsWith(token))) break;\r\n\r\n if (syntax.block && rest.startsWith('/*')) {\r\n const end = input.indexOf('*/', i + 2);\r\n if (end === -1) return null;\r\n out += ' ';\r\n i = end + 2;\r\n continue;\r\n }\r\n\r\n const char = input[i];\r\n\r\n if (char === \"'\" || char === '\"') {\r\n const close = findStringEnd(input, i + 1, char);\r\n if (close === -1) return null;\r\n out += ' ';\r\n i = close + 1;\r\n continue;\r\n }\r\n\r\n if (char === '`') {\r\n const scanned = scanTemplateLiteral(input, i);\r\n if (!scanned) return null;\r\n out += scanned.code;\r\n i = scanned.next;\r\n continue;\r\n }\r\n\r\n out += char;\r\n i += 1;\r\n }\r\n\r\n return out;\r\n}\r\n\r\nfunction findStringEnd(input: string, start: number, quote: string): number {\r\n for (let i = start; i < input.length; i++) {\r\n if (input[i] === '\\\\') {\r\n i += 1;\r\n continue;\r\n }\r\n if (input[i] === quote) return i;\r\n }\r\n return -1;\r\n}\r\n\r\nfunction scanTemplateLiteral(input: string, start: number): { code: string; next: number } | null {\r\n let code = ' ';\r\n let i = start + 1;\r\n\r\n while (i < input.length) {\r\n if (input[i] === '\\\\') {\r\n i += 2;\r\n continue;\r\n }\r\n if (input[i] === '`') return { code, next: i + 1 };\r\n if (input[i] === '$' && input[i + 1] === '{') {\r\n let depth = 1;\r\n let j = i + 2;\r\n while (j < input.length && depth > 0) {\r\n if (input[j] === '{') depth += 1;\r\n else if (input[j] === '}') depth -= 1;\r\n j += 1;\r\n }\r\n if (depth !== 0) return null;\r\n code += ` ${input.slice(i + 2, j - 1)} `;\r\n i = j;\r\n continue;\r\n }\r\n i += 1;\r\n }\r\n\r\n return null;\r\n}\r\n\r\nconst TOKEN_PATTERN = /[A-Za-z_$][\\w$]*/g;\r\n\r\nexport function buildPresenceIndex(file: FileDiff): PresenceIndex {\r\n const syntax = commentSyntaxFor(file.path);\r\n const byToken = new Map<string, PresenceEntry[]>();\r\n const entries: PresenceEntry[] = [];\r\n const hunkByLine = new Map<number, number>();\r\n\r\n file.hunks.forEach((hunk, hunkIndex) => {\r\n for (const line of hunk.lines) {\r\n if (line.newLineNumber !== undefined) hunkByLine.set(line.newLineNumber, hunkIndex);\r\n\r\n if (line.kind === 'del') continue;\r\n\r\n const code = stripCommentsAndStrings(normalizeDiffText(line.content), syntax);\r\n if (code === null) continue;\r\n\r\n const entry: PresenceEntry = { line, hunkIndex, code };\r\n entries.push(entry);\r\n\r\n for (const match of code.matchAll(TOKEN_PATTERN)) {\r\n const token = match[0];\r\n const existing = byToken.get(token);\r\n if (existing) existing.push(entry);\r\n else byToken.set(token, [entry]);\r\n }\r\n }\r\n });\r\n\r\n return { byToken, entries, hunkByLine };\r\n}\r\n\r\nconst SIMPLE_IDENTIFIER = /^[A-Za-z_$][\\w$]*$/;\r\nconst DOTTED_IDENTIFIER = /^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)+$/;\r\n\r\nfunction extractIdentifier(sentence: string): { identifier: string } | 'none' | 'ambiguous' {\r\n const spans = [\r\n ...sentence.matchAll(/`([^`]+)`/g),\r\n ...sentence.matchAll(/'([^']+)'/g),\r\n ...sentence.matchAll(/\"([^\"]+)\"/g),\r\n ].map((match) => match[1].trim());\r\n\r\n const candidates = new Set(\r\n spans.filter((span) => SIMPLE_IDENTIFIER.test(span) || DOTTED_IDENTIFIER.test(span)),\r\n );\r\n\r\n if (candidates.size === 0) return 'none';\r\n if (candidates.size > 1) return 'ambiguous';\r\n return { identifier: [...candidates][0] };\r\n}\r\n\r\nfunction absenceSentences(text: string): string[] {\r\n return text.split(/[.;\\n]/).filter((sentence) => ABSENCE_PATTERNS.some((pattern) => pattern.test(sentence)));\r\n}\r\n\r\nexport function checkAbsenceClaim(input: {\r\n title: string;\r\n body: string;\r\n anchorLine: number | undefined;\r\n index: PresenceIndex;\r\n}): AbsenceClaimVerdict {\r\n const text = `${input.title}\\n${input.body.slice(0, 600)}`;\r\n\r\n const sentences = absenceSentences(text);\r\n if (sentences.length === 0) return { status: 'unknown', reason: 'not_absence_shaped' };\r\n\r\n let identifier: string | undefined;\r\n for (const sentence of sentences) {\r\n const extracted = extractIdentifier(sentence);\r\n if (extracted === 'ambiguous') return { status: 'unknown', reason: 'ambiguous_identifier' };\r\n if (extracted !== 'none') {\r\n identifier = extracted.identifier;\r\n break;\r\n }\r\n }\r\n if (!identifier) return { status: 'unknown', reason: 'no_identifier' };\r\n\r\n const head = identifier.split('.')[0];\r\n if (identifier.length < MIN_IDENTIFIER_LENGTH) return { status: 'unknown', reason: 'stoplisted' };\r\n if (IDENTIFIER_STOPLIST.has(identifier.toLowerCase()) || IDENTIFIER_STOPLIST.has(head.toLowerCase())) {\r\n return { status: 'unknown', reason: 'stoplisted' };\r\n }\r\n\r\n const occurrences = identifier.includes('.')\r\n ? input.index.entries.filter((entry) => entry.code.replace(/\\s*\\.\\s*/g, '.').includes(identifier))\r\n : (input.index.byToken.get(identifier) ?? []);\r\n\r\n if (occurrences.length === 0) return { status: 'unknown', reason: 'not_present' };\r\n\r\n const anchorHunk = input.anchorLine !== undefined ? input.index.hunkByLine.get(input.anchorLine) : undefined;\r\n const nearby = occurrences.find((entry) => {\r\n if (anchorHunk !== undefined && entry.hunkIndex === anchorHunk) return true;\r\n if (input.anchorLine === undefined || entry.line.newLineNumber === undefined) return false;\r\n return Math.abs(entry.line.newLineNumber - input.anchorLine) <= PROXIMITY_WINDOW_LINES;\r\n });\r\n\r\n if (!nearby) return { status: 'unknown', reason: 'out_of_window' };\r\n return { status: 'refuted', identifier, line: nearby.line };\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/prompts/verify.ts"],"sourcesContent":["import { z } from 'zod';\r\nimport { jsonrepair } from 'jsonrepair';\r\nimport type { FileDiff } from '../diff';\r\n\r\nexport type VerifyCandidate = {\r\n index: number;\r\n path: string;\r\n line: number | null;\r\n title: string;\r\n body: string;\r\n snippet: string;\r\n evidence?: string | null;\r\n};\r\n\r\nconst verifyResultSchema = z.object({\r\n results: z\r\n .array(\r\n z.object({\r\n index: z.number().int(),\r\n reason: z.string().optional(),\r\n // decidable\" -- only an explicit `false` costs a finding. See the note on the prompt below.\r\n decidable: z.boolean().optional(),\r\n verdict: z.enum(['keep', 'drop']),\r\n confidence: z.number().min(0).max(1).optional(),\r\n }),\r\n )\r\n .default([]),\r\n});\r\n\r\nexport type VerifyResult = z.infer<typeof verifyResultSchema>['results'][number];\r\n\r\nexport const VERIFY_RESPONSE_SCHEMA = {\r\n name: 'codra_verify_findings',\r\n schema: {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['results'],\r\n properties: {\r\n results: {\r\n type: 'array',\r\n items: {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['index', 'reason', 'decidable', 'verdict'],\r\n properties: {\r\n index: { type: 'integer', minimum: 0 },\r\n reason: { type: 'string', maxLength: 300 },\r\n decidable: { type: 'boolean' },\r\n verdict: { type: 'string', enum: ['keep', 'drop'] },\r\n confidence: { type: 'number', minimum: 0, maximum: 1 },\r\n },\r\n },\r\n },\r\n },\r\n },\r\n} as const;\r\n\r\nexport const VERIFY_SYSTEM_PROMPT = `You are a meticulous senior engineer checking whether each candidate code-review finding is actually supported by the code it points at.\r\n\r\nFor EACH finding you are given the claim and a SHORT WINDOW of diff context around the line it was anchored to. That window is all you have: you cannot see the rest of the file, any other file, the project's dependencies and their versions, its build target, or its runtime.\r\n\r\nAnswer two questions per finding, in this order.\r\n\r\n1. \"decidable\": can this claim be settled from the window you were given?\r\n - true - the window contains everything needed to say whether the claim holds.\r\n - false - settling it would need something outside the window: which files import this one, what a function defined elsewhere does, which version of a dependency is installed, what engine or renderer the code runs on, or how a caller uses the result.\r\n Watch for claims that assert a CONSEQUENCE somewhere you cannot see: \"this breaks importers\", \"this throws on older runtimes\", \"this fails during server rendering\", \"the caller will not await this\". The anchored line can be exactly as quoted and the consequence still be unverifiable - confirming that the quote is real is NOT confirming the claim.\r\n When \"decidable\" is false, say in \"reason\" what you would have to look at, e.g. \"would need the importers of this module\".\r\n\r\n Two rules, because both have been got wrong on real reviews:\r\n\r\n a) A claim of the form \"if X() fails / rejects / throws, this is unhandled\" is NOT decidable unless the\r\n BODY of X is inside your window. A function whose body you cannot see may well handle its own\r\n errors, in which case there is nothing to report. Seeing the CALL is not seeing the body. Mark it\r\n not decidable and say you would need that function's implementation.\r\n\r\n b) Read the diff markers before you agree that something was removed or changed. A line prefixed \"-\"\r\n is the OLD code and a line prefixed \"+\" is the NEW code. A claim that says \"X was replaced by Y\" is\r\n false if the diff shows Y being replaced by X, and a claim that a safeguard was \"removed\" is false\r\n if the \"+\" line still carries an equivalent one under a different name. State the direction in your\r\n reason: \"the + line adds strict validation, so the claim is backwards\".\r\n\r\n2. \"verdict\":\r\n - \"keep\": the code in the window genuinely exhibits the problem the claim describes.\r\n - \"drop\": the claim is not supported by the code shown - it describes something that isn't there, it is speculative, it is a subjective style preference, or it is not decidable from this window.\r\n A claim you marked not decidable is always a \"drop\".\r\n\r\nJudge the CLAIM against the CODE. Do not defer to the claim's confidence or phrasing; a well-written claim about code that doesn't do what it says is still a drop.\r\nBe strict: when in doubt, \"drop\". It is better to drop a borderline finding than to keep a wrong one.\r\n\r\nOutput MUST be valid JSON, exactly one object, no prose before or after:\r\n{\r\n \"results\": [\r\n { \"index\": <number>, \"reason\": \"<why, and if not decidable what you would need to see>\", \"decidable\": true | false, \"verdict\": \"keep\" | \"drop\", \"confidence\": <float 0.0-1.0> }\r\n ]\r\n}\r\nInclude exactly one result object for every finding index provided, and use the same index numbers you were given.`;\r\n\r\nexport function buildVerifyPrompt(candidates: VerifyCandidate[]): string {\r\n const blocks = candidates.map((c) => {\r\n const location = c.line != null ? `${c.path}:${c.line}` : c.path;\r\n return [\r\n `### Finding index ${c.index}`,\r\n `Location: ${location}`,\r\n `Title: ${c.title}`,\r\n `Claim: ${c.body}`,\r\n ...(c.evidence ? [`Code the claim cites: ${c.evidence}`] : []),\r\n 'Relevant diff:',\r\n c.snippet || '(no diff context available for this location)',\r\n ].join('\\n');\r\n });\r\n\r\n return [\r\n 'Validate each finding below against its diff context. Return a verdict for every index.',\r\n '',\r\n blocks.join('\\n\\n'),\r\n ].join('\\n');\r\n}\r\n\r\nexport function renderDiffSnippet(file: FileDiff | undefined, line: number | undefined, radius = 12): string {\r\n if (!file) return '';\r\n const flat = file.hunks.flatMap((hunk) => hunk.lines);\r\n if (flat.length === 0) return '';\r\n\r\n if (line == null) return '';\r\n\r\n const byNewLine = flat.findIndex((l) => l.newLineNumber === line);\r\n const anchor = byNewLine !== -1 ? byNewLine : flat.findIndex((l) => l.oldLineNumber === line);\r\n if (anchor === -1) return '';\r\n\r\n const start = Math.max(0, anchor - radius);\r\n const end = Math.min(flat.length, anchor + radius + 1);\r\n\r\n return flat\r\n .slice(start, end)\r\n .map((l) => {\r\n const prefix = l.kind === 'add' ? '+' : l.kind === 'del' ? '-' : ' ';\r\n const gutter = String(l.newLineNumber ?? l.oldLineNumber ?? '').padStart(4, ' ');\r\n return `${gutter} ${prefix}${l.content}`;\r\n })\r\n .join('\\n');\r\n}\r\n\r\nexport function parseVerifyResponse(raw: string): VerifyResult[] {\r\n const trimmed = raw.trim();\r\n const start = trimmed.indexOf('{');\r\n const end = trimmed.lastIndexOf('}');\r\n const candidate = start !== -1 && end !== -1 && end > start ? trimmed.slice(start, end + 1) : trimmed;\r\n\r\n let json: unknown;\r\n try {\r\n json = JSON.parse(candidate);\r\n } catch {\r\n json = JSON.parse(jsonrepair(candidate));\r\n }\r\n\r\n return verifyResultSchema.parse(json).results;\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/logger.ts"],"sourcesContent":["\r\n/**\r\n * The logging port. A correct implementation must:\r\n * - never throw, for any input, including circular objects (callers log on failure paths, so a\r\n * throwing logger converts a handled error into an unhandled one);\r\n * - never block the caller on I/O;\r\n * - scrub secrets before emitting, using `scrubString`/`redact` below rather than its own rules.\r\n * Ordering between calls is not guaranteed and callers must not rely on it.\r\n */\r\nexport interface Logger {\r\n info(message: string, data?: unknown): void;\r\n warn(message: string, data?: unknown): void;\r\n error(message: string, data?: unknown): void;\r\n debug(message: string, data?: unknown): void;\r\n}\r\n\r\nconst SENSITIVE_KEYS = [\r\n 'api_key',\r\n 'api-key',\r\n 'apikey',\r\n 'secret',\r\n 'password',\r\n 'token',\r\n 'private_key',\r\n 'private-key',\r\n 'database_url',\r\n 'authorization',\r\n 'session',\r\n 'cookie',\r\n];\r\n\r\nconst JWT = /\\beyJ[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]*/g;\r\nconst BEARER = /\\b(Bearer|Basic)\\s+[A-Za-z0-9._~+/=-]{8,}/gi;\r\n\r\nexport function scrubString(value: string): string {\r\n return value.replace(JWT, '[REDACTED_JWT]').replace(BEARER, (m) => `${m.split(/\\s+/)[0]} [REDACTED]`);\r\n}\r\n\r\nexport function redact(obj: any): any {\r\n if (obj === null || obj === undefined) return obj;\r\n if (typeof obj !== 'object') {\r\n return typeof obj === 'string' ? scrubString(obj) : obj;\r\n }\r\n if (Array.isArray(obj)) return obj.map(redact);\r\n if (obj instanceof Error) {\r\n return {\r\n name: obj.name,\r\n message: scrubString(obj.message),\r\n ...(obj.stack ? { stack: scrubString(obj.stack) } : {}),\r\n };\r\n }\r\n\r\n const redacted: any = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n const lowerKey = key.toLowerCase();\r\n if (SENSITIVE_KEYS.some((sk) => lowerKey.includes(sk))) {\r\n redacted[key] = '[REDACTED]';\r\n } else {\r\n redacted[key] = redact(value);\r\n }\r\n }\r\n return redacted;\r\n}\r\n\r\nexport function formatLogRecord(\r\n level: string,\r\n message: string,\r\n contexts: Array<Record<string, any>>,\r\n data?: any,\r\n): Record<string, unknown> {\r\n return {\r\n timestamp: new Date().toISOString(),\r\n level,\r\n message: scrubString(message),\r\n ...contexts.reduce<Record<string, unknown>>((merged, context) => Object.assign(merged, redact(context)), {}),\r\n ...(data ? { data: redact(data) } : {}),\r\n };\r\n}\r\n\r\nexport const consoleLogger: Logger = {\r\n info: (message, data) => console.log(JSON.stringify(formatLogRecord('info', message, [], data))),\r\n warn: (message, data) => console.warn(JSON.stringify(formatLogRecord('warn', message, [], data))),\r\n error: (message, data) => console.error(JSON.stringify(formatLogRecord('error', message, [], data))),\r\n debug: (message, data) => console.log(JSON.stringify(formatLogRecord('debug', message, [], data))),\r\n};\r\n\r\nlet sink: Logger = consoleLogger;\r\n\r\n/**\r\n * Installs the host's logger. Called once at import scope by src/server/core/logger.ts, and by tests\r\n * that want to capture output.\r\n *\r\n * This is the one piece of module-level mutable state in this package, and it is deliberate: `logger`\r\n * below is used at import scope by fifteen modules here, several of them (model-output/*, rules/*,\r\n * finding-gates.ts) pure functions with no runtime parameter to hang a port off. Threading a Logger\r\n * argument through all of them would be by far the largest and least mechanical part of the\r\n * extraction, for no behavioural gain.\r\n */\r\nexport function setLoggerSink(next: Logger) {\r\n sink = next;\r\n}\r\n\r\nexport const logger: Logger = {\r\n info: (message, data) => sink.info(message, data),\r\n warn: (message, data) => sink.warn(message, data),\r\n error: (message, data) => sink.error(message, data),\r\n debug: (message, data) => sink.debug(message, data),\r\n};\r\n"],"mappings":";AAgBA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,MAAM;AACZ,IAAM,SAAS;AAER,SAAS,YAAY,OAAuB;AACjD,SAAO,MAAM,QAAQ,KAAK,gBAAgB,EAAE,QAAQ,QAAQ,CAAC,MAAM,GAAG,EAAE,MAAM,KAAK,EAAE,CAAC,CAAC,aAAa;AACtG;AAEO,SAAS,OAAO,KAAe;AACpC,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,MAAI,OAAO,QAAQ,UAAU;AAC3B,WAAO,OAAO,QAAQ,WAAW,YAAY,GAAG,IAAI;AAAA,EACtD;AACA,MAAI,MAAM,QAAQ,GAAG,EAAG,QAAO,IAAI,IAAI,MAAM;AAC7C,MAAI,eAAe,OAAO;AACxB,WAAO;AAAA,MACL,MAAM,IAAI;AAAA,MACV,SAAS,YAAY,IAAI,OAAO;AAAA,MAChC,GAAI,IAAI,QAAQ,EAAE,OAAO,YAAY,IAAI,KAAK,EAAE,IAAI,CAAC;AAAA,IACvD;AAAA,EACF;AAEA,QAAM,WAAgB,CAAC;AACvB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,UAAM,WAAW,IAAI,YAAY;AACjC,QAAI,eAAe,KAAK,CAAC,OAAO,SAAS,SAAS,EAAE,CAAC,GAAG;AACtD,eAAS,GAAG,IAAI;AAAA,IAClB,OAAO;AACL,eAAS,GAAG,IAAI,OAAO,KAAK;AAAA,IAC9B;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,gBACd,OACA,SACA,UACA,MACyB;AACzB,SAAO;AAAA,IACL,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,IACA,SAAS,YAAY,OAAO;AAAA,IAC5B,GAAG,SAAS,OAAgC,CAAC,QAAQ,YAAY,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;AAAA,IAC3G,GAAI,OAAO,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,CAAC;AAAA,EACvC;AACF;AAEO,IAAM,gBAAwB;AAAA,EACnC,MAAM,CAAC,SAAS,SAAS,QAAQ,IAAI,KAAK,UAAU,gBAAgB,QAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,EAC/F,MAAM,CAAC,SAAS,SAAS,QAAQ,KAAK,KAAK,UAAU,gBAAgB,QAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,EAChG,OAAO,CAAC,SAAS,SAAS,QAAQ,MAAM,KAAK,UAAU,gBAAgB,SAAS,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,EACnG,OAAO,CAAC,SAAS,SAAS,QAAQ,IAAI,KAAK,UAAU,gBAAgB,SAAS,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AACnG;AAEA,IAAI,OAAe;AAYZ,SAAS,cAAc,MAAc;AAC1C,SAAO;AACT;AAEO,IAAM,SAAiB;AAAA,EAC5B,MAAM,CAAC,SAAS,SAAS,KAAK,KAAK,SAAS,IAAI;AAAA,EAChD,MAAM,CAAC,SAAS,SAAS,KAAK,KAAK,SAAS,IAAI;AAAA,EAChD,OAAO,CAAC,SAAS,SAAS,KAAK,MAAM,SAAS,IAAI;AAAA,EAClD,OAAO,CAAC,SAAS,SAAS,KAAK,MAAM,SAAS,IAAI;AACpD;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/model-output/index.ts","../src/model-output/json.ts","../src/model-output/evidence.ts","../src/model-output/dedupe.ts","../src/model-output/non-answer.ts","../src/model-output/json-batch.ts","../src/model-output/batch.ts"],"sourcesContent":["import {\r\n fileReviewModelOutputSchema,\r\n parsedReviewCommentSchema,\r\n toClaimType,\r\n CLAIM_TYPE_CATEGORY,\r\n type ClaimType,\r\n type ParsedReviewComment,\r\n reviewSeverities,\r\n} from '@codraoss/schema';\r\nimport { renderDiffSnippet } from '../prompts/verify';\r\nimport { logger } from '../logger';\r\nimport { z } from 'zod';\r\nimport { findPositionForLine, getValidPositions, type DiffLine, type FileDiff } from '../diff';\r\nimport {\r\n buildAnchorHash,\r\n buildFindingFingerprint,\r\n buildFindingFingerprintV2,\r\n} from '../fingerprint';\r\nimport {\r\n buildPresenceIndex,\r\n checkAbsenceClaim,\r\n isVersionClaimRefutedByPin,\r\n looksLikeExternalVersionClaim,\r\n refuteUndecidableClaim,\r\n} from '../claim-checks';\r\nimport { parseRawPayload } from './json';\r\nimport {\r\n type BinAmbiguityIndex,\r\n type EvidenceIndex,\r\n buildEvidenceIndex,\r\n foldFirstEvidenceLine,\r\n resolveEvidence,\r\n} from './evidence';\r\n\r\nexport function samePath(a: string, b: string): boolean {\r\n const strip = (p: string) => p.trim().replace(/^\\.\\//, '').replace(/^[ab]\\//, '').replace(/^\\//, '');\r\n return strip(a) === strip(b);\r\n}\r\n\r\nexport type BinAmbiguity = {\r\n index: BinAmbiguityIndex;\r\n filePath: string;\r\n stats: { ambiguousAcrossBin: number };\r\n};\r\n\r\nfunction withSuggestion(body: string, codeSuggestion?: string) {\r\n if (!codeSuggestion) return body;\r\n\r\n const cleanSuggestion = codeSuggestion.replace(/```suggestion\\n?|```/g, '').trim();\r\n\r\n const cleanBody = body.split('```suggestion')[0].trim();\r\n\r\n return `${cleanBody}\\n\\n\\`\\`\\`suggestion\\n${cleanSuggestion}\\n\\`\\`\\``;\r\n}\r\n\r\nconst CLAIM_TYPE_REPAIRS: ReadonlyArray<{ pattern: RegExp; claimType: ClaimType }> = [\r\n { pattern: /dependenc(?:y|ies)\\s+array|exhaustive[- ]deps/i, claimType: 'react_hook_missing_deps' },\r\n { pattern: /redos|catastrophic backtrack|exponential backtrack/i, claimType: 'redos_regex' },\r\n];\r\n\r\nfunction repairClaimType(claimType: ClaimType, title: string, body: string, onRepair: () => void): ClaimType {\r\n if (claimType !== 'other') return claimType;\r\n const text = `${title}\\n${body}`;\r\n\r\n if (looksLikeExternalVersionClaim(title, body)) {\r\n onRepair();\r\n return 'external_version_claim';\r\n }\r\n\r\n for (const { pattern, claimType: repaired } of CLAIM_TYPE_REPAIRS) {\r\n if (pattern.test(text)) {\r\n onRepair();\r\n return repaired;\r\n }\r\n }\r\n return claimType;\r\n}\r\n\r\ntype RawFinding = z.infer<typeof fileReviewModelOutputSchema>['findings'][number];\r\n\r\ntype Withheld = { title: string; body: string; tag?: string };\r\n\r\nfunction formatWithheld(w: Withheld): string {\r\n return w.tag ? `- **[${w.tag}] ${w.title}:** ${w.body}` : `- **${w.title}:** ${w.body}`;\r\n}\r\n\r\nfunction groundFindingInEvidence(\r\n finding: RawFinding,\r\n evidenceIndex: EvidenceIndex,\r\n evidenceStats: { total: number; matched: number; unmatched: number; weak: number; absent: number },\r\n ambiguity?: BinAmbiguity,\r\n): { diffLine: DiffLine } | { withheld: Withheld } {\r\n const reportedLine = finding.code_location.line || finding.code_location.line_range?.start;\r\n\r\n evidenceStats.total += 1;\r\n const evidence = resolveEvidence(finding.evidence, evidenceIndex, reportedLine);\r\n if (evidence.status === 'matched') evidenceStats.matched += 1;\r\n else if (evidence.status === 'unmatched') evidenceStats.unmatched += 1;\r\n else if (evidence.status === 'weak') evidenceStats.weak += 1;\r\n else if (evidence.status === 'absent') evidenceStats.absent += 1;\r\n\r\n if (evidence.status !== 'matched') {\r\n return { withheld: { title: finding.title, body: finding.body, tag: `unverified:${evidence.status}` } };\r\n }\r\n\r\n if (ambiguity) {\r\n const firstLine = foldFirstEvidenceLine(finding.evidence);\r\n const claimedPath = finding.code_location.absolute_file_path?.trim();\r\n const ambiguousAcrossBin = firstLine ? (ambiguity.index.get(firstLine) ?? 0) > 1 : false;\r\n if (ambiguousAcrossBin && claimedPath && !samePath(claimedPath, ambiguity.filePath)) {\r\n ambiguity.stats.ambiguousAcrossBin += 1;\r\n return {\r\n withheld: {\r\n title: finding.title,\r\n body: finding.body,\r\n tag: 'unverified:ambiguous-across-bin',\r\n },\r\n };\r\n }\r\n }\r\n\r\n return { diffLine: evidence.line };\r\n}\r\n\r\nfunction anchorToDiffPosition(\r\n file: FileDiff,\r\n diffLine: DiffLine,\r\n validPositions: Set<number>,\r\n finding: RawFinding,\r\n): { line: number; position: number } | { withheld: Withheld } {\r\n const line = diffLine.newLineNumber!;\r\n const position = findPositionForLine(file, line);\r\n\r\n if (position === undefined || !validPositions.has(position)) {\r\n return { withheld: { title: finding.title, body: finding.body } };\r\n }\r\n\r\n return { line, position };\r\n}\r\n\r\nfunction validateFindingShape(finding: RawFinding): { severity: typeof reviewSeverities[number]; title: string; body: string } {\r\n const priorityMap: Record<number, typeof reviewSeverities[number]> = {\r\n 0: 'P0',\r\n 1: 'P1',\r\n 2: 'P2',\r\n 3: 'P3',\r\n 4: 'nit',\r\n };\r\n const severity = finding.priority !== undefined\r\n ? priorityMap[finding.priority] || 'P3'\r\n : 'P3';\r\n\r\n const cleanText = (text: string) => {\r\n let current = text.trim();\r\n let prev = '';\r\n while (current !== prev) {\r\n prev = current;\r\n current = current\r\n .replace(/^(?:[^\\w\\s]+|(?:QUALITY|SECURITY|BUG|PERFORMANCE|CORRECTNESS|P[0-3]|NIT)\\b)+/giu, '')\r\n .replace(/\\n\\s*/g, ' ')\r\n .trim();\r\n }\r\n return current;\r\n };\r\n\r\n const title = cleanText(finding.title);\r\n let body = cleanText(finding.body);\r\n\r\n const bodyPrefix = cleanText(body.split('\\n')[0]);\r\n if (bodyPrefix.toLowerCase().startsWith(title.toLowerCase()) || title.toLowerCase().startsWith(bodyPrefix.toLowerCase())) {\r\n body = cleanText(body.slice(body.split('\\n')[0].length));\r\n }\r\n\r\n return { severity, title, body };\r\n}\r\n\r\nfunction applyClaimGate(\r\n finding: RawFinding,\r\n title: string,\r\n body: string,\r\n anchorContent: string,\r\n deniedClaimTypes: Set<ClaimType>,\r\n claimTypeCounts: Record<string, number>,\r\n deniedClaimCounts: Record<string, number>,\r\n): { claimType: ClaimType } | { withheld: Withheld } {\r\n const claimType = repairClaimType(toClaimType(finding.claim_type), title, body, () => {\r\n claimTypeCounts.__repaired = (claimTypeCounts.__repaired ?? 0) + 1;\r\n });\r\n\r\n claimTypeCounts[claimType] = (claimTypeCounts[claimType] ?? 0) + 1;\r\n\r\n if (deniedClaimTypes.has(claimType)) {\r\n deniedClaimCounts[claimType] = (deniedClaimCounts[claimType] ?? 0) + 1;\r\n return { withheld: { title, body, tag: `claim-denied:${claimType}` } };\r\n }\r\n\r\n if (isVersionClaimRefutedByPin({ title, body, anchorContent })) {\r\n deniedClaimCounts.version_claim_on_pinned_sha = (deniedClaimCounts.version_claim_on_pinned_sha ?? 0) + 1;\r\n return { withheld: { title, body, tag: 'refuted:pinned-sha' } };\r\n }\r\n\r\n const undecidable = refuteUndecidableClaim({ title, body });\r\n if (undecidable) {\r\n const key = `undecidable_${undecidable.replace('-', '_')}`;\r\n deniedClaimCounts[key] = (deniedClaimCounts[key] ?? 0) + 1;\r\n return { withheld: { title, body, tag: `refuted:${undecidable}` } };\r\n }\r\n\r\n return { claimType };\r\n}\r\n\r\nfunction buildParsedComment(params: {\r\n file: FileDiff;\r\n line: number;\r\n position: number;\r\n severity: typeof reviewSeverities[number];\r\n title: string;\r\n body: string;\r\n claimType: ClaimType;\r\n anchorContent: string;\r\n finding: RawFinding;\r\n presenceIndex: ReturnType<typeof buildPresenceIndex>;\r\n absenceCheckStats: { absenceShaped: number; identifierExtracted: number; refuted: number };\r\n}): ParsedReviewComment {\r\n const { file, line, position, severity, title, body, claimType, anchorContent, finding, presenceIndex, absenceCheckStats } = params;\r\n\r\n const absence = checkAbsenceClaim({ title, body, anchorLine: line, index: presenceIndex });\r\n if (absence.status === 'refuted') {\r\n absenceCheckStats.absenceShaped += 1;\r\n absenceCheckStats.identifierExtracted += 1;\r\n absenceCheckStats.refuted += 1;\r\n } else if (absence.reason !== 'not_absence_shaped') {\r\n absenceCheckStats.absenceShaped += 1;\r\n if (absence.reason !== 'no_identifier' && absence.reason !== 'ambiguous_identifier') {\r\n absenceCheckStats.identifierExtracted += 1;\r\n }\r\n }\r\n\r\n const confidenceScore = typeof finding.confidence_score === 'number'\r\n ? finding.confidence_score\r\n : 0;\r\n\r\n const codeSuggestion = typeof finding.code_suggestion === 'string' && finding.code_suggestion.trim()\r\n ? finding.code_suggestion\r\n : undefined;\r\n\r\n return parsedReviewCommentSchema.parse({\r\n path: file.path,\r\n line,\r\n position,\r\n severity,\r\n category: CLAIM_TYPE_CATEGORY[claimType],\r\n claimType,\r\n contextSnippet: renderDiffSnippet(file, line) || undefined,\r\n title,\r\n body: withSuggestion(body, codeSuggestion),\r\n codeSuggestion,\r\n confidenceScore,\r\n evidence: typeof finding.evidence === 'string' && finding.evidence.trim() ? finding.evidence.trim() : undefined,\r\n fingerprint: buildFindingFingerprint(file.path, title),\r\n anchorHash: anchorContent ? buildAnchorHash(anchorContent) : undefined,\r\n fingerprintV2: buildFindingFingerprintV2(\r\n file.path,\r\n claimType,\r\n anchorContent ? buildAnchorHash(anchorContent) : undefined,\r\n ) ?? undefined,\r\n });\r\n}\r\n\r\nexport type FileReviewPayload = z.infer<typeof fileReviewModelOutputSchema>;\r\n\r\nexport type GroundingOptions = {\r\n deniedClaimTypes?: readonly ClaimType[];\r\n ambiguity?: BinAmbiguity;\r\n};\r\n\r\nexport type GroundedFileReview = {\r\n comments: ParsedReviewComment[];\r\n verdict: 'approve' | 'comment';\r\n fileSummary: string;\r\n overallCorrectness?: string;\r\n confidenceScore?: number;\r\n evidenceStats: { total: number; matched: number; unmatched: number; weak: number; absent: number };\r\n claimTypeCounts: Record<string, number>;\r\n deniedClaimCounts: Record<string, number>;\r\n absenceCheckStats: { absenceShaped: number; identifierExtracted: number; refuted: number };\r\n};\r\n\r\nexport function groundParsedFindings(\r\n parsed: FileReviewPayload,\r\n file: FileDiff,\r\n options?: GroundingOptions,\r\n): GroundedFileReview {\r\n const validPositions = getValidPositions(file);\r\n const evidenceIndex = buildEvidenceIndex(file);\r\n const evidenceStats = { total: 0, matched: 0, unmatched: 0, weak: 0, absent: 0 };\r\n const claimTypeCounts: Record<string, number> = {};\r\n const deniedClaimCounts: Record<string, number> = {};\r\n const deniedClaimTypes = new Set<ClaimType>(options?.deniedClaimTypes ?? []);\r\n const presenceIndex = buildPresenceIndex(file);\r\n const absenceCheckStats = { absenceShaped: 0, identifierExtracted: 0, refuted: 0 };\r\n const orphanedComments: string[] = [];\r\n\r\n const comments = (parsed.findings || [])\r\n .map((finding): ParsedReviewComment | null => {\r\n const grounded = groundFindingInEvidence(finding, evidenceIndex, evidenceStats, options?.ambiguity);\r\n if ('withheld' in grounded) {\r\n orphanedComments.push(formatWithheld(grounded.withheld));\r\n return null;\r\n }\r\n\r\n const anchored = anchorToDiffPosition(file, grounded.diffLine, validPositions, finding);\r\n if ('withheld' in anchored) {\r\n orphanedComments.push(formatWithheld(anchored.withheld));\r\n return null;\r\n }\r\n\r\n const { severity, title, body } = validateFindingShape(finding);\r\n\r\n const anchorContent = grounded.diffLine.content\r\n ?? file.hunks.flatMap((h) => h.lines).find((l) => l.newLineNumber === anchored.line)?.content\r\n ?? '';\r\n\r\n const gated = applyClaimGate(finding, title, body, anchorContent, deniedClaimTypes, claimTypeCounts, deniedClaimCounts);\r\n if ('withheld' in gated) {\r\n orphanedComments.push(formatWithheld(gated.withheld));\r\n return null;\r\n }\r\n\r\n try {\r\n return buildParsedComment({\r\n file,\r\n line: anchored.line,\r\n position: anchored.position,\r\n severity,\r\n title,\r\n body,\r\n claimType: gated.claimType,\r\n anchorContent,\r\n finding,\r\n presenceIndex,\r\n absenceCheckStats,\r\n });\r\n } catch (error) {\r\n if (!(error instanceof z.ZodError)) throw error;\r\n\r\n orphanedComments.push(formatWithheld({\r\n title: finding.title,\r\n body: finding.body,\r\n tag: 'unverified:unassemblable',\r\n }));\r\n logger.warn('Dropped a finding that could not be assembled', {\r\n path: file.path,\r\n title: finding.title,\r\n error: error.message,\r\n });\r\n return null;\r\n }\r\n })\r\n .filter((comment): comment is ParsedReviewComment => Boolean(comment));\r\n\r\n const verdict = parsed.overall_correctness.toLowerCase().includes('patch is correct') ? 'approve' : 'comment';\r\n let fileSummary = parsed.overall_explanation;\r\n\r\n if (orphanedComments.length > 0) {\r\n fileSummary += `\\n\\n### Additional Comments (Off-diff)\\n${orphanedComments.join('\\n')}`;\r\n }\r\n\r\n return {\r\n comments,\r\n verdict: comments.length > 0 ? 'comment' : verdict,\r\n fileSummary,\r\n overallCorrectness: parsed.overall_correctness,\r\n confidenceScore: parsed.overall_confidence_score,\r\n evidenceStats,\r\n claimTypeCounts,\r\n deniedClaimCounts,\r\n absenceCheckStats,\r\n };\r\n}\r\n\r\nexport function parseFileReviewResponse(\r\n raw: string,\r\n file: FileDiff,\r\n options?: GroundingOptions,\r\n): GroundedFileReview {\r\n return groundParsedFindings(parseRawPayload(raw), file, options);\r\n}\r\n\r\n\r\nexport { dedupeFindings } from './dedupe';\r\nexport { isNonAnswerReview } from './non-answer';\r\nexport {\r\n NON_ANSWER_MAX_RESPONSE_CHARS,\r\n NON_ANSWER_MIN_DIFF_LINES,\r\n} from '../constants';\r\nexport { parseRawBatchPayload, type RawBatchPayload } from './json-batch';\r\nexport { parseBatchReviewResponse, type BatchParseStats, type BatchReviewResult } from './batch';\r\n","import { fileReviewModelOutputSchema } from '@codraoss/schema';\r\nimport { jsonrepair } from 'jsonrepair';\r\nimport { z } from 'zod';\r\nimport { logger } from '../logger';\r\n\r\nimport { MAX_LOGGED_JSON_CHARS } from '../constants';\r\n\r\nexport function truncateJsonForLog(value: string) {\r\n if (value.length <= MAX_LOGGED_JSON_CHARS) return value;\r\n return `${value.slice(0, MAX_LOGGED_JSON_CHARS)}... [truncated ${value.length - MAX_LOGGED_JSON_CHARS} chars]`;\r\n}\r\n\r\nexport function hasReviewKeys(input: string) {\r\n return /\"(files|findings|overall_explanation|overall_correctness|overall_confidence_score|summary)\"\\s*:/.test(input);\r\n}\r\n\r\nfunction scanBalanced(raw: string, startIdx: number, open: string, close: string): string | null {\r\n let stack = 0;\r\n let inString = false;\r\n let escape = false;\r\n\r\n for (let i = startIdx; i < raw.length; i++) {\r\n const char = raw[i];\r\n\r\n if (escape) {\r\n escape = false;\r\n continue;\r\n }\r\n if (char === '\\\\') {\r\n escape = true;\r\n continue;\r\n }\r\n if (char === '\"') {\r\n inString = !inString;\r\n continue;\r\n }\r\n if (inString) continue;\r\n\r\n if (char === open) stack++;\r\n else if (char === close) {\r\n stack--;\r\n if (stack === 0) return raw.slice(startIdx, i + 1);\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n\r\nexport function extractJson(raw: string, anchorKey: 'findings' | 'files' = 'findings') {\r\n const jsonBlocks = Array.from(raw.matchAll(/```json([\\s\\S]*?)```/gi));\r\n if (jsonBlocks.length > 0) {\r\n return jsonBlocks[jsonBlocks.length - 1][1].trim();\r\n }\r\n\r\n const genericBlocks = Array.from(raw.matchAll(/```(?:[\\w+-]+)?([\\s\\S]*?)```/gi));\r\n if (genericBlocks.length > 0) {\r\n const candidates = genericBlocks.filter(b => b[1].includes('{') && b[1].includes('}') && hasReviewKeys(b[1]));\r\n if (candidates.length > 0) {\r\n const content = candidates[candidates.length - 1][1].trim();\r\n const start = content.indexOf('{');\r\n const end = content.lastIndexOf('}');\r\n if (start !== -1 && end !== -1 && end > start) {\r\n return content.slice(start, end + 1);\r\n }\r\n return content;\r\n }\r\n }\r\n\r\n const arrayStart = raw.indexOf('[');\r\n if (arrayStart !== -1 && raw.slice(0, arrayStart).trim() === '') {\r\n const matched = scanBalanced(raw, arrayStart, '[', ']');\r\n if (matched && hasReviewKeys(matched)) return matched;\r\n }\r\n\r\n const anchorIdx = anchorKey === 'files' ? raw.indexOf('\"files\"') : -1;\r\n const findingsIdx = anchorIdx !== -1 ? anchorIdx : raw.indexOf('\"findings\"');\r\n const summaryIdx = raw.indexOf('\"summary\"');\r\n const targetIdx = findingsIdx !== -1 ? findingsIdx : (summaryIdx !== -1 ? summaryIdx : -1);\r\n\r\n let firstBrace = -1;\r\n if (targetIdx !== -1) {\r\n firstBrace = raw.lastIndexOf('{', targetIdx);\r\n }\r\n\r\n if (firstBrace === -1) {\r\n const allBraces = Array.from(raw.matchAll(/\\{/g));\r\n let bestIdx = -1;\r\n let bestScore = -1;\r\n\r\n for (const match of allBraces) {\r\n const idx = match.index!;\r\n const excerpt = raw.slice(idx, idx + 200);\r\n let score = 0;\r\n\r\n if (excerpt.includes('\"files\"')) score += 100;\r\n if (excerpt.includes('\"findings\"')) score += 100;\r\n if (excerpt.includes('\"summary\"')) score += 50;\r\n if (excerpt.includes('\"overall_explanation\"')) score += 50;\r\n\r\n if (excerpt.includes('\" : ') || excerpt.includes('\":')) score += 10;\r\n if (excerpt.includes('\"[')) score += 5;\r\n\r\n if (excerpt.includes(': number;') || excerpt.includes(': string;')) score -= 80;\r\n if (excerpt.includes('export ') || excerpt.includes('function ')) score -= 80;\r\n if (excerpt.includes('interface ') || excerpt.includes('type ')) score -= 80;\r\n if (excerpt.includes(' + ')) score -= 20; // Looks like a diff hunk\r\n\r\n if (score > bestScore) {\r\n bestScore = score;\r\n bestIdx = idx;\r\n }\r\n }\r\n\r\n if (bestIdx !== -1 && bestScore > 0) {\r\n firstBrace = bestIdx;\r\n }\r\n }\r\n\r\n if (firstBrace === -1) {\r\n const start = raw.indexOf('{');\r\n if (start !== -1) {\r\n const excerpt = raw.slice(start, start + 50);\r\n if (excerpt.includes('\"') && excerpt.includes(':')) {\r\n firstBrace = start;\r\n }\r\n }\r\n }\r\n\r\n if (firstBrace !== -1) {\r\n let stack = 0;\r\n let inString = false;\r\n let escape = false;\r\n\r\n for (let i = firstBrace; i < raw.length; i++) {\r\n const char = raw[i];\r\n\r\n if (escape) {\r\n escape = false;\r\n continue;\r\n }\r\n\r\n if (char === '\\\\') {\r\n escape = true;\r\n continue;\r\n }\r\n\r\n if (char === '\"') {\r\n inString = !inString;\r\n continue;\r\n }\r\n\r\n if (!inString) {\r\n if (char === '{') stack++;\r\n else if (char === '}') {\r\n stack--;\r\n if (stack === 0) {\r\n return raw.slice(firstBrace, i + 1);\r\n }\r\n }\r\n }\r\n }\r\n\r\n const partial = raw.slice(firstBrace).trim();\r\n let closing = '';\r\n if (inString) closing += '\"';\r\n closing += '}'.repeat(Math.max(1, stack));\r\n return `${partial}${closing}`;\r\n }\r\n\r\n return raw.trim();\r\n}\r\n\r\nexport function preprocessJson(json: string): string {\r\n let result = '';\r\n let inString = false;\r\n let escape = false;\r\n\r\n for (let i = 0; i < json.length; i++) {\r\n const char = json[i];\r\n\r\n if (escape) {\r\n result += char;\r\n escape = false;\r\n continue;\r\n }\r\n\r\n if (char === '\\\\') {\r\n result += char;\r\n escape = true;\r\n continue;\r\n }\r\n\r\n if (char === '\"') {\r\n inString = !inString;\r\n result += char;\r\n continue;\r\n }\r\n\r\n if (inString) {\r\n if (char === '\\n') {\r\n result += '\\\\n';\r\n } else if (char === '\\r') {\r\n result += '\\\\r';\r\n } else {\r\n result += char;\r\n }\r\n } else {\r\n result += char;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Deletes every `null`-valued key, recursively, before Zod sees the payload.\r\n *\r\n * The model output schemas mark optional fields `.optional()`, which accepts an ABSENT key and rejects\r\n * an explicit `null` -- and these models routinely emit `\"code_suggestion\": null` for a finding that\r\n * carries no suggestion. On the batched path that single null failed\r\n * `batchReviewModelOutputSchema.parse`, so `parseBatchReviewResponse` threw and the response for EVERY\r\n * file in the bin was discarded, then reported as an unreadable answer and failed over to the next\r\n * model. Measured on this repository's own review: 37 of 88 rejected payloads were otherwise complete\r\n * and readable.\r\n *\r\n * Stripping rather than widening each field is deliberate: absent and null mean the same thing to every\r\n * one of these schemas, one pass covers the fields nobody has thought of yet, and no downstream type\r\n * has to learn about `null`.\r\n */\r\nexport function stripNulls<T>(value: T): T {\r\n if (Array.isArray(value)) return value.map(stripNulls) as unknown as T;\r\n if (value === null || typeof value !== 'object') return value;\r\n\r\n const out: Record<string, unknown> = {};\r\n for (const [key, entry] of Object.entries(value as Record<string, unknown>)) {\r\n if (entry === null) continue;\r\n out[key] = stripNulls(entry);\r\n }\r\n return out as T;\r\n}\r\n\r\nfunction isPlaceholderString(value: unknown) {\r\n return typeof value === 'string' && /^<[^>]+>$/.test(value.trim());\r\n}\r\n\r\nfunction coerceReviewNumber(value: unknown) {\r\n if (typeof value === 'number' && Number.isFinite(value)) return value;\r\n if (typeof value === 'string' && !isPlaceholderString(value)) {\r\n const parsed = Number(value);\r\n if (Number.isFinite(parsed)) return parsed;\r\n }\r\n return undefined;\r\n}\r\n\r\nexport function normalizeFinding(finding: unknown) {\r\n if (!finding || typeof finding !== 'object') return null;\r\n const f = finding as Record<string, unknown>;\r\n if (isPlaceholderString(f.title) || isPlaceholderString(f.body) || isPlaceholderString(f.evidence)) return null;\r\n\r\n const location = f.code_location && typeof f.code_location === 'object' ? (f.code_location as Record<string, unknown>) : {};\r\n const line = coerceReviewNumber(location.line);\r\n const start = coerceReviewNumber(location.line_range && typeof location.line_range === 'object' ? (location.line_range as Record<string, unknown>).start : undefined);\r\n const end = coerceReviewNumber(location.line_range && typeof location.line_range === 'object' ? (location.line_range as Record<string, unknown>).end : undefined);\r\n const priority = coerceReviewNumber(f.priority);\r\n\r\n const codeLocation: Record<string, unknown> = {\r\n absolute_file_path: location.absolute_file_path || f.path || '',\r\n };\r\n if (line !== undefined) {\r\n codeLocation.line = Math.trunc(line as number);\r\n }\r\n if (start !== undefined || end !== undefined) {\r\n codeLocation.line_range = {\r\n start: Math.trunc((start as number) ?? (end as number)!),\r\n end: Math.trunc((end as number) ?? (start as number)!),\r\n };\r\n }\r\n\r\n return {\r\n ...f,\r\n title: (f.title ? String(f.title) : '').trim().slice(0, 100).replace(/[\\uD800-\\uDBFF]$/, '') || 'Code finding',\r\n priority: priority === undefined ? undefined : Math.max(0, Math.min(4, Math.trunc(priority as number))),\r\n code_location: codeLocation,\r\n confidence_score: typeof f.confidence_score === 'number'\r\n ? Math.max(0, Math.min(1, f.confidence_score > 1 ? f.confidence_score / 10 : f.confidence_score))\r\n : undefined,\r\n };\r\n}\r\n\r\nexport function parseRawPayload(raw: string): z.infer<typeof fileReviewModelOutputSchema> {\r\n let extracted: string;\r\n try {\r\n extracted = extractJson(raw);\r\n if (!hasReviewKeys(extracted)) {\r\n throw new Error('Model response did not contain review JSON keys.');\r\n }\r\n } catch (e) {\r\n logger.error('Failed to extract JSON from model response', {\r\n rawLength: raw.length,\r\n rawPrefix: raw.slice(0, 500),\r\n error: e instanceof Error ? e.message : String(e),\r\n });\r\n throw new Error('Could not find JSON root in model response.', { cause: e });\r\n }\r\n\r\n let preprocessed: string;\r\n try {\r\n preprocessed = preprocessJson(extracted);\r\n } catch (e) {\r\n logger.warn('JSON preprocessing partially failed, continuing...', { extracted, error: e });\r\n preprocessed = extracted;\r\n }\r\n\r\n let repaired = preprocessed;\r\n try {\r\n repaired = jsonrepair(preprocessed);\r\n } catch (e) {\r\n logger.warn('jsonrepair failed to fix model output, using preprocessed text', { preprocessed: truncateJsonForLog(preprocessed), error: e });\r\n }\r\n\r\n let parsedJson: unknown;\r\n try {\r\n parsedJson = stripNulls(JSON.parse(repaired));\r\n } catch (e) {\r\n logger.error('Critical JSON parse error after extraction and repair', { repaired: truncateJsonForLog(repaired), error: e });\r\n throw new Error(`Invalid JSON format: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e });\r\n }\r\n\r\n try {\r\n const findReviewObject = (arr: unknown[]): unknown | null => {\r\n const best = arr.find(i => i && typeof i === 'object' && Array.isArray((i as Record<string, unknown>).findings) && typeof (i as Record<string, unknown>).summary === 'string');\r\n if (best) return best;\r\n\r\n const good = arr.find(i => i && typeof i === 'object' && Array.isArray((i as Record<string, unknown>).findings));\r\n if (good) return good;\r\n\r\n return arr.find(i =>\r\n i && typeof i === 'object' &&\r\n ('findings' in i || 'overall_explanation' in i || 'summary' in i || 'overall_correctness' in i)\r\n );\r\n };\r\n\r\n let data = Array.isArray(parsedJson) ? (findReviewObject(parsedJson) || parsedJson[0] || {}) : parsedJson;\r\n\r\n if (data && typeof data === 'object') {\r\n const obj = data as Record<string, unknown>;\r\n if (!obj.findings) obj.findings = [];\r\n if (!obj.overall_explanation) obj.overall_explanation = 'No explanation provided.';\r\n if (!obj.overall_correctness) obj.overall_correctness = 'Uncertain';\r\n\r\n if (typeof obj.overall_confidence_score === 'number') {\r\n if (obj.overall_confidence_score > 1) {\r\n obj.overall_confidence_score = Math.min(obj.overall_confidence_score / 10, 1);\r\n } else if (obj.overall_confidence_score < 0) {\r\n obj.overall_confidence_score = 0;\r\n }\r\n } else {\r\n obj.overall_confidence_score = 0.5;\r\n }\r\n\r\n if (Array.isArray(obj.findings)) {\r\n obj.findings = obj.findings.flatMap((finding: unknown) => {\r\n const normalized = normalizeFinding(finding);\r\n return normalized ? [normalized] : [];\r\n });\r\n }\r\n data = obj;\r\n }\r\n\r\n return fileReviewModelOutputSchema.parse(data);\r\n } catch (e) {\r\n logger.error('Model response failed schema validation', { parsedJson, error: e });\r\n throw new Error(`Response schema mismatch: ${e instanceof Error ? e.message : 'Check logs'}`, { cause: e });\r\n }\r\n}\r\n","import { foldEvidenceText } from '../fingerprint';\r\nimport type { DiffLine, FileDiff } from '../diff';\r\n\r\nimport { MIN_DISCRIMINATING_EVIDENCE_CHARS } from '../constants';\r\n\r\nexport type EvidenceIndex = {\r\n byContent: Map<string, DiffLine[]>;\r\n lines: { normalized: string; line: DiffLine }[];\r\n};\r\n\r\nexport function buildEvidenceIndex(file: FileDiff): EvidenceIndex {\r\n const byContent = new Map<string, DiffLine[]>();\r\n const lines: { normalized: string; line: DiffLine }[] = [];\r\n\r\n for (const hunk of file.hunks) {\r\n const postable = hunk.lines.filter((line) => line.kind !== 'del' && line.newLineNumber !== undefined);\r\n if (postable.length === 0) continue;\r\n\r\n hunk.lines.forEach((line, lineIndex) => {\r\n const normalized = foldEvidenceText(line.content);\r\n if (!normalized) return;\r\n\r\n let anchor = line;\r\n if (line.kind === 'del' || line.newLineNumber === undefined) {\r\n anchor = hunk.lines.slice(lineIndex + 1).find((l) => l.kind !== 'del' && l.newLineNumber !== undefined)\r\n ?? hunk.lines.slice(0, lineIndex).reverse().find((l) => l.kind !== 'del' && l.newLineNumber !== undefined)\r\n ?? postable[0];\r\n }\r\n\r\n lines.push({ normalized, line: anchor });\r\n const existing = byContent.get(normalized);\r\n if (existing) existing.push(anchor);\r\n else byContent.set(normalized, [anchor]);\r\n });\r\n }\r\n\r\n return { byContent, lines };\r\n}\r\n\r\nexport function foldFirstEvidenceLine(evidence: unknown): string | null {\r\n if (typeof evidence !== 'string') return null;\r\n return evidence.split('\\n').map(foldEvidenceText).find((l) => l.length > 0) ?? null;\r\n}\r\n\r\nexport type BinAmbiguityIndex = Map<string, number>;\r\n\r\nexport function buildBinAmbiguityIndex(files: readonly FileDiff[]): BinAmbiguityIndex {\r\n const filesPerLine = new Map<string, Set<string>>();\r\n\r\n for (const file of files) {\r\n for (const hunk of file.hunks) {\r\n for (const line of hunk.lines) {\r\n const normalized = foldEvidenceText(line.content);\r\n if (normalized.length < MIN_DISCRIMINATING_EVIDENCE_CHARS) continue;\r\n const paths = filesPerLine.get(normalized);\r\n if (paths) paths.add(file.path);\r\n else filesPerLine.set(normalized, new Set([file.path]));\r\n }\r\n }\r\n }\r\n\r\n const index: BinAmbiguityIndex = new Map();\r\n for (const [normalized, paths] of filesPerLine) {\r\n if (paths.size > 1) index.set(normalized, paths.size);\r\n }\r\n return index;\r\n}\r\n\r\nexport type EvidenceResolution =\r\n | { status: 'absent' }\r\n | { status: 'weak' }\r\n | { status: 'matched'; line: DiffLine }\r\n | { status: 'unmatched' };\r\n\r\nexport function resolveEvidence(\r\n evidence: unknown,\r\n index: EvidenceIndex,\r\n reportedLine: number | undefined,\r\n): EvidenceResolution {\r\n if (typeof evidence !== 'string') return { status: 'absent' };\r\n\r\n const firstLine = foldFirstEvidenceLine(evidence);\r\n if (!firstLine) return { status: 'absent' };\r\n if (firstLine.length < MIN_DISCRIMINATING_EVIDENCE_CHARS) return { status: 'weak' };\r\n\r\n const nearest = (candidates: DiffLine[]) => {\r\n if (reportedLine === undefined) return candidates[0];\r\n return candidates.reduce((best, candidate) =>\r\n Math.abs((candidate.newLineNumber ?? 0) - reportedLine) < Math.abs((best.newLineNumber ?? 0) - reportedLine)\r\n ? candidate\r\n : best,\r\n );\r\n };\r\n\r\n const exact = index.byContent.get(firstLine);\r\n if (exact && exact.length > 0) return { status: 'matched', line: nearest(exact) };\r\n\r\n const contained = index.lines.flatMap(({ normalized, line }) =>\r\n normalized.length >= MIN_DISCRIMINATING_EVIDENCE_CHARS\r\n && (normalized.includes(firstLine) || firstLine.includes(normalized))\r\n ? [line]\r\n : []);\r\n if (contained.length > 0) return { status: 'matched', line: nearest(contained) };\r\n\r\n return { status: 'unmatched' };\r\n}\r\n","import type { ParsedReviewComment } from '@codraoss/schema';\r\nimport { normalizeFindingTitle } from '../fingerprint';\r\n\r\nconst SEVERITY_RANK: Record<string, number> = { P0: 0, P1: 1, P2: 2, P3: 3, nit: 4 };\r\n\r\nconst NUL = String.fromCharCode(0);\r\n\r\nexport function dedupeFindings(comments: ParsedReviewComment[]): ParsedReviewComment[] {\r\n const best = new Map<string, ParsedReviewComment>();\r\n for (const comment of comments) {\r\n const key = comment.source === 'rule'\r\n ? `rule${NUL}${comment.ruleId ?? ''}${NUL}${comment.path}${NUL}${comment.anchorHash ?? ''}`\r\n : normalizeFindingTitle(comment.title);\r\n if (!key) {\r\n best.set(`__unique__${best.size}`, comment);\r\n continue;\r\n }\r\n const existing = best.get(key);\r\n if (!existing) {\r\n best.set(key, comment);\r\n continue;\r\n }\r\n const rank = SEVERITY_RANK[comment.severity] ?? 4;\r\n const existingRank = SEVERITY_RANK[existing.severity] ?? 4;\r\n const isBetter =\r\n rank < existingRank ||\r\n (rank === existingRank && (comment.confidenceScore ?? 0) > (existing.confidenceScore ?? 0));\r\n if (isBetter) best.set(key, comment);\r\n }\r\n return Array.from(best.values());\r\n}\r\n","\r\nimport type { FileDiff } from '../diff';\r\n\r\nimport {\r\n NON_ANSWER_MAX_RESPONSE_CHARS,\r\n NON_ANSWER_MIN_DIFF_LINES,\r\n} from '../constants';\r\n\r\n/**\r\n * True when a review response is a non-answer: a substantive diff dismissed in a sentence with no\r\n * findings. Deliberately conservative -- it must never fire on a small diff, and never when the model\r\n * actually engaged, because the cost of a false positive is an escalation that spends real quota.\r\n */\r\nexport function isNonAnswerReview(input: {\r\n rawText: string;\r\n file: Pick<FileDiff, 'lineCount'>;\r\n findingCount: number;\r\n minDiffLines?: number;\r\n}): boolean {\r\n if (input.findingCount > 0) return false;\r\n if (input.file.lineCount < (input.minDiffLines ?? NON_ANSWER_MIN_DIFF_LINES)) return false;\r\n return input.rawText.trim().length < NON_ANSWER_MAX_RESPONSE_CHARS;\r\n}\r\n","import { batchReviewModelOutputSchema, fileReviewModelOutputSchema } from '@codraoss/schema';\r\nimport { jsonrepair } from 'jsonrepair';\r\nimport { z } from 'zod';\r\nimport { logger } from '../logger';\r\nimport {\r\n extractJson,\r\n hasReviewKeys,\r\n normalizeFinding,\r\n parseRawPayload,\r\n preprocessJson,\r\n stripNulls,\r\n truncateJsonForLog,\r\n} from './json';\r\n\r\nfunction readEntryPath(entry: Record<string, unknown>): string | null {\r\n for (const key of ['absolute_file_path', 'path', 'file', 'file_path', 'filename'] as const) {\r\n const value = entry[key];\r\n if (typeof value === 'string' && value.trim()) return value.trim();\r\n }\r\n return null;\r\n}\r\n\r\nfunction normalizeConfidence(value: unknown): number | undefined {\r\n if (typeof value !== 'number' || !Number.isFinite(value)) return undefined;\r\n if (value > 1) return Math.min(value / 10, 1);\r\n if (value < 0) return 0;\r\n return value;\r\n}\r\n\r\nfunction normalizeBatchFileEntry(entry: unknown, fallbackPath?: string): unknown | null {\r\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return null;\r\n const e = entry as Record<string, unknown>;\r\n const path = readEntryPath(e) ?? fallbackPath;\r\n if (!path) return null;\r\n\r\n if (!Array.isArray(e.findings)) return null;\r\n\r\n return {\r\n absolute_file_path: path,\r\n findings: e.findings.flatMap((finding) => {\r\n const normalized = normalizeFinding(finding);\r\n return normalized ? [normalized] : [];\r\n }),\r\n overall_correctness: typeof e.overall_correctness === 'string' && e.overall_correctness ? e.overall_correctness : undefined,\r\n overall_explanation: typeof e.overall_explanation === 'string' && e.overall_explanation ? e.overall_explanation : undefined,\r\n overall_confidence_score: normalizeConfidence(e.overall_confidence_score),\r\n };\r\n}\r\n\r\nfunction collectBatchEntries(parsedJson: unknown): unknown[] | null {\r\n const root = parsedJson && typeof parsedJson === 'object' ? (parsedJson as Record<string, unknown>) : null;\r\n const files = root?.files ?? (Array.isArray(parsedJson) ? parsedJson : undefined);\r\n\r\n if (Array.isArray(files)) {\r\n return files.flatMap((entry) => {\r\n const normalized = normalizeBatchFileEntry(entry);\r\n return normalized ? [normalized] : [];\r\n });\r\n }\r\n if (files && typeof files === 'object') {\r\n return Object.entries(files as Record<string, unknown>).flatMap(([path, entry]) => {\r\n const normalized = normalizeBatchFileEntry(entry, path);\r\n return normalized ? [normalized] : [];\r\n });\r\n }\r\n return null;\r\n}\r\n\r\nexport type RawBatchPayload =\r\n | { shape: 'nested'; data: z.infer<typeof batchReviewModelOutputSchema> }\r\n | { shape: 'flat'; data: z.infer<typeof fileReviewModelOutputSchema> };\r\n\r\nexport function parseRawBatchPayload(raw: string): RawBatchPayload {\r\n let extracted: string;\r\n try {\r\n extracted = extractJson(raw, 'files');\r\n if (!hasReviewKeys(extracted)) {\r\n throw new Error('Model response did not contain review JSON keys.');\r\n }\r\n } catch (e) {\r\n logger.error('Failed to extract JSON from batched model response', {\r\n rawLength: raw.length,\r\n rawPrefix: raw.slice(0, 500),\r\n error: e instanceof Error ? e.message : String(e),\r\n });\r\n throw new Error('Could not find JSON root in batched model response.', { cause: e });\r\n }\r\n\r\n let preprocessed: string;\r\n try {\r\n preprocessed = preprocessJson(extracted);\r\n } catch (e) {\r\n logger.warn('JSON preprocessing partially failed, continuing...', { extracted, error: e });\r\n preprocessed = extracted;\r\n }\r\n\r\n let repaired = preprocessed;\r\n try {\r\n repaired = jsonrepair(preprocessed);\r\n } catch (e) {\r\n logger.warn('jsonrepair failed to fix batched model output, using preprocessed text', { preprocessed: truncateJsonForLog(preprocessed), error: e });\r\n }\r\n\r\n let parsedJson: unknown;\r\n try {\r\n parsedJson = stripNulls(JSON.parse(repaired));\r\n } catch (e) {\r\n logger.error('Critical JSON parse error after extraction and repair', { repaired: truncateJsonForLog(repaired), error: e });\r\n throw new Error(`Invalid JSON format: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e });\r\n }\r\n\r\n const entries = collectBatchEntries(parsedJson);\r\n const root = parsedJson && typeof parsedJson === 'object' && !Array.isArray(parsedJson)\r\n ? (parsedJson as Record<string, unknown>)\r\n : {};\r\n\r\n if (!entries?.length) {\r\n if (Array.isArray(root.findings)) return { shape: 'flat', data: parseRawPayload(raw) };\r\n logger.error('Batched model response contained no recognisable file entries', {\r\n parsedJson: truncateJsonForLog(JSON.stringify(parsedJson ?? null)),\r\n });\r\n throw new Error('Batched response contained no recognisable file entries.');\r\n }\r\n\r\n try {\r\n return {\r\n shape: 'nested',\r\n data: batchReviewModelOutputSchema.parse({\r\n files: entries,\r\n overall_confidence_score: normalizeConfidence(root.overall_confidence_score) ?? 0.5,\r\n }),\r\n };\r\n } catch (e) {\r\n logger.error('Batched model response failed schema validation', { parsedJson, error: e });\r\n throw new Error(`Batched response schema mismatch: ${e instanceof Error ? e.message : 'Check logs'}`, { cause: e });\r\n }\r\n}\r\n","import type { ClaimType } from '@codraoss/schema';\r\nimport type { FileDiff } from '../diff';\r\nimport { generatorFindingCap } from '../prompts/file-review';\r\nimport { logger } from '../logger';\r\nimport { buildBinAmbiguityIndex } from './evidence';\r\nimport { type GroundedFileReview, groundParsedFindings, samePath } from './index';\r\nimport { parseRawBatchPayload } from './json-batch';\r\n\r\nexport type BatchParseStats = {\r\n unroutableEntries: number;\r\n pathMismatchFindings: number;\r\n ambiguousAcrossBin: number;\r\n flatFallback: number;\r\n overCap: number;\r\n entriesReturned: number;\r\n};\r\n\r\nexport type BatchReviewResult = {\r\n reviews: Map<string, GroundedFileReview>;\r\n missing: string[];\r\n stats: BatchParseStats;\r\n};\r\n\r\ntype Ambiguity = { index: ReturnType<typeof buildBinAmbiguityIndex>; stats: { ambiguousAcrossBin: number } };\r\ntype RawEntry = { findings: unknown[]; overall_correctness: string; overall_explanation: string };\r\n\r\nconst basename = (path: string) => path.split('/').pop() ?? path;\r\nimport { SEVERITY_ORDER } from '../constants';\r\n\r\nfunction resolveEntryPath(reported: string, candidates: readonly FileDiff[], claimed: Set<string>): FileDiff | null {\r\n const unclaimed = (matches: readonly FileDiff[]) => matches.find((f) => !claimed.has(f.path)) ?? null;\r\n\r\n const exact = candidates.filter((f) => samePath(f.path, reported));\r\n const renamed = candidates.filter((f) => f.previousPath && samePath(f.previousPath, reported));\r\n if (exact.length > 0 || renamed.length > 0) return unclaimed(exact) ?? unclaimed(renamed);\r\n\r\n const stripped = reported.trim().replace(/^\\.\\//, '').replace(/^[ab]\\//, '').replace(/^\\//, '');\r\n const suffixed = candidates.filter((f) => f.path.endsWith(`/${stripped}`));\r\n if (suffixed.length === 1) return unclaimed(suffixed);\r\n\r\n const named = candidates.filter((f) => basename(f.path) === basename(stripped));\r\n return named.length === 1 ? unclaimed(named) : null;\r\n}\r\n\r\nfunction groundEntry(\r\n file: FileDiff,\r\n entry: RawEntry,\r\n deniedClaimTypes: readonly ClaimType[] | undefined,\r\n ambiguity: Ambiguity,\r\n confidenceScore: number | undefined,\r\n stats: BatchParseStats,\r\n): GroundedFileReview {\r\n for (const finding of entry.findings as Array<{ code_location?: { absolute_file_path?: string } }>) {\r\n const claimed = finding.code_location?.absolute_file_path?.trim();\r\n if (claimed && !samePath(claimed, file.path)) stats.pathMismatchFindings += 1;\r\n }\r\n\r\n return groundParsedFindings(\r\n { ...entry, findings: entry.findings as never, overall_confidence_score: confidenceScore },\r\n file,\r\n { deniedClaimTypes, ambiguity: { index: ambiguity.index, filePath: file.path, stats: ambiguity.stats } },\r\n );\r\n}\r\n\r\nfunction trimOverCap(reviews: Map<string, GroundedFileReview>, cap: number, stats: BatchParseStats) {\r\n for (const [path, review] of reviews) {\r\n if (review.comments.length <= cap) continue;\r\n\r\n const ranked = review.comments.toSorted((a, b) => SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity));\r\n const dropped = ranked.slice(cap);\r\n stats.overCap += dropped.length;\r\n\r\n const header = '### Additional Comments (Off-diff)';\r\n const bullets = dropped.map((c) => `- **[over-cap] ${c.title}:** ${c.body}`).join('\\n');\r\n reviews.set(path, {\r\n ...review,\r\n comments: ranked.slice(0, cap),\r\n fileSummary: review.fileSummary.includes(header)\r\n ? `${review.fileSummary}\\n${bullets}`\r\n : `${review.fileSummary}\\n\\n${header}\\n${bullets}`,\r\n });\r\n }\r\n}\r\n\r\nexport function parseBatchReviewResponse(\r\n raw: string,\r\n files: readonly FileDiff[],\r\n options?: { deniedClaimTypes?: readonly ClaimType[]; maxCommentsPerFile?: number },\r\n): BatchReviewResult {\r\n const stats: BatchParseStats = {\r\n unroutableEntries: 0, pathMismatchFindings: 0, ambiguousAcrossBin: 0,\r\n flatFallback: 0, overCap: 0, entriesReturned: 0,\r\n };\r\n const payload = parseRawBatchPayload(raw);\r\n const reviews = new Map<string, GroundedFileReview>();\r\n const ambiguity: Ambiguity = { index: buildBinAmbiguityIndex(files), stats: { ambiguousAcrossBin: 0 } };\r\n const claimed = new Set<string>();\r\n\r\n const ground = (file: FileDiff, entry: RawEntry, confidence: number | undefined) =>\r\n reviews.set(file.path, groundEntry(file, entry, options?.deniedClaimTypes, ambiguity, confidence, stats));\r\n\r\n if (payload.shape === 'flat') {\r\n stats.flatFallback = 1;\r\n stats.entriesReturned = 1;\r\n\r\n const byFile = new Map<string, { file: FileDiff; findings: typeof payload.data.findings }>();\r\n for (const finding of payload.data.findings) {\r\n const reported = finding.code_location.absolute_file_path?.trim();\r\n const target = !reported && files.length === 1 ? files[0] : resolveEntryPath(reported ?? '', files, claimed);\r\n if (!target) {\r\n stats.unroutableEntries += 1;\r\n continue;\r\n }\r\n const bucket = byFile.get(target.path);\r\n if (bucket) bucket.findings.push(finding);\r\n else byFile.set(target.path, { file: target, findings: [finding] });\r\n }\r\n\r\n for (const { file, findings } of byFile.values()) {\r\n ground(file, {\r\n findings,\r\n overall_correctness: payload.data.overall_correctness,\r\n overall_explanation: payload.data.overall_explanation,\r\n }, payload.data.overall_confidence_score);\r\n }\r\n } else {\r\n stats.entriesReturned = payload.data.files.length;\r\n for (const entry of payload.data.files) {\r\n const file = resolveEntryPath(entry.absolute_file_path, files, claimed);\r\n if (!file) {\r\n stats.unroutableEntries += 1;\r\n logger.warn('Batched review returned an entry for an unknown path', { reported: entry.absolute_file_path });\r\n continue;\r\n }\r\n claimed.add(file.path);\r\n ground(file, entry, entry.overall_confidence_score ?? payload.data.overall_confidence_score);\r\n }\r\n }\r\n\r\n stats.ambiguousAcrossBin = ambiguity.stats.ambiguousAcrossBin;\r\n if (options?.maxCommentsPerFile) trimOverCap(reviews, generatorFindingCap(options.maxCommentsPerFile), stats);\r\n\r\n return { reviews, missing: files.flatMap((f) => (reviews.has(f.path) ? [] : [f.path])), stats };\r\n}\r\n"],"mappings":"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","names":["z","jsonrepair","jsonrepair","z"]}
|