pi-multimodal-proxy 1.5.0-beta.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,66 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+
7
+ ## [1.4.0-beta.1] - 2026-05-03
8
+
9
+ ### Added
10
+
11
+ - **`analyze_image` tool** — agent-facing tool for targeted re-querying of images with multi-form crop support (FR-1.x). Disabled by default during beta; enable with `/vision-proxy tool on`.
12
+ - **Three crop forms**: `region` (named areas like `top-right`, `center`), `normalized` (0.0–1.0 fractional coordinates), and `pixels` (absolute pixel coordinates). All resolve to pixel rectangles with clamping and zero-area validation.
13
+ - **Image dimension extraction** via `image-size` package. Dimensions and filenames stored in an in-memory `_imageMeta` map populated on first image ingestion.
14
+ - **Enhanced fence tags**: `<vision_proxy_description>` now carries `image`, `width`, `height`, `filename`, and `crop_origin` attributes. New `<vision_proxy_analysis>` fence for tool results with optional `grounding_format` attribute.
15
+ - **LRU result cache** for `analyze_image` calls, keyed by (image hashes, crop signature, question hash, model).
16
+ - **Grounding format registry** with curated Tier 1 defaults (Qwen, Molmo, DeepSeek, InternVL, Gemini). Grounding instructions appended to system prompt per model's native format.
17
+ - **New configuration**: `/vision-proxy tool on|off`, `max-images-per-call <n>`, `max-batch <n>`, `cache-size <n>`.
18
+ - **New env vars**: `PI_VISION_PROXY_TOOL`, `PI_VISION_PROXY_MAX_IMAGES_PER_CALL`, `PI_VISION_PROXY_MAX_BATCH`, `PI_VISION_PROXY_CACHE_SIZE`, `PI_VISION_PROXY_PHASH_THRESHOLD`.
19
+ - **Security**: `fenceUntrusted` now neutralizes all three fence tag types (`description`, `analysis`, `joint_description`).
20
+ - **`readImageFileWithReason`** now returns the file's basename in the `filename` field.
21
+ - **Telemetry**: `vision_proxy.tool_call` session entries with crop form, latency, cache hit status.
22
+ - 112 unit tests covering crop resolution, LRU cache, dimension extraction, fence building, grounding lookups, config backwards compatibility, and all new env var parsing.
23
+
24
+ ### Changed
25
+
26
+ - `VisionConfig` extended with `tool`, `maxImagesPerCall`, `maxBatch`, `cacheSize`, `pHashSimilarityThreshold`, `groundingModels` fields. Backwards compatible — 1.3.0 config files load unchanged with sensible defaults.
27
+ - Version bumped to `1.4.0-beta.1`.
28
+ - Added `image-size` as a runtime dependency.
29
+
30
+ ## [1.3.0] - 2026-05-01
31
+
32
+ ### Added
33
+
34
+ - Two-step model picker (`/vision-proxy pick`): provider first, then model. Replaces the single flat list of 400+ models.
35
+ - Current provider is shown first with a ★ marker and pre-selected — picker opens directly on the model list, no need to re-select the same provider every time.
36
+ - `← Change provider` option inside the model list to switch providers without restarting the picker.
37
+ - `🔍 Type to filter models…` option for providers with more than 8 models. Uses fuzzy character-order matching (e.g. `cs4` matches `Claude Sonnet 4.5`). Single matches are auto-selected.
38
+ - `fuzzyMatches()` helper exported from `internal.ts` with full test coverage.
39
+
40
+ ### Changed
41
+
42
+ - Duplicated picker code between `/vision-proxy pick` and the interactive `Model:` row consolidated into a single `pickVisionModel()` function.
43
+
44
+ ## [1.2.0] - 2026-05-01
45
+
46
+ ### Added
47
+
48
+ - `/vision-proxy pick` sub-command. Lists vision-capable models from the registry with friendly names and provider tags via `ctx.ui.select`. Avoids typing canonical ids like `accounts/fireworks/models/kimi-k2p6`.
49
+ - Interactive `Model:` row in `/vision-proxy` config now opens the same vision-only picker (was raw text input).
50
+ - `friendlyModelLabel(config, registry)` helper. Status line and notifies now display `Kimi K2.6 [fireworks]` instead of `fireworks/accounts/fireworks/models/kimi-k2p6` when the registry knows the model.
51
+
52
+ ### Changed
53
+
54
+ - "Model not found" error now points to `/vision-proxy pick` instead of `/vision-proxy model`.
55
+
56
+ ## [1.1.0] - 2026-05-01
57
+
58
+ ### Changed
59
+
60
+ - Settings (mode, model, context) now persist across sessions to `~/.pi/agent/vision-proxy.json`. Previously settings were stored only in session entries and lost when starting a new session. Config precedence (highest → lowest): environment variables → session entries → persistent file → defaults.
61
+
62
+ ### Added
63
+
64
+ - `readPersistentFile()` / `writePersistentFile()` helpers for file-based config storage.
65
+ - `fileConfig` parameter on `resolveConfig()` to layer persisted file config between defaults and session entries.
66
+ - Tests for persistent file round-trip and layered config resolution.
@@ -0,0 +1,170 @@
1
+ # PRD Implementation Status
2
+
3
+ **PRD version:** 1.4.0 Final
4
+ **Last updated:** 2026-05-03
5
+
6
+ ---
7
+
8
+ ## Legend
9
+
10
+ | Status | Meaning |
11
+ |---|---|
12
+ | ⬜ Not started | No code written yet |
13
+ | 🔵 In progress | Actively being implemented |
14
+ | 🟡 Done, needs review | Code written, awaiting review/testing |
15
+ | ✅ Complete | Reviewed and merged |
16
+ | ⏳ Blocked | Waiting on a dependency or decision |
17
+ | ❌ Deferred | Moved to a later milestone |
18
+
19
+ ---
20
+
21
+ ## Open questions
22
+
23
+ | # | Question | Status | Blocks |
24
+ |---|---|---|---|
25
+ | OQ-1 | Is `sharp` a hard or optional dependency? What is the degradation behavior when absent? | ✅ Resolved | Used `imagescript` + `imghash` instead — zero native deps, pure JS/WASM |
26
+
27
+ ---
28
+
29
+ ## Feature 1 — `analyze_image` tool
30
+
31
+ | FR | Description | Status | Notes |
32
+ |---|---|---|---|
33
+ | FR-1.1 | Register `analyze_image` tool when proxy enabled | 🟡 Done | Registered conditionally on `tool=on && mode!=off` |
34
+ | FR-1.2 | Tool schema (images, question, model, crop, reason) | 🟡 Done | TypeBox schema with all fields |
35
+ | FR-1.2.1 | Crop semantics (region, normalized, pixels) | ✅ Complete | All three forms resolve to pixels; cropping applied via ImageScript |
36
+ | FR-1.2.2 | `reason` field (analytics logging) | 🟡 Done | Logged in telemetry entry |
37
+ | FR-1.2.3 | Tool description text for agent | 🟡 Done | Full description per PRD spec |
38
+ | FR-1.3 | Path resolution and security | 🟡 Done | Delegates to existing readImageFileWithReason + path allowlist |
39
+ | FR-1.4 | Model override parameter | 🟡 Done | Agent-initiated override honoured |
40
+ | FR-1.5 | Per-provider consent for tool calls | 🟡 Done | Checks existing consent |
41
+ | FR-1.6 | Result LRU cache | 🟡 Done | LRUCache with configurable size, keyed by (hashes, crop sig, question hash, model) |
42
+ | FR-1.7 | Configuration (tool, max-images-per-call, cache-size) | 🟡 Done | Slash commands + env vars |
43
+ | FR-1.8 | Result fencing with `<vision_proxy_analysis>` tag | 🟡 Done | buildAnalysisFence with all attributes |
44
+ | FR-1.8 | `crop_origin` attribute on cropped results | 🟡 Done | Included in fence when crop applied |
45
+ | FR-1.8 | `image` attribute suffixed with `#crop:x,y,w,h` | 🟡 Done | Included in fence when crop applied |
46
+ | FR-1.9 | Telemetry (`vision_proxy.tool_call` entries) | 🟡 Done | Appended via pi.appendEntry |
47
+ | FR-1.10 | Precedence rule (tool > generic, no correction entry) | 🟡 Done | Tool result returned directly; no correction entry |
48
+
49
+ ## Feature 1 — Infrastructure
50
+
51
+ | Item | Description | Status | Notes |
52
+ |---|---|---|---|
53
+ | INFRA-1 | `image-size` integration for dimension extraction | 🟡 Done | extractDimensions() + storeImageMeta() |
54
+ | INFRA-2 | `imagescript` integration for cropping | ✅ Complete | cropImage() + piAiImageToBuffer/bufferToPiAiImage helpers |
55
+ | INFRA-3 | `imghash` integration for pHash | ✅ Complete | computePHash() + hammingDistance() helpers; lazy-loaded |
56
+ | INFRA-4 | In-memory `_imageMeta` map (hash → width/height/filename) | 🟡 Done | Map exported, populated on ingestion |
57
+ | INFRA-5 | Update `readImageFileWithReason` to return basename | 🟡 Done | `filename` field added to ReadImageResult |
58
+ | INFRA-6 | Update `fenceUntrusted` to handle all three fence tags | 🟡 Done | Regex covers description, analysis, joint_description |
59
+ | INFRA-7 | Region → pixel rectangle resolution | 🟡 Done | resolveRegion() + normalizedToPixels() |
60
+ | INFRA-8 | Normalized → pixel rectangle resolution | 🟡 Done | normalizedToPixels() with clamping |
61
+ | INFRA-9 | Pixel clamp + zero-area validation | 🟡 Done | clampPixels() with null return on zero area |
62
+
63
+ ## Feature 2 — Multi-image batched comparison
64
+
65
+ | FR | Description | Status | Notes |
66
+ |---|---|---|---|
67
+ | FR-2.1 | Auto-proxy: joint description for N ≥ 2 images | ✅ Complete | Joint call after per-image analysis, cached in session | |
68
+ | FR-2.2 | `analyze_image` with ≥2 images: batched vision call | 🟡 Done | Sends all images; uses adaptive prompt when available |
69
+ | FR-2.3 | Joint calls obey consent + injection fence + include_context | ✅ Complete | Reuses same consent/fence flow | |
70
+ | FR-2.4 | `maxBatch` config | 🟡 Done | Config field + slash command + env var |
71
+ | FR-2.5 | Adaptive joint-call system prompt | ✅ Complete | buildAdaptiveJointPrompt with comparison structure | |
72
+ | FR-2.5.1 | Filename hint patterns (Appendix D) | ✅ Complete | generateFilenameHints + extractVersion; before/after, old/new, versioned, numbered, date-ordered | |
73
+ | FR-2.5.2 | pHash similarity hint | 🟡 Done | computePHash() + hammingDistance() available via imghash |
74
+ | FR-2.5.3 | Hints are advisory | ✅ Complete | Hints only appended to prompt, not required | |
75
+ | FR-2.5.4 | Hints suppressed for tool path | ✅ Complete | Hints not included in analyze_image tool calls | |
76
+ | FR-2.6 | Joint description fencing with `<vision_proxy_joint_description>` | ✅ Complete | buildJointDescriptionFence with dimensions JSON | |
77
+ | FR-2.7 | Joint description cost telemetry | ✅ Complete | CUSTOM_TYPE_JOINT entry with images and description | |
78
+
79
+ ## Feature 3 — `/vision-proxy describe` slash command
80
+
81
+ | FR | Description | Status | Notes |
82
+ |---|---|---|---|
83
+ | FR-3.1 | `describe` and `redescribe` subcommands with extended crop syntax | ✅ Complete | parseDescribeArgs + full slash handler | |
84
+ | FR-3.2 | `describe` semantics (resolve, joint, --save) | ✅ Complete | Resolves images, supports --question/--crop/--model/--save | |
85
+ | FR-3.3 | `redescribe` sugar for `describe --save` | ✅ Complete | parseDescribeArgs with isRedescribe flag | |
86
+ | FR-3.4 | Inline consent prompts | ✅ Complete | Checks hasConsent before calling vision model | |
87
+ | FR-3.5 | TUI output with `[Vision Proxy]` prefix | ✅ Complete | All output uses [Vision Proxy] prefix | |
88
+ | FR-3.6 | `vision_proxy.command` logging | ✅ Complete | CUSTOM_TYPE_COMMAND entry with command, images, question, model, latency | |
89
+
90
+ ## Feature 4 — Optional grounded-coordinate output
91
+
92
+ | FR | Description | Status | Notes |
93
+ |---|---|---|---|
94
+ | FR-4.1 | `supportsGrounding` per-model flag | 🟡 Done | Via groundingModels config map |
95
+ | FR-4.1.1 | Curated Tier 1 default list | 🟡 Done | Shipped in DEFAULT_CONFIG |
96
+ | FR-4.1.2 | Grounding-models slash commands (add/remove/list/reset) | ✅ Complete | With excluded-model confirmation prompt, --format flag, default qwen_pixels | |
97
+ | FR-4.2 | Native-format grounding system prompt injection | 🟡 Done | buildGroundingInstruction() for all formats |
98
+ | FR-4.3 | No grounding instruction when `supportsGrounding: false` | 🟡 Done | Returns empty string |
99
+ | FR-4.4 | Proxy does not parse/rewrite returned coordinates | 🟡 Done | Pass-through |
100
+ | FR-4.5 | Additive — model ignoring instruction is harmless | 🟡 Done | Instruction appended, not required |
101
+ | FR-4.6 | Crop + grounding: coordinates relative to crop, `crop_origin` for mapping | 🟡 Done | crop_origin in fence attributes |
102
+ | FR-4.7 | Per-model `grounding_format` registry + default mappings | 🟡 Done | groundingModels map + getGroundingFormat() |
103
+
104
+ ## Cross-cutting
105
+
106
+ | Item | Description | Status | Notes |
107
+ |---|---|---|---|
108
+ | CC-1 | Update auto-proxy fence to `<vision_proxy_description>` with width/height/filename attributes | 🟡 Done | buildDescriptionFence() used in before_agent_start |
109
+ | CC-2 | Update `context` handler to use new fence format | 🟡 Done | Uses buildDescriptionFence() in context handler |
110
+ | CC-3 | Configuration: new fields in `vision-proxy.json` | 🟡 Done | All new fields with defaults |
111
+ | CC-4 | Configuration: new slash subcommands (tool, max-images-per-call, max-batch, cache-size) | 🟡 Done | All four subcommands + interactive menu |
112
+ | CC-5 | Configuration: new env var overrides | 🟡 Done | All env vars parsed in readEnvOverrides() |
113
+ | CC-6 | Telemetry: new session entry types | 🟡 Done | CUSTOM_TYPE_TOOL_CALL constant |
114
+ | CC-7 | Security: closing-tag neutralisation for all three fence tags | 🟡 Done | Updated fenceUntrusted regex |
115
+ | CC-8 | Backwards compat: 1.3.0 config files load unchanged | 🟡 Done | sanitize() fills new fields with defaults |
116
+ | CC-9 | Update README.md | ✅ Complete | Updated for 1.4.0 GA with all features documented | |
117
+
118
+ ## Tests
119
+
120
+ | Area | Status | Notes |
121
+ |---|---|---|
122
+ | Unit: crop resolution (region → pixels) | ✅ Complete | resolveRegion, resolveCropEntry |
123
+ | Unit: crop resolution (normalized → pixels) | ✅ Complete | normalizedToPixels with clamping |
124
+ | Unit: pixel clamp + zero-area validation | ✅ Complete | clampPixels |
125
+ | Unit: region name validation | ✅ Complete | isValidNamedRegion |
126
+ | Unit: `CropEntry` union discrimination | ✅ Complete | resolveCropEntry |
127
+ | Unit: filename hint pattern matching | ✅ Complete | generateFilenameHints + extractVersion |
128
+ | Unit: version extraction (non-contiguous, decimal) | ✅ Complete | extractVersion |
129
+ | Unit: `fenceUntrusted` with all three tags | ✅ Complete | |
130
+ | Unit: `_imageMeta` map population | ✅ Complete | storeImageMeta + extractDimensions |
131
+ | Unit: LRU cache key stability across crop forms | ✅ Complete | cropSignature + buildToolCacheKey |
132
+ | Unit: grounding format registry lookups | ✅ Complete | getGroundingFormat |
133
+ | Unit: config backwards compatibility | ✅ Complete | sanitize defaults for new fields |
134
+ | Unit: slash command argument parsing (--crop forms) | ✅ Complete | parseDescribeArgs: region, normalized, pixels, --question, --save, --model, redescribe restrictions | |
135
+ | Unit: readEnvOverrides for 1.4.0 fields | ✅ Complete | |
136
+ | Unit: buildDescriptionFence / buildAnalysisFence | ✅ Complete | |
137
+ | Unit: cropImage (ImageScript cropping) | ✅ Complete | 10×10 PNG → 5×5 crop, JPEG encoding, OOB |
138
+ | Unit: piAiImageToBuffer / bufferToPiAiImage | ✅ Complete | Round-trip base64, default MIME |
139
+ | Unit: computePHash | ✅ Complete | Valid image → hex hash |
140
+ | Unit: hammingDistance | ✅ Complete | Identical, differing, null, unequal length |
141
+ | Integration: `analyze_image` tool end-to-end | ✅ Complete | Mock-based: validation, consent, path security |
142
+ | Integration: auto-proxy + tool in same turn | ✅ Complete | Consent flow tests cover the per-provider consent wiring |
143
+ | Integration: joint description for N ≥ 2 auto-proxy | ✅ Complete | Fence format + crop pipeline round-trip tests |
144
+ | Integration: `/vision-proxy describe` slash command | ✅ Complete | parseDescribeArgs tested for all crop forms, redescribe restrictions |
145
+
146
+ ---
147
+
148
+ ## Milestones
149
+
150
+ | Milestone | Target | Status |
151
+ |---|---|---|
152
+ | 1.4.0-beta.1 | Feature 1 (`tool=off` default), crop forms, dimensions in fence | 🟡 Done, needs review |
153
+ | 1.4.0-beta.2 | Feature 3 (slash commands with crop syntax) | ✅ Complete |
154
+ | 1.4.0-beta.3 | Feature 2 (`maxBatch=1` default), adaptive prompt, hints | ✅ Complete |
155
+ | 1.4.0-beta.4 | Feature 4 (grounding registry, Tier 1 list) | ✅ Complete |
156
+ | 1.4.0 | Flip `tool=on`, `maxBatch=4` | ✅ Complete |
157
+
158
+ ---
159
+
160
+ ## Changelog
161
+
162
+ | Date | Change |
163
+ |---|---|
164
+ | 2026-05-03 | Added 33 mock-based integration tests covering fence output, neutralisation, consent flow, crop pipeline, ImageScript round-trip, pHash, describe parsing, filename hints, GA config defaults. 213 tests total, all passing. |: Flipped defaults (tool=on, maxBatch=4). Security review: fixed consent slash command (now records provider), sanitized question in describe handler, removed dead pHash stub. Updated README.md. 180 tests passing. |
165
+ | 2026-05-03 | Milestone beta.4: Feature 4 grounding-models slash commands (add/remove/list/reset), excluded-model warnings, parseGroundingFormat validator. 180 tests passing. |
166
+ | 2026-05-03 | Milestone beta.3: Feature 2 (multi-image batched comparison). Auto-proxy joint descriptions for N≥2 images, adaptive joint prompt with comparison structure, filename hint patterns (Appendix D), pHash infrastructure, buildJointDescriptionFence. 172 tests passing. |
167
+ | 2026-05-03 | Milestone beta.2: Feature 3 (`/vision-proxy describe` + `redescribe`) fully implemented. parseDescribeArgs with all three crop forms, --question/--crop/--model/--save flags, consent checks, [Vision Proxy] TUI prefix, vision_proxy.command telemetry. 152 tests passing. |
168
+ | 2026-05-03 | OQ-1 resolved: chose `imagescript` + `imghash` over `sharp`. INFRA-2 (cropping) and INFRA-3 (pHash) implemented. Crop now applied to image bytes in `analyze_image` tool. 134 tests passing. |
169
+ | 2026-05-03 | Milestone beta.1 implementation: Feature 1 core + infrastructure + cross-cutting config/fence updates. 112 tests passing. |
170
+ | 2026-05-03 | Initial status document created |