Hashline editing: content-hash anchors for edit_file stale-patch detection.
Pure-JS xxHash32, line hash computation, validation with HashlineMismatchError,
256-entry hash dictionary. 6 files in apps/coder/src/services/hashline/.
Audit hooks: emitHook('tool.execute.after') wired in frame-emitter.ts for
completed/failed tool results. emitHook('turn.end') wired at terminal points
in dispatcher.ts (all 5 run functions: native, external, opencode, warm ACP,
claude SDK). Fire-and-forget, non-blocking.
32 lines
1.1 KiB
TypeScript
32 lines
1.1 KiB
TypeScript
import { HASHLINE_DICT } from "./constants.js"
|
|
import { hashXxh32 } from "./xxhash32.js"
|
|
|
|
const RE_SIGNIFICANT = /[\p{L}\p{N}]/u
|
|
|
|
function computeNormalizedLineHash(lineNumber: number, normalizedContent: string): string {
|
|
const stripped = normalizedContent
|
|
const seed = RE_SIGNIFICANT.test(stripped) ? 0 : lineNumber
|
|
const hash = hashXxh32(stripped, seed)
|
|
const index = hash % 256
|
|
return HASHLINE_DICT[index]!
|
|
}
|
|
|
|
export function computeLineHash(lineNumber: number, content: string): string {
|
|
return computeNormalizedLineHash(lineNumber, content.replace(/\r/g, "").trimEnd())
|
|
}
|
|
|
|
export function computeLegacyLineHash(lineNumber: number, content: string): string {
|
|
return computeNormalizedLineHash(lineNumber, content.replace(/\r/g, "").replace(/\s+/g, ""))
|
|
}
|
|
|
|
export function formatHashLine(lineNumber: number, content: string): string {
|
|
const hash = computeLineHash(lineNumber, content)
|
|
return `${lineNumber}#${hash}|${content}`
|
|
}
|
|
|
|
export function formatHashLines(content: string): string {
|
|
if (!content) return ""
|
|
const lines = content.split("\n")
|
|
return lines.map((line, index) => formatHashLine(index + 1, line)).join("\n")
|
|
}
|