vector-mirror 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alan Klaus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,249 @@
1
+ # Mirror — a deterministic SVG perception eye for LLM agents
2
+
3
+ > **Mirror renders SVG in a real browser and reports what is actually where — in grid cells and
4
+ > W3C color names, with prose and structured output — and it never guesses.** Spatial layout
5
+ > constraints become unit tests. And every tool description is a claim the server proves about
6
+ > itself. Built for agents that can't afford to hallucinate coordinates.
7
+
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
9
+ [![npm](https://img.shields.io/npm/v/vector-mirror.svg)](https://www.npmjs.com/package/vector-mirror)
10
+ [![GitHub stars](https://img.shields.io/github/stars/c64dos-png/vector-mirror.svg?style=social)](https://github.com/c64dos-png/vector-mirror)
11
+ [![MCP](https://img.shields.io/badge/MCP-server-blue.svg)](https://modelcontextprotocol.io)
12
+
13
+ ---
14
+
15
+ ## What is this?
16
+
17
+ When an LLM agent edits an SVG, how does it know whether the change worked? Today it either
18
+ **guesses from the source text** (and hallucinates positions, because layout is a render-time
19
+ property, not a source property) or it **takes a screenshot and guesses from pixels** (and burns
20
+ tokens on a vision round-trip that still can't name a color or address a region precisely).
21
+
22
+ Mirror is the third option: a **measuring eye**. It is a sensory organ for LLMs, not a tool with
23
+ a purpose. It perceives and measures; it never interprets, decides, or acts — the **brain** (the
24
+ LLM or you) does that. The eye has three tissues:
25
+
26
+ | Tissue | What it does |
27
+ |--------|--------------|
28
+ | **Retina** (measurement) | renders the SVG in real Chromium and measures geometry, colors, visibility |
29
+ | **Mouth** (utterance) | says what it saw — `prose` + `structured`, two projections of one truth |
30
+ | **Package insert** (self-description) | explains the organ to a stranger brain: every tool description is a machine-verified claim |
31
+
32
+ Because the measurement runs in a real browser engine, Mirror sees what the browser sees — including
33
+ things the SVG source never tells you.
34
+
35
+ ---
36
+
37
+ ## Killer demo: the perceive → constrain → correct → compare loop
38
+
39
+ A cold agent (no source code, MCP only) built a 17-element mission-control panel and verified it
40
+ to an exact stop-condition. Here is the loop, with **real outputs from that session**:
41
+
42
+ ```text
43
+ # 1 — INSPECT: see the layout in LLM grammar (no constraints checked yet)
44
+ inspect(svg)
45
+ → scene: 16 elements, grid 16×10, canvas_validity: valid
46
+ e.g. { id: "led-ok", tag: "circle", cell: "B3", color: "lime" }
47
+ colors come out as W3C NAMES, never hex.
48
+
49
+ # 2 — CONSTRAINTS: ask the vocabulary (it is finite and closed)
50
+ constraints()
51
+ → 11 types. RIGHT-OF / BELOW deliberately do NOT exist
52
+ (use LEFT-OF / ABOVE with swapped operands).
53
+
54
+ # 3 — ANALYZE: turn layout intent into unit tests
55
+ analyze(svg, ["#title CENTERED-IN #frame", "#led-ok ABOVE #led-warn", ...16 constraints])
56
+ → PARTIAL: 2 unchecked SUBJECT_TIME_VARIANT
57
+ "#beacon has an animated r → not measurable; geometrically satisfied @t0 (bbox …)"
58
+ # the eye refuses to guess about an animated value. It says so, with a reason code.
59
+
60
+ # 4 — fix, re-analyze until PASS (convergence is tracked, not vibes)
61
+ analyze(svg_v3, [...], previousIssueCount: N)
62
+ → PASS (corrections == [] && unchecked == [] && canvas_validity == valid && diff == [])
63
+
64
+ # 5 — SNIPER LOOP: pin a baseline, edit, catch regressions in one call
65
+ bookmark("panel-pass", analysisId)
66
+ compare(sabotaged_svg, [], "panel-pass")
67
+ → FAIL: FARBÄNDERUNG orange→crimson + VERSCHOBEN D7→H8
68
+ with ready-to-apply fixes: { x: 262→182, y: 372→324 }
69
+ # exactly the two injected faults. Nothing more, nothing less.
70
+ ```
71
+
72
+ The agent's verdict, verbatim: *"In 13 calls it never once guessed, lied, or made me guess.
73
+ `analyze` landed on the first try. Lost diagnostic loops: 0."* — **8.5/10.**
74
+
75
+ ---
76
+
77
+ ## Features — the honesty axes
78
+
79
+ Mirror's differentiator is not *what* it measures but that it is **honest about what it cannot**.
80
+
81
+ | Capability | What it gives the agent | Honest about |
82
+ |------------|-------------------------|--------------|
83
+ | **Grid grammar** | every element addressed by cell (`B3`, `D7→H8`) | measurement space is the viewBox, declared |
84
+ | **W3C color names** | `lime`, `crimson` — never raw hex | nearest-name snap is named as such |
85
+ | **Spatial constraints** | `#logo CENTERED-IN #frame` as a checkable assertion | unknown types → `unchecked` + reason code, never guessed |
86
+ | **Diff vocabulary** | finite, typed: `MOVED / COLOR-CHANGE / SHAPE-CHANGE / NEW / REMOVED` | — |
87
+ | **State axis** | flags elements whose visibility depends on interaction | `state_dependent` |
88
+ | **Media axis** | flags viewport/media-dependent measurement | `media_dependent` |
89
+ | **Motion axis** | animated geometry → `not_measurable`, never a guessed frame | `motion_dependent`, `SUBJECT_TIME_VARIANT` |
90
+ | **Paint truth** | ink outside the geometric bbox (glow/shadow/blur) is flagged | `has_paint_overflow`, `visual_bbox` |
91
+ | **Convergence** | `previousIssueCount` → BASELINE / SOLVED, progress is measured | — |
92
+
93
+ The rule, stated once: **the eye measures gaps; the brain prescribes corrections.** Mirror never
94
+ clamps, smooths, or invents a value to please you. If it can't measure, it says `not_measurable`.
95
+
96
+ ---
97
+
98
+ ## Why not screenshots?
99
+
100
+ A whole field exists for "show the agent the UI" — screenshot MCPs, SVG rasterizers, pixel-diff
101
+ regression (Percy, BackstopJS, Playwright snapshots), accessibility-tree snapshots. They all share
102
+ one limit: **they hand the agent pixels (or structure) and the agent still has to guess.** A
103
+ screenshot can't be addressed (`which region?`), can't name a color precisely, can't tell you
104
+ whether an element is *measurably* inside a frame or just *looks* inside, and can't say "I don't
105
+ actually know — this is animated." (A 24-source competitive scan found no tool that combines
106
+ LLM-grammar SVG perception, declared honesty axes, and proven tool descriptions.)
107
+
108
+ Mirror replaces the pixel guess with **measured render truth in a grammar LLMs read natively** —
109
+ no image modality, no vision round-trip, no hallucinated coordinates.
110
+
111
+ ---
112
+
113
+ ## Quickstart
114
+
115
+ **Requirements:** Node ≥ 18. Mirror renders in real Chromium via Playwright, so a browser binary
116
+ is needed once.
117
+
118
+ ```bash
119
+ # install the Chromium engine Mirror measures with (one-time)
120
+ npx playwright install chromium
121
+ ```
122
+
123
+ **Run as an MCP server** — add to your client's MCP config (Claude Desktop, etc.):
124
+
125
+ ```json
126
+ {
127
+ "mcpServers": {
128
+ "vector-mirror": {
129
+ "command": "npx",
130
+ "args": ["-y", "vector-mirror"]
131
+ }
132
+ }
133
+ }
134
+ ```
135
+
136
+ On `initialize`, the server hands your agent a full **Quickstart**: the workflow, the constraint
137
+ grammar, four verified gotchas, a glossary, and the exact stop-condition — so a cold agent is
138
+ productive with **zero source reading** (this is measured; see The Proof).
139
+
140
+ **First call to try:** `vector_mirror_inspect` with an SVG string → you get the scene in grid
141
+ grammar. Then `vector_mirror_constraints` for the vocabulary, then `vector_mirror_analyze`.
142
+
143
+ ---
144
+
145
+ ## The Proof
146
+
147
+ Mirror does not ask for trust; it earns it. Three pieces of evidence:
148
+
149
+ ### 1 · Cold-consumer love metric
150
+ A cold agent (MCP only, no source) built and verified a 17-element panel in **13 tool calls with
151
+ 0 lost diagnostic loops**, scoring **8.5/10** with the reason *"never once guessed, lied, or made
152
+ me guess."* The built-in sabotage test caught exactly the two injected faults, with ready-to-apply
153
+ fix numbers. The one point deducted was an honestly-reported design seam — Mirror reports its own
154
+ weaknesses too.
155
+
156
+ ### 2 · Multi-model convergence
157
+ **13 cold agents across 5 model families** (Haiku, Sonnet, Opus, Gemini, Codex) consumed the
158
+ server independently. **Median love 8.0/10; substantive lost loops near zero.** Agents guided
159
+ through the MCP layer did not fall into the traps that un-guided raw-API users did — the guidance
160
+ layer measurably works.
161
+
162
+ ### 3 · Machine-proven tool descriptions + anti-circular self-test
163
+ Every sentence the server ships — tool descriptions, quickstart, glossary — is projected from **one
164
+ source** (`src/interface/claims.js`) and is a **probe-backed claim** (`tests/relais_red/`). A
165
+ mutation in the shipped text turns a test red. On top of that, a **5-fixture self-test** runs
166
+ **anti-circularly** — against spec-derived expected values, never against the server's own stored
167
+ output. As one consumer put it: *"that is the sentence that actually justifies trust."*
168
+
169
+ > **Determinism:** byte-identical input (sanitized DOM, time, selector) → byte-identical output,
170
+ > for a pinned Chromium engine. Verified by a dedicated determinism suite (including an
171
+ > anti-false-green mutation) and property-based fuzzing (`fast-check`).
172
+
173
+ ---
174
+
175
+ ## Architecture — three tissues, one organ
176
+
177
+ ```
178
+ ┌─────────────────────────────────────────┐
179
+ raw SVG ──────▶ │ RETINA sanitize → real Chromium │ measured truth
180
+ │ (src/adapters/renderer, src/core) │ (geometry, color,
181
+ │ hexagonal: core is import-free │ visibility, paint)
182
+ └───────────────────┬─────────────────────┘
183
+
184
+ ┌─────────────────────────────────────────┐
185
+ │ MOUTH prose + structured │ two projections
186
+ │ (src/adapters/emitter) │ of ONE truth
187
+ └───────────────────┬─────────────────────┘
188
+
189
+ ┌─────────────────────────────────────────┐
190
+ │ PACKAGE INSERT tool descriptions + │ every claim
191
+ │ quickstart (src/interface/claims.js) │ is proven
192
+ └─────────────────────────────────────────┘
193
+ ```
194
+
195
+ - **Retina:** DOMPurify sanitize → headless Chromium (Playwright) → `getBBox` /
196
+ `getBoundingClientRect` + computed style → quantized onto a letter-digit grid → AABB constraints.
197
+ - **Hexagonal contract:** all measurement lives in the adapter; the core (grid / arbitrate / diff /
198
+ palette) imports nothing from adapters or interface. Reference truth comes from outside, never
199
+ from the tool's own past output (anti-circular).
200
+ - **Tools:** `inspect`, `constraints`, `analyze`, `compare`, `bookmark`, `palette`, `arrange`,
201
+ `status`, `selftest`.
202
+
203
+ ---
204
+
205
+ ## Honest Limitations
206
+
207
+ Mirror's promise is *"the eye does not lie about what it claims to see, and does not stay silent
208
+ about what it cannot see."* That means stating the blind spots plainly:
209
+
210
+ - **Pinned-engine determinism.** Byte-identity holds for a pinned Chromium version. Cross-version
211
+ bit-stability is a standing, *not-yet-closed* watch.
212
+ - **Measurement space is the viewBox, not CSS pixels.** A visually distorted shape (round in
213
+ viewBox, oval on screen) measures by its viewBox geometry. Declared, but easy to forget.
214
+ - **Display caps.** `inspect`/`analyze` show up to 7 elements; the rest are counted in `suppressed`
215
+ but not individually addressable (no paging cursor yet). Hard limits: 500 DOM nodes / 100 KB.
216
+ - **Geometry-only motion flag (today).** The motion axis flags clock-rooted SMIL *geometry*; a
217
+ purely blinking (`opacity`) or color-animating (`fill`) element can currently return a static
218
+ value. On the roadmap.
219
+ - **Constraints check the geometric bbox**, not `visual_bbox` — paint overflow is reported
220
+ separately, not folded into `INSIDE`.
221
+ - **Prose channel language.** The structured/tool layer is English; the layout-prose vocabulary
222
+ is currently German. Full English prose projection is a v1.1 item.
223
+ - **Mask/clip geometry, `<use>` shadow edges** and a few other surfaces are over-flagged
224
+ (`indeterminate` / `not_measurable`) rather than silently guessed.
225
+
226
+ We list these because honesty is the product. Several are tracked as issues — see the roadmap.
227
+
228
+ ---
229
+
230
+ ## Roadmap (teaser)
231
+
232
+ The eye is declared healthy on its known blind-spot denominator; the growth epoch is open. Next
233
+ super-powers (deterministic + non-generating, like an eye gaining night-vision or zoom):
234
+
235
+ - a paging cursor / `limits` section so suppressed elements are addressable;
236
+ - a full paint/opacity/fill **time axis** (not just SMIL geometry);
237
+ - machine-readable measurement constants (tolerance, grid rule, color-snap distance);
238
+ - the optional **docking station** — a deterministic correction loop that drives one measured gap
239
+ to a brain-given goal (lives *downstream* of the eye, physically separate);
240
+ - full English prose projection (v1.1).
241
+
242
+ ---
243
+
244
+ ## License
245
+
246
+ [MIT](./LICENSE) © c64dos-png
247
+
248
+ <!-- The brain decides; the eye only measures. "It's in the eye of the beholder: if the eye is
249
+ healthy, the beholder can do whatever they want with it." -->
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "vector-mirror",
3
+ "version": "1.0.0",
4
+ "description": "Deterministic SVG perception eye for LLM agents — renders in a real browser and reports what is actually where, in grid cells and W3C color names, with prose and structured output. Measures, never guesses. MCP server.",
5
+ "type": "module",
6
+ "main": "src/interface/tools.js",
7
+ "bin": {
8
+ "vector-mirror": "src/interface/server.js"
9
+ },
10
+ "mcpName": "io.github.c64dos-png/vector-mirror",
11
+ "engines": {
12
+ "node": ">=18"
13
+ },
14
+ "files": [
15
+ "src/",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "test": "node scripts/gate.mjs --tier=full",
21
+ "test:fast": "node scripts/gate.mjs --tier=fast",
22
+ "gate:docs": "node scripts/doc_budget.mjs",
23
+ "audit": "npm audit --audit-level=moderate",
24
+ "selftest": "node tests/audit/selftest.mjs",
25
+ "test:determinism": "node tests/integration/test_determinism.mjs",
26
+ "test:sanitizer": "node tests/integration/test_sanitizer_d1.mjs",
27
+ "test:use_graph": "node tests/unit/test_use_graph.js"
28
+ },
29
+ "keywords": [
30
+ "mcp",
31
+ "mcp-server",
32
+ "model-context-protocol",
33
+ "llm",
34
+ "llm-agents",
35
+ "llm-tools",
36
+ "svg",
37
+ "svg-rendering",
38
+ "playwright",
39
+ "deterministic",
40
+ "perception",
41
+ "agent-tools"
42
+ ],
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/c64dos-png/vector-mirror.git"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/c64dos-png/vector-mirror/issues"
49
+ },
50
+ "homepage": "https://github.com/c64dos-png/vector-mirror#readme",
51
+ "license": "MIT",
52
+ "dependencies": {
53
+ "@modelcontextprotocol/sdk": "^1.29.0",
54
+ "async-mutex": "^0.5.0",
55
+ "isomorphic-dompurify": "2.36.0",
56
+ "opossum": "^9.0.0",
57
+ "playwright": "^1.40.0",
58
+ "zod": "^3.25.76"
59
+ },
60
+ "devDependencies": {
61
+ "ajv": "^8.20.0",
62
+ "ajv-formats": "^3.0.1",
63
+ "fast-check": "^4.7.0"
64
+ }
65
+ }