Bind content projections to the observed reply text

Retain each visible link label so the source content projection cannot attest to different text. Keep decorated-label support with exact destinations and negative coverage.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-29 23:12:58 -05:00
1 parent 4f7489b60a
commit 97048b0153
4 files changed
+14 -6

No files matched your search

+1 -1
View File
@@ -66,7 +66,7 @@ describe("neutral decline evidence", () => {
const input = native();
const body = "Contacts are unavailable. See [Connection outcome](/QA/issues/QA-123#document-outcome).";
Object.assign(input.nativeFinalReply, { sourceBody: body, body: "Contacts are unavailable. See QA-123.", origin: "https://fixture.test",
content: [{ text: "Contacts are unavailable. See " }, { href: "/QA/issues/QA-123#document-outcome" }, { text: "." }] });
content: [{ text: "Contacts are unavailable. See " }, { href: "/QA/issues/QA-123#document-outcome", label: "QA-123" }, { text: "." }] });
input.nativeFinalReply.finalEvent.body = body;
expect(passes(input)).toBe(true);
Object.assign(input.nativeFinalReply, { sourceBody: body.replace("Connection outcome", "PRIVATE_MARKER") });
+3 -2
View File
@@ -80,11 +80,12 @@ it("binds complete Markdown content and the exact link destination despite a dec
const published = { ...run, resultJson: { presentationDecision: { commentId: "selected" } } };
const bubble: TaskReplyObservation = { ...observed, anchor: "selected", origin: "https://fixture.test",
body: "I couldn't retrieve the pages. See QA-123.",
content: [{ text: "I couldn't retrieve the pages. See " }, { href: "https://fixture.test/QA/issues/QA-123#document-outcome" }, { text: "." }] };
content: [{ text: "I couldn't retrieve the pages. See " }, { href: "https://fixture.test/QA/issues/QA-123#document-outcome", label: "QA-123" }, { text: "." }] };
expect(taskReplyBelongsToRun(bubble, published, "owned-agent", [comment])).toBe(true);
const copy = events(); copy[0]!.payload.prpEvent.payload.text = body;
expect(taskReplyBelongsToRun({ ...bubble, anchor: "final-run:turn" }, run, "owned-agent", [], copy)).toBe(true);
expect(taskReplyBelongsToRun({ ...bubble, body: "Different visible response" }, published, "owned-agent", [comment])).toBe(false);
for (const content of [undefined, [{ text: bubble.body }], [...bubble.content!, { text: " extra" }],
[{ text: "I couldn't retrieve the pages. See " }, { href: "/other-task" }, { text: "." }]])
[{ text: "I couldn't retrieve the pages. See " }, { href: "/other-task", label: "QA-123" }, { text: "." }]])
expect(taskReplyBelongsToRun({ ...bubble, content }, published, "owned-agent", [comment])).toBe(false);
});
+2 -2
View File
@@ -54,13 +54,13 @@ export function uniqueBoundTaskReply(all: TaskReplyObservation[], run: Row, agen
export function observeTaskReplyElements(elements: Array<HTMLElement | SVGElement>): TaskReplyObservation[] {
return elements.map(element => {
const content: Array<{ text: string } | { href: string }> = [];
const content: Array<{ text: string } | { href: string; label: string }> = [];
const walk = (node: Node) => {
if (node.nodeType === Node.TEXT_NODE) { content.push({ text: node.textContent ?? "" }); return; }
if (!(node instanceof HTMLElement) || node.getAttribute("aria-hidden") === "true"
|| getComputedStyle(node).display === "none" || getComputedStyle(node).visibility === "hidden") return;
if (node instanceof HTMLAnchorElement) {
if (node.getClientRects().length && (node.innerText.trim() || node.getAttribute("aria-label"))) content.push({ href: node.href });
if (node.getClientRects().length && node.innerText.trim()) content.push({ href: node.href, label: node.innerText });
return;
}
const block = ["P", "DIV", "BR", "LI", "PRE", "BLOCKQUOTE", "H1", "H2", "H3", "H4", "H5", "H6"].includes(node.tagName);
+8 -1
View File
@@ -1,6 +1,6 @@
import { fromMarkdown } from "mdast-util-from-markdown";
export type ReplyContentPart = { text: string } | { href: string };
export type ReplyContentPart = { text: string } | { href: string; label?: string };
type MarkdownNode = { type: string; value?: string; url?: string; children?: MarkdownNode[] };
function normalized(parts: ReplyContentPart[], origin: string) {
@@ -28,6 +28,13 @@ export function taskReplyContentMatches(
observation: { body: string; content?: ReplyContentPart[]; origin?: string },
markdown: string,
) {
if (observation.content) {
// Bind the structured projection to the independently observed innerText.
// A destination alone cannot attest to arbitrary visible wording.
if (observation.content.some(part => "href" in part && (typeof part.label !== "string" || !part.label.trim()))) return false;
const displayed = observation.content.map(part => "text" in part ? part.text : part.label!).join("").replace(/\s+/g, " ").trim();
if (displayed !== observation.body.replace(/\s+/g, " ").trim()) return false;
}
const parts: ReplyContentPart[] = [];
const inlineLabel = (node: MarkdownNode): boolean => ["text", "inlineCode", "strong", "emphasis"].includes(node.type)
&& (node.children ?? []).every(inlineLabel);