@thehammer/template-verification 0.2.9 → 0.2.11

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/README.md CHANGED
@@ -1,28 +1,39 @@
1
1
  # @thehammer/template-verification
2
2
 
3
- A read-write **verification overlay** for rendered template apps. It overlays a
4
- trust layer on top of an already-rendered document template: per-data-point
5
- confidence pills + verified check chips, discrepancy indicators, and a
6
- click-through **Inspector** dialog — a hero strip (resolved value, gradient
7
- confidence meter, discrepancy alert) over three tabs:
8
-
9
- - **Evidence** — readonly-markdown reasoning, cited-text quote, an embedded
10
- `DanxFileViewer` over the cited source pages, and per-document drill-down
11
- to the FULL parent PDF in a stacked dialog.
12
- - **Candidates** — the resolution flow: one card per candidate
13
- (override/extracted/claim) with Accept actions + custom-value entry;
14
- saves toast and flip the in-document pill to a verified chip.
15
- - **Audit trail** — the LLM calls behind the data point (model, status,
16
- timing, collapsible JSON request/response), lazy-fetched from the host's
17
- token-sealed `audit_url`. Tab hidden when the host mints none.
18
-
19
- `VerificationChrome` adds a review-progress meter (X of Y human-verified),
20
- a needs-review jump button, and completion celebration.
21
-
22
- Render data + `__meta` arrive via a single `initVerification()` call — the
23
- only network the library touches is the host-supplied `saveOverride`
24
- transport and the optional `auditUrl` endpoint. Backend-agnostic and
25
- unit-testable standalone.
3
+ The **verification overlay** a rendered template app wears, and the one signal it
4
+ sends back out.
5
+
6
+ It draws ONE mark beside every bound data value — a confidence pill, a verified
7
+ check chip once a person has locked the value in, and a discrepancy indicator
8
+ when candidates or source documents disagree. Clicking that mark posts a
9
+ `data-point-activated` message to the embedding application. Nothing else
10
+ happens inside the document.
11
+
12
+ `VerificationChrome` adds the overlay's on/off toggle, a review-progress meter
13
+ (X of Y human-verified) and a needs-review jump button, all hidden under print.
14
+
15
+ Render data + `__meta` arrive via a single `initVerification()` call. **The
16
+ library fetches nothing and saves nothing.**
17
+
18
+ ## What this library used to do, and deliberately no longer does
19
+
20
+ Earlier versions rendered the whole review surface INSIDE the iframe: a
21
+ 90vw × 90vh **Inspector** dialog with Evidence / Candidates / History / Audit
22
+ tabs, an embedded file viewer over the cited pages, a hover summary panel on
23
+ every mark, and `contenteditable` editing on the value with a save transport
24
+ behind it. All of that is **deleted**, along with `VerificationModal`,
25
+ `CitationEvidence`, `CitationPageList`, `CitationPage`, `citationEvidence`,
26
+ `citationSummary` and `fetchDataPointDetail`.
27
+
28
+ A letter is a document, and a document that grows a second application on top of
29
+ itself is two products fighting over one viewport — in a frame that cannot reach
30
+ any of the viewers the embedding app already has. Reviewing a value now happens
31
+ in that app, which opens the citation and the record beside the letter.
32
+
33
+ `initVerification` still **accepts** `saveOverride` and `detailUrl` and ignores
34
+ both. That is compile-time compatibility for already-built templates, whose
35
+ `App.vue` is generated from a snippet that still passes them; it is not a
36
+ feature. See `src/context.ts`.
26
37
 
27
38
  ## Install
28
39
 
@@ -33,8 +44,9 @@ npm install @thehammer/template-verification
33
44
  `vue` and `@thehammer/danx-ui` are peer dependencies (runtime externals) — the
34
45
  host template app already loads both.
35
46
 
36
- Import the stylesheet once (it carries the `@media print` rule that hides the
37
- overlay chrome on print/export):
47
+ Import the stylesheet once. It carries the `@media print` rule that hides the
48
+ overlay chrome on print/export, AND the positioning rule that keeps the mark out
49
+ of the text flow:
38
50
 
39
51
  ```ts
40
52
  import "@thehammer/template-verification/style.css"
@@ -52,23 +64,12 @@ import { initVerification, VerificationChrome, VerifiedField } from "@thehammer/
52
64
 
53
65
  const props = defineProps<{ data: any }>()
54
66
 
55
- // Host-supplied, authenticated transport. Its presence enables inline editing.
56
- async function saveOverride(args) {
57
- const res = await fetch(`/api/workflow-inputs/${args.workflow_input_id}/data-point-overrides`, {
58
- method: "POST",
59
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
60
- body: JSON.stringify({ ...args, object_id: args.object_ids.at(-1) })
61
- })
62
- return res.json() // recomputed DataPointMeta
63
- }
64
-
65
- // Once, at root setup. Absent saveOverride ⇒ every field is read-only.
66
67
  initVerification({
67
68
  data: props.data,
68
- saveOverride,
69
- workflowInputId: props.data.__verification?.workflow_input_id,
70
- // Token-sealed audit endpoint (optional) — enables the Inspector's Audit tab.
71
- auditUrl: props.data.__verification?.audit_url ?? null
69
+ // Echoed on every activation message, so the embedder can tell a click on
70
+ // THIS render from a click on one it has already replaced.
71
+ workflowInputId: props.data.__verification?.workflow_input_id ?? null,
72
+ defaultEnabled: props.data.__verification?.default_enabled ?? false
72
73
  })
73
74
  </script>
74
75
 
@@ -89,6 +90,46 @@ The backing store is a **module singleton** — safe because each rendered templ
89
90
  runs as its own isolated Vue app inside its own iframe (one JS module instance,
90
91
  one app-wide config, no SSR). See `src/context.ts` for the invariant.
91
92
 
93
+ ## The mark occupies no space, and that is a contract
94
+
95
+ The mark is absolutely positioned against its wrapper, overlaying the right-hand
96
+ end of the value it belongs to. Turning the overlay on or off, or a field going
97
+ from unverified to verified, must not move a single character of the document.
98
+
99
+ Measured rather than asserted: `.junk/reflow-probe` in the gpt-manager repo
100
+ mounts `VerifiedField` twice into one paragraph in a real browser, with the
101
+ overlay on and off, and compares `getBoundingClientRect()` for the value and for
102
+ the text after it. `src/__tests__/VerifiedField.test.ts` pins the structural
103
+ half (happy-dom has no layout engine, so a rect assertion there would pass on
104
+ zeroes).
105
+
106
+ ## The outbound message
107
+
108
+ ```ts
109
+ {
110
+ source: "danxbot-template", // TEMPLATE_MESSAGE_SOURCE
111
+ version: 1, // TEMPLATE_PROTOCOL_VERSION
112
+ type: "data-point-activated", // DATA_POINT_ACTIVATED
113
+ anchor: { // copied VERBATIM from __meta, unreshaped
114
+ object_ids: [4211, 77], // full root-to-leaf path; LAST is the record
115
+ field: "full_name",
116
+ field_path: "medical_providers.0.full_name"
117
+ },
118
+ workflowInputId: 431 | null,
119
+ label: "full_name" | null,
120
+ value: "Jane Doe" | null
121
+ }
122
+ ```
123
+
124
+ Posted to `window.parent` at the embedder's **real origin**, never `"*"`. The
125
+ origin is resolved from `location.ancestorOrigins[0]`, falling back to
126
+ `document.referrer`'s origin; when neither resolves, **nothing is posted**. See
127
+ `src/lib/hostBridge.ts`.
128
+
129
+ A receiver must check `event.origin` before it looks at the payload. A payload
130
+ check is not a security check: anyone can post a well-formed message, and only
131
+ the bundle can post from the bundle's origin.
132
+
92
133
  ## The `__meta` contract
93
134
 
94
135
  Each rendered object node carries a `__meta` map keyed by leaf field name. Each
@@ -99,7 +140,7 @@ interface DataPointMeta {
99
140
  anchor: { object_ids: (number | string)[]; field: string; field_path: string }
100
141
  candidates: {
101
142
  override?: { value; source_choice }
102
- extracted?: { value; confidence: number | null; reasoning; cited_text; cited_text_verified; sources[] }
143
+ extracted?: { value; confidence: number | null; state; sources_count; history_count }
103
144
  claim?: { value; claim_set_label }
104
145
  }
105
146
  discrepancy: boolean
@@ -111,15 +152,16 @@ interface DataPointMeta {
111
152
 
112
153
  - **Resolution priority:** `override > extracted > claim`.
113
154
  - **Status** buckets the **integer** confidence: `null → unverifiable (gray)`,
114
- `≤2 → low (red)`, `==3 → medium (yellow)`, `≥4 → high (green)`.
155
+ `≤2 → low (red)`, `==3 → medium (yellow)`, `≥4 → high (green)`. A `failed`
156
+ extraction state is its own bucket, not a confidence.
115
157
  - **Discrepancy** is orthogonal to confidence — a high-confidence field can still
116
158
  disagree with a differing claim/override.
117
159
 
118
160
  ## Activation
119
161
 
120
- Fully self-contained: a fixed bottom-right settings button toggles the overlay,
121
- persisted to `localStorage`. No URL flags, no postMessage, no host activation prop.
122
- All library chrome is hidden under print/export media.
162
+ The overlay's own toggle is a fixed bottom-right settings button, persisted to
163
+ `localStorage`. No URL flags and no host activation prop. All library chrome is
164
+ hidden under print/export media.
123
165
 
124
166
  ## Develop
125
167
 
@@ -4,12 +4,36 @@ import type { MetaCarrier } from "../types";
4
4
  * `source` object at `field` (`source.__meta[field]`), renders the RESOLVED value
5
5
  * (override > extracted > claim) with a HARD fallback to the host's raw value —
6
6
  * the document must never lose data because provenance is thin. When the overlay
7
- * is enabled via `useVerification()` it overlays a confidence pill (or, for
8
- * provenance-less points, a subtle dotted underline), a discrepancy indicator,
9
- * and click-through to the Inspector dialog. Inline contenteditable editing is
10
- * available whenever a `saveOverride` transport was passed to `initVerification`;
11
- * absent it, read-only. With no meta/source it degrades to a bare value — and
12
- * with no `initVerification` call at all it still renders and never throws.
7
+ * is enabled via `useVerification()` it draws ONE mark beside the value, and
8
+ * clicking that mark tells the embedding app which data point was activated
9
+ * ({@link notifyDataPointActivated}). With no meta/source it degrades to a bare
10
+ * value — and with no `initVerification` call at all it still renders and never
11
+ * throws.
12
+ *
13
+ * ==== THE MARK IS THE WHOLE SURFACE. THERE IS NOTHING ELSE ====
14
+ *
15
+ * What used to be here as well: a 90vw x 90vh tabbed Inspector dialog, a hover
16
+ * panel summarising confidence and citation counts, and `contenteditable` on
17
+ * the value with a save transport behind it. All three are deleted, not
18
+ * disabled. A letter is a document. Reviewing a value — reading its citations,
19
+ * seeing the record it belongs to, correcting it — is the embedding app's job,
20
+ * and that app already does all three better than a dialog squeezed into an
21
+ * iframe ever could. Everything a reader can do from here is: click the mark.
22
+ *
23
+ * The mark's own summary now lives in its ACCESSIBLE NAME rather than in a
24
+ * hover panel, so the same sentence reaches a screen reader and a mouse
25
+ * hovering the glyph, and neither costs a second piece of chrome in the page.
26
+ *
27
+ * ==== THE MARK IS OUT OF FLOW, AND THAT IS A HARD REQUIREMENT ====
28
+ *
29
+ * It is absolutely positioned against the wrapper (see the `<style>` block at
30
+ * the foot of this file), overlaying the right-hand end of the value it belongs
31
+ * to. It therefore occupies ZERO inline space: turning the overlay on or off,
32
+ * or toggling a field from "unverified" to "verified", must not move a single
33
+ * character of the letter. The previous layout was an `inline-flex` row with a
34
+ * gap, so every mark pushed the rest of its line along — which reflowed the
35
+ * document the moment the overlay was switched on, in a product whose entire
36
+ * point is that the document is the deliverable.
13
37
  */
14
38
  type __VLS_Props = {
15
39
  source?: MetaCarrier;
package/dist/context.d.ts CHANGED
@@ -22,17 +22,33 @@ import type { SaveOverrideFn } from "./types";
22
22
  export interface InitVerificationConfig {
23
23
  /** The bound data tree (carries per-node `__meta`). */
24
24
  data: Record<string, unknown>;
25
- /** Host transport; absent ⇒ all fields read-only. */
25
+ /**
26
+ * ACCEPTED AND IGNORED. Nothing in this library saves anything any more.
27
+ *
28
+ * The in-letter review surface this fed — the Inspector dialog and the
29
+ * `contenteditable` value — is gone; correcting a value is the embedding
30
+ * app's job now. The field stays in this interface for exactly one reason:
31
+ * every already-built template's `App.vue` passes it (the wiring snippet
32
+ * that generates them lives in gpt-manager's `SchemaBuilderContextBuilder`),
33
+ * and removing it here turns every one of those into a TypeScript excess-
34
+ * property error the next time it is rebuilt — for a library that cannot
35
+ * rebuild them. It goes when that snippet stops emitting it.
36
+ */
26
37
  saveOverride?: SaveOverrideFn;
27
- /** Opaque workflow input id echoed back in saves. */
38
+ /**
39
+ * The workflow input this render is for. Echoed on the outbound activation
40
+ * message so an embedder can tell a click on the CURRENT render from a
41
+ * click on a render it has already replaced — see
42
+ * {@link ../lib/hostBridge.DataPointActivatedMessage}.
43
+ */
28
44
  workflowInputId?: number | string | null;
29
45
  /**
30
- * Token-sealed per-field detail endpoint base (`__verification.detail_url` from
31
- * the host). Present ⇒ the Inspector dialog lazy-loads a data point's full
32
- * citation sources, extraction history, and LLM-call audit trace from
33
- * `{detailUrl}?object_id=&field=` the instant it opens for that field (SG-392).
34
- * Absent ⇒ the Audit tab is hidden, and the base render's light `extracted`
35
- * candidate (see `types.ts`) is all any tab ever shows.
46
+ * ACCEPTED AND IGNORED, for the same reason as `saveOverride` above.
47
+ *
48
+ * This was the Inspector dialog's lazy per-field detail endpoint. The
49
+ * Inspector is gone, and the embedder fetches a data point's citations and
50
+ * history against its own authenticated session rather than against a
51
+ * token-sealed url handed into an iframe.
36
52
  */
37
53
  detailUrl?: string | null;
38
54
  /** Seeds the overlay toggle ONLY when localStorage holds no prior value. */
@@ -53,12 +69,8 @@ export interface Verification {
53
69
  enabled: Ref<boolean>;
54
70
  /** The bound data tree (carries per-node `__meta`). */
55
71
  data: Record<string, unknown>;
56
- /** Host transport; absent ⇒ all fields read-only. */
57
- saveOverride?: SaveOverrideFn;
58
- /** Opaque workflow input id echoed back in saves. */
72
+ /** The workflow input this render is for; echoed on activation messages. */
59
73
  workflowInputId?: number | string | null;
60
- /** Token-sealed per-field detail endpoint base; null/absent hides the Audit tab. */
61
- detailUrl?: string | null;
62
74
  /** True while the host is fetching a new data source; see {@link InitVerificationConfig.loading}. */
63
75
  loading: boolean;
64
76
  }
@@ -69,8 +81,8 @@ export interface Verification {
69
81
  */
70
82
  export declare function initVerification(config: InitVerificationConfig): void;
71
83
  /**
72
- * Consume the live verification state. Returns a default (empty data, disabled,
73
- * no transport) when {@link initVerification} was never called, so a
84
+ * Consume the live verification state. Returns a default (empty data, disabled)
85
+ * when {@link initVerification} was never called, so a
74
86
  * `<VerifiedField>` used standalone degrades to a bare value instead of throwing.
75
87
  * Consumed internally by `VerifiedField` + `VerificationChrome`; exported for
76
88
  * advanced hosts.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,29 @@
1
1
  /**
2
- * @thehammer/template-verification — read-write verification overlay for rendered
3
- * template apps. Consumes the host's `__meta` sidecar (override > extracted >
4
- * claim resolution + discrepancy), overlays confidence/status icons, and a
5
- * click-through Inspector dialog with inline + modal-driven override editing.
6
- * The host calls `initVerification` once with `data` and an optional
7
- * `saveOverride` transport. SG-392: the base `__meta` is LIGHT (no citation
8
- * sources/history — only their `*_count`); the ONE thing this library fetches
9
- * on its own is that full per-field detail, via `detailUrl`, the instant the
10
- * Inspector dialog opens for a field (see `fetchDataPointDetail`).
2
+ * @thehammer/template-verification — the verification overlay a rendered
3
+ * template app wears, and the one signal it sends back out.
4
+ *
5
+ * Consumes the host's `__meta` sidecar (override > extracted > claim resolution
6
+ * + discrepancy) and draws ONE mark beside each bound value. Clicking a mark
7
+ * posts {@link DataPointActivatedMessage} to the embedding app; that app opens
8
+ * the citation and the record. The host calls `initVerification` once with
9
+ * `data`.
10
+ *
11
+ * ==== WHAT THIS LIBRARY DELIBERATELY NO LONGER DOES ====
12
+ *
13
+ * It used to render the entire review surface inside the iframe — a tabbed
14
+ * Inspector dialog (citations, extraction history, LLM audit trace), a hover
15
+ * summary panel on every mark, and `contenteditable` editing with a save
16
+ * transport behind it — and it fetched per-field detail from a token-sealed
17
+ * endpoint to fill them. All of that is deleted. It fetches nothing, saves
18
+ * nothing, and opens nothing. A letter is a document; the review happens in the
19
+ * application framing it, which already owns a citation viewer, a page viewer
20
+ * and a record editor.
21
+ *
22
+ * Removed exports, for anyone chasing an import that no longer resolves:
23
+ * `VerificationModal`, `CitationEvidence`, `CitationPageList`, `CitationPage`,
24
+ * `citationEvidence`, `citationSummary`, `fetchDataPointDetail`, and the
25
+ * `CitationPageModel` / `CitationEvidenceModel` / `AuditLlmCall` /
26
+ * `DataPointDetail` types.
11
27
  */
12
28
  import "./styles.css";
13
29
  export { initVerification, useVerification } from "./context";
@@ -15,20 +31,14 @@ export type { InitVerificationConfig, Verification } from "./context";
15
31
  export { default as VerifiedField } from "./components/VerifiedField.vue";
16
32
  export { default as VerificationChrome } from "./components/VerificationChrome.vue";
17
33
  export { default as ConfidenceIcon } from "./components/ConfidenceIcon.vue";
18
- export { default as VerificationModal } from "./components/VerificationModal.vue";
19
- export { default as CitationEvidence } from "./components/CitationEvidence.vue";
20
- export { default as CitationPageList } from "./components/CitationPageList.vue";
21
- export { default as CitationPage } from "./components/CitationPage.vue";
34
+ export { TEMPLATE_MESSAGE_SOURCE, TEMPLATE_PROTOCOL_VERSION, DATA_POINT_ACTIVATED, embedderOrigin, notifyDataPointActivated, } from "./lib/hostBridge";
35
+ export type { DataPointActivatedMessage, DataPointActivation, } from "./lib/hostBridge";
22
36
  export { bucketConfidence, STATUS_COLORS, STATUS_LABELS, } from "./lib/confidence";
23
- export { resolveDataPoint, resolveCandidates, reasoningOf, RESOLUTION_PRIORITY, } from "./lib/resolveDataPoint";
37
+ export { resolveDataPoint, resolveCandidates, RESOLUTION_PRIORITY, } from "./lib/resolveDataPoint";
24
38
  export { hasDiscrepancy, candidatesDisagree } from "./lib/discrepancy";
25
39
  export { walkDataPoints } from "./lib/walk";
26
40
  export type { WalkedDataPoint } from "./lib/walk";
27
- export { citationEvidence, citationSummary } from "./lib/citations";
28
- export type { CitationPage as CitationPageModel, CitationEvidence as CitationEvidenceModel } from "./lib/citations";
29
41
  export { computeProgress } from "./lib/progress";
30
42
  export type { VerificationProgress } from "./lib/progress";
31
- export { fetchDataPointDetail } from "./lib/dataPointDetail";
32
- export type { AuditLlmCall, DataPointDetail } from "./lib/dataPointDetail";
33
43
  export { CHROME_CLASS, ENABLED_STORAGE_KEY } from "./constants";
34
44
  export type { VerificationStatus, ResolvedSource, SourceChoice, VerificationSource, ExtractedCandidate, OverrideCandidate, ClaimCandidate, CandidateSet, DataPointAnchor, ResolvedValue, DataPointMeta, MetaCarrier, SaveOverrideArgs, SaveOverrideFn, } from "./types";
@@ -12,3 +12,11 @@ import type { CandidateSet, DataPointMeta } from "../types";
12
12
  export declare function hasDiscrepancy(meta?: DataPointMeta | null): boolean;
13
13
  /** True when >=2 present candidates hold differing values. */
14
14
  export declare function candidatesDisagree(candidates?: CandidateSet | null): boolean;
15
+ /**
16
+ * True when a data point's discrepancy (if any) comes from a SOURCE-DOCUMENT conflict the
17
+ * host already reconciled (SG-217), rather than — or in addition to — two candidates
18
+ * disagreeing. Distinguishes the two kinds for copy/icon purposes; both share the same
19
+ * `discrepancy` flag and the same indicator in `VerifiedField.vue` — this only decides
20
+ * which wording the mark's accessible name carries.
21
+ */
22
+ export declare function isSourceDiscrepancy(meta?: DataPointMeta | null): boolean;
@@ -1,25 +1,127 @@
1
1
  /**
2
- * Outbound signal to the embedding consumer (gpt-manager's `TemplatePreview.vue`)
3
- * telling it a nested pan/zoom surface — the Evidence tab's `DanxFileViewer` — is
4
- * currently visible and wants exclusive ownership of Ctrl/Cmd+drag.
5
- *
6
- * Why this exists: the consumer's own preview canvas ALSO binds Ctrl/Cmd+drag to
7
- * pan the whole rendered document, and it activates that gesture the instant it
8
- * sees a relayed Ctrl/Cmd keydown from anywhere inside this iframe (the danxbot
9
- * host-bridge relays raw keyboard events regardless of what's focused inside).
10
- * Its pan overlay is a transparent `position: absolute; inset: 0` div stacked
11
- * ABOVE the iframe in the PARENT's own DOM — once it renders, every subsequent
12
- * mouse event over the iframe's whole bounding box (including this dialog's
13
- * nested file viewer) is captured by that parent-level div instead of reaching
14
- * anything inside the iframe. The nested viewer's own Ctrl+drag-to-pan-the-image
15
- * gesture then never receives its mousedown at all. Posting this message lets
16
- * the consumer gate its overlay off while a nested viewer wants the gesture,
17
- * without either side needing to know the other's internal DOM structure.
18
- *
19
- * Message type is namespaced under the same `source: "danxbot-template"`
20
- * envelope the danxbot host-bridge already uses for keyboard/wheel relays (see
21
- * danxbot's `scaffold.ts` → `buildHostBridgeScript`), sent directly via
22
- * `window.top.postMessage` — this call runs as ordinary Vue code already
23
- * executing inside the iframe, so it needs no relay of its own.
2
+ * The one outbound channel from a rendered template to the app embedding it.
3
+ *
4
+ * ==== WHY THE LETTER NO LONGER REVIEWS ANYTHING ITSELF ====
5
+ *
6
+ * This library used to render the whole review surface INSIDE the iframe: a
7
+ * 90vw x 90vh dialog with tabs for citations, extraction history and the LLM
8
+ * audit trace, a hover panel on every badge, and contenteditable editing on the
9
+ * value itself. All of it is gone. A letter is a document; a document that
10
+ * grows a second application on top of itself is two products fighting for one
11
+ * viewport, and the embedding app already owns a citation viewer, a record
12
+ * editor and a page viewer that are better than the ones in here ever were.
13
+ *
14
+ * What is left in the letter is the MARK and nothing else. Clicking it posts
15
+ * this message; everything that happens next happens in the embedder.
16
+ *
17
+ * ==== THE TARGET ORIGIN IS RESOLVED, NEVER "*" ====
18
+ *
19
+ * The message names the record and field a reader is looking at, which is this
20
+ * team's data. Posting it to `"*"` hands it to whatever happens to be framing
21
+ * the document, which on a mis-served preview is not the app that asked for it.
22
+ *
23
+ * The embedder's origin is not readable through `window.parent` — that is the
24
+ * whole point of a cross-origin frame — so it is resolved from the two places a
25
+ * browser DOES publish it, in order:
26
+ *
27
+ * 1. `location.ancestorOrigins[0]` — the embedder's origin, stated directly.
28
+ * Chromium and WebKit only.
29
+ * 2. `document.referrer`'s origin — the document that framed this one. Under
30
+ * the default `strict-origin-when-cross-origin` referrer policy a
31
+ * cross-origin embed still sends the ORIGIN, which is exactly and only
32
+ * what is needed here.
33
+ *
34
+ * If neither resolves, NOTHING IS POSTED. A mark that does nothing is a bug
35
+ * worth reporting; a token-shaped payload broadcast to an unknown origin is a
36
+ * leak, and the two are not comparable.
37
+ */
38
+ import type { DataPointAnchor } from "../types";
39
+ /**
40
+ * The envelope every message out of a rendered template carries.
41
+ *
42
+ * Deliberately the SAME `source` the danxbot host-bridge scaffold already uses
43
+ * for its `ready` handshake and its keyboard/wheel relays. An embedder filters
44
+ * on `source` once and then switches on `type`; a second envelope would mean a
45
+ * second listener and a second thing to keep in step.
46
+ */
47
+ export declare const TEMPLATE_MESSAGE_SOURCE = "danxbot-template";
48
+ /**
49
+ * The protocol version, matching the one the embedder stamps on its own
50
+ * outbound settings messages. It is on the wire so a receiver can refuse a
51
+ * shape it does not know rather than half-reading it; today every receiver
52
+ * accepts 1.
53
+ */
54
+ export declare const TEMPLATE_PROTOCOL_VERSION = 1;
55
+ /** A reader clicked the verification mark on a rendered value. */
56
+ export declare const DATA_POINT_ACTIVATED = "data-point-activated";
57
+ /**
58
+ * The activation message.
59
+ *
60
+ * ==== THE ANCHOR IS COPIED VERBATIM, NEVER RESHAPED ====
61
+ *
62
+ * `anchor` carries the host's own three fields under the host's own names —
63
+ * `object_ids` (the FULL root-to-leaf path), `field`, `field_path` — because
64
+ * the receiver's citation endpoint and record lookup both key off exactly
65
+ * those. Renaming them into JS-side camelCase here would put a translation
66
+ * between the two halves of one protocol, and a translation is a place to get
67
+ * it wrong for no gain. `object_ids` is shallow-copied rather than passed by
68
+ * reference only because the source array is a Vue reactive proxy and the
69
+ * structured-clone algorithm has no business being handed one.
70
+ *
71
+ * `object_ids.at(-1)` is the record the field lives on; `object_ids[0]` is the
72
+ * demand itself. That distinction is load-bearing on the receiving side and is
73
+ * the reason the whole path travels rather than one id.
74
+ *
75
+ * ==== WHY `workflowInputId` IS ON THE MESSAGE ====
76
+ *
77
+ * An embedder does not remount the frame when it switches which record it is
78
+ * showing — it posts new data into the frame it already has. So a click can in
79
+ * principle arrive from a render of the PREVIOUS record, and the receiver has
80
+ * no other way to notice. Echoing the id the letter was initialised with lets
81
+ * it be checked in one comparison. Null when the host never supplied one.
82
+ *
83
+ * ==== WHY `value` TRAVELS AND `confidence` DOES NOT ====
84
+ *
85
+ * `value` is what the reader is looking at, so a panel can name the thing that
86
+ * was clicked before it has resolved anything, and a divergence between the
87
+ * letter and the record is visible rather than silent. Everything else about
88
+ * the data point — confidence, candidates, citations, history — the receiver
89
+ * already holds or can fetch against the anchor, and shipping a second copy
90
+ * would create two sources for one fact.
91
+ */
92
+ export interface DataPointActivatedMessage {
93
+ source: typeof TEMPLATE_MESSAGE_SOURCE;
94
+ version: number;
95
+ type: typeof DATA_POINT_ACTIVATED;
96
+ anchor: DataPointAnchor;
97
+ /** The workflow input this render was initialised for. Null when unknown. */
98
+ workflowInputId: number | string | null;
99
+ /** The field name the template bound, for a panel heading. Null when unbound. */
100
+ label: string | null;
101
+ /** The resolved text currently rendered in the letter. */
102
+ value: string | null;
103
+ }
104
+ /** What {@link notifyDataPointActivated} needs in order to name a data point. */
105
+ export interface DataPointActivation {
106
+ anchor: DataPointAnchor;
107
+ workflowInputId?: number | string | null;
108
+ label?: string | null;
109
+ value?: string | null;
110
+ }
111
+ /**
112
+ * The origin of the document framing this one, or null when it cannot be known.
113
+ *
114
+ * Exported because it is the security property of this module and a test that
115
+ * cannot reach it can only assert it indirectly.
116
+ */
117
+ export declare function embedderOrigin(): string | null;
118
+ /**
119
+ * Tell the embedder a reader activated one data point.
120
+ *
121
+ * Returns whether anything was actually posted, so a caller (or a test) can
122
+ * tell "the embedder was told" from "there was nobody to tell" without reaching
123
+ * inside. Silent no-op — not a throw — in all three cases where there is no
124
+ * legitimate recipient: no window at all, not framed, or an unresolvable
125
+ * embedder origin.
24
126
  */
25
- export declare function notifyNestedViewerActive(active: boolean): void;
127
+ export declare function notifyDataPointActivated(activation: DataPointActivation): boolean;
@@ -17,8 +17,3 @@ export declare const RESOLUTION_PRIORITY: Array<keyof CandidateSet>;
17
17
  export declare function resolveDataPoint(meta?: DataPointMeta | null): ResolvedValue;
18
18
  /** Derive the winning resolution purely from a candidate set, by priority. */
19
19
  export declare function resolveCandidates(candidates?: CandidateSet | null): ResolvedValue;
20
- /**
21
- * The extracted candidate's reasoning, normalized to a non-empty string or null.
22
- * Reasoning is frequently `""`/null — callers render a quiet empty state, never throw.
23
- */
24
- export declare function reasoningOf(meta?: DataPointMeta | null): string | null;