245 Commits

Author SHA1 Message Date
teamchong f1cc22b8b6 chore: release 0.6.5 v0.6.5 2026-06-25 17:23:54 -04:00
teamchong 78abd081e4 feat(factsheet): byte-stable fact-sheet of exact identifiers on Claude + OpenAI paths
Recovers precision-critical strings (paths/ids/versions) from the imaged
slab as cache-stable text so they survive OCR. Byte-identical across turns,
validated on both Claude and gpt-5.5 paths.
2026-06-25 17:23:03 -04:00
teamchong 45d9d57ce5 feat(dashboard): add Sonnet 4.6 and Sonnet 5 chips to image-model catalog 2026-06-25 17:23:03 -04:00
teamchong 3d3b31e66f docs(findings): reframe — correct baseline is /compact, not verbatim recall
Adds the 2026-06-23 entry: pxpipe's right comparison is /compact (itself lossy),
not perfect recall. compact-vs-gist analysis (same job, opposite loss profiles —
pxpipe wins on Fable, peer-on-Opus with abstention, recoverability as the
structural edge); code-grounded notes on the open-source harnesses (codex,
opencode); ties to the agent-memory (reducer/channel-routing) direction. n=1
behavioral parts flagged; eval numbers cross-checked against existing entries.
Docs only — not shipped in the npm package.
2026-06-23 23:13:52 -04:00
teamchong 8417380061 test: e2e guards for cache stability, dashboard honesty, and design behaviors
Test-only (no src changes). Adds fake-upstream + real-createProxy suites:

- cache-stability-e2e: byte-identical cacheable prefix across growth, marker
  conservation + relocation onto the history image, tool-pair integrity.
- savings-math-e2e: gate never images at a net loss; baselineImagedTokens
  cross-checked against the real o200k tokenizer; baselineTokens wired from
  the count_tokens probe.
- savings-honesty: categorical no-overclaim invariants (warm <= cold,
  saved==delta*weight, zero credit on passthrough) + per-model pricing guards
  (Anthropic 1.25/0.1 shared policy; GPT 0.1x model-gated).
- design-behavior-e2e: system-prompt imaging, history collapse (old imaged /
  recent legible), tools-in-recent-turns kept usable.
- dashboard-api: GPT cold-cache parity (full text delta, not 0.1x warm rate).

Each guard mutation-verified (broke source, confirmed red, reverted).
2026-06-23 12:30:16 -04:00
teamchong c75179b50f chore: release 0.6.4
Anthropic-path recency anchor for collapsed history (commit 9eb66f5): banner now
warns against reopening low-N turns, plus a bounded ~300-char text pointer to the
most-recent collapsed user turn after the images. Patch — a guardrail fix on the
visible path; cache-safe (pointer sits after the cache_control breakpoint).

Adversarially reviewed: 8 findings, all low/nit — 6 are positive confirmations
(cache-safe, matcher in sync via shared constant, relocation robust to the trailing
block, GPT path unaffected, no off-by-one); 2 benign (tool-result-only user falls
back to an earlier user turn with a correct t-index; mid-word preview truncation).

474 tests pass; tsc + build clean.
v0.6.4
2026-06-23 06:45:39 -04:00
Steven Chong 9eb66f54f2 fix(history): anchor collapsed history recency 2026-06-23 06:37:44 -04:00
teamchong 85f8536b67 chore: release 0.6.3
GPT autonomous agents (OpenCode/gpt-5.5) no longer lose the live request to
history imaging — the most-recent user turn is pinned as legible text between
before/after history images, with the guard echoing it. Fixes snap/confabulation
/off-task drift; validated on gpt-5.5 (stays on task, no off-task edits).

Patch, not minor: GPT is opt-in/WIP and hidden in the dashboard, and the visible
Anthropic path is byte-identical (pin commit 08a2b0a touched only the GPT path).

Also: clarifying comment on the minCollapseTokens gate (counts imageable work
only; sub-floor histories stay fully text by design — request stays legible).

473 tests pass; tsc + build clean.
v0.6.3
2026-06-22 16:25:02 -04:00
teamchong 08a2b0a118 feat(gpt): pin the live user request as text in history collapse
Autonomous GPT agents (OpenCode/gpt-5.5) send ONE human request then a long
run of tool turns. The lone request is the OLDEST turn, so history collapse
imaged it first and the model lost it — "I wonder what the user actually asked"
→ off-task drift (observed: agent edited compaction.ts instead of answering a
compare question). The live-request guard's "the request is the trailing user
message" heuristic assumes interactive chat and points at nothing here.

Fix: keep the most-recent user turn OVERALL as legible TEXT, spliced between
before-pin and after-pin history images inside the synthetic user message;
older user turns stay imaged (must not look live — snap-to-first-prompt guard).
The guard now echoes the request verbatim (capped). History is imaged on both
sides of the pin, so compression barely changes.

Cache safety (adversarially reviewed):
- Pin ONLY when the latest user turn is INSIDE the collapse range. If it's in
  the kept tail (ordinary interactive turn) it's already native text — pinning
  an older in-range turn would migrate the pin across collapse-chunk boundaries
  and re-image frozen history. Restricting to the latest turn fixes its position
  until the next prompt, so before/after sections stay byte-stable across a run.
- Undersized before-pin remainder merges into the previous before-section rather
  than emitting a sub-threshold (net-negative) image.

Both Chat Completions and Responses paths. 473 tests pass; tsc + build clean.
Not yet released — validate on the gpt-5.5 machine first.
2026-06-22 12:15:16 -04:00
teamchong 681f40fd5b chore: release 0.6.2 v0.6.2 2026-06-22 11:07:24 -04:00
teamchong 22d89ee37d fix(sessions): complete honest cache-math warmth gate
b9418bf repriced cr>0-but-no-prior turns warm in dashboard.ts (update + replay)
but left the identical gate in sessions.ts unfixed — so the Sessions panel still
fabricated inflated savings after a restart/eviction or on the first tracked turn
(a cr>0 turn priced cold bills the text counterfactual a 1.25x create on a prefix
we KNOW was cached). Mirror the dashboard fix so "honest cache math" holds on every
panel.

- sessions.ts: warm follows the observed read (cr>0) alone; with no fresh prior,
  assume the cacheable prefix was fully reused (prevCacheable = cacheable) rather
  than mispricing a known-warm read.
- sessions.test.ts: the "real prefix compression" case had cr=100 on its first
  turn (observably warm) yet asserted the cold-priced 22490 fabrication; corrected
  to the honest 1790 (warm) + 95 = 1885. Now a regression guard for this fix.
- dashboard.ts: drop the stale "no per-session state" replay comment — replay now
  mirrors update()'s per-session warmth.

Found by adversarial review of b9418bf. 462 tests pass; tsc clean.
2026-06-22 11:07:02 -04:00
Steven Chong b9418bfa6f fix(gpt): cap history images and honest cache math 2026-06-22 10:46:47 -04:00
teamchong 023669dec4 chore: release 0.6.1 v0.6.1 2026-06-21 17:43:03 -04:00
Steven Chong f6be3d3f88 fix(openai): keep opening prompt collapsible 2026-06-21 17:37:51 -04:00
teamchong 62b9c872ac chore: release 0.6.0 v0.6.0 2026-06-21 17:17:56 -04:00
teamchong fe6b0ec2a8 feat: structure-through role attribution + turn-index recency anchor for history images
Collapsed-history images now carry who-said-what AND when, instead of leaving
the model to reconstruct it from a flattened, role-ambiguous blob (which led it
to resurface the opening turn as if it were the live request).

- Turn-index recency anchor: each collapsed turn serializes as <user t="N"> /
  <assistant t="N"> with an ABSOLUTE turn index (larger N = more recent), so the
  model can tell turn 1 from turn 60. Absolute (never "N ago"/"i of total") so
  frozen chunks stay byte-identical and keep hitting the prompt cache. Mirrored on
  the GPT path. (history.ts, openai-history.ts, openai.ts)
- colorByRole: role tags are tinted via a parallel "slot string" carried from
  serialize time (structure-through), replacing the parse-back that miscolored a
  body quoting a literal <tag>. (render.ts, history.ts)
- neutralizeSentinel: a pre-existing ↵ (U+21B5) in content is swapped to ⏎
  (U+23CE) in render-prep so reflow packs newlines instead of bailing to a raw,
  unpacked render — common when the content is about pxpipe itself. Render-only;
  originals preserved. (render.ts, transform.ts, history.ts, openai-history.ts)
- Per-model GPT vision/render profiles, retunable via PXPIPE_GPT_PROFILES,
  behavior-identical to the old hardcoded values. (gpt-model-profiles.ts)

Review fixes folded in:
- Banner intro/outro consolidated to a single source of truth; the GPT path now
  aliases the Anthropic constant instead of byte-copying it (can't drift).
- slotCopyBody neutralizes literal slot markers to a width-equivalent control
  char (U+0003), not a space the minifier would strip and misalign.

459 tests pass; tsc clean.
2026-06-21 17:17:08 -04:00
teamchong dbc6d895f2 chore: release 0.5.0 v0.5.0 2026-06-20 21:16:47 -04:00
Steven Chong 47b5e412b3 feat: cache-stable history-collapse imaging for GPT (OpenAI) + Anthropic
Collapse old conversation history into rendered PNG sections so the model
reads a compact image instead of re-billed text, while preserving prompt
caching and tool-call behavior. Measures real vs compressed token/cost.

Core:
- GPT history collapse (openai-history.ts): append-only, o200k token-length
  sectioning. Sections seal only at a tool-closed boundary (open call-id set
  empty), so a function_call and its function_call_output never split across
  the collapse cut. Fixes the OpenAI 400 "No tool call found for function
  call output with call_id ..." that hit long Responses-API sessions.
- Anthropic cache contract (history.ts): append-only per-chunk rendering;
  cache_control markers are preserved/moved, never added; chunk boundaries
  align with caller marker seams for byte-stable prefix caching.
- GPT image budget (openai.ts): detail:'original' for gpt-5.x, flagship
  vision-multiplier fix, patch cap; schema-strip preserves real descriptions.
- Savings accounting (openai-savings.ts): cached_tokens + vision-token basis.

Model scope (applicability.ts):
- Default imaged scope = claude-fable-5 + gpt-5.6.
- gpt-5.5 and claude-opus-4-8 stay opt-in: same pipeline, but they degrade
  reading dense imaged history (gist drift), so silently imaging them by
  default is wrong. Promotion is gated on an OCR/recall threshold.

Dashboard: GPT + Anthropic rendering, per-family model toggles, persisted
metrics, thumbnail-expired session UI, reflow/newline handling.

Tests: cache-alignment (GPT + Anthropic), history sectioning + tool-boundary
invariants, savings, dashboard, sessions/restart-restore. 452 passing.
2026-06-20 21:11:24 -04:00
teamchong c44cdf4258 chore: release 0.4.0 v0.4.0 2026-06-19 16:53:11 -04:00
teamchong 8b15525cb9 fix(transform): relocate cache anchor onto history image (stop 1.25x re-create)
The history image sits after the slab in prefix order, but the single cache
breakpoint was anchored on the slab. So the largest block — the ~141k-token
history image — only cached when the caller's roaming downstream marker happened
to land after it. When it didn't, the whole history image re-created at the 1.25x
cache_create rate every turn instead of amortizing into 0.1x reads. On a warm
session that turned a real compression win into a net loss (the "411% more"
the dashboard correctly surfaced).

Fix is pure relocation: move pxpipe's single breakpoint from the slab onto the
LAST history image (which sits after the slab), so slab + history cache as one
stable prefix — created once, then read. Marker count is unchanged: pxpipe still
never ADDS a marker (Task #21), it only moves the caller's existing one. When no
slab imaged, the history path keeps its own relocation.

- transform.ts: relocateAnchorToHistoryImage on the collapse-success branch
- history.test.ts: two old "history carries no marker" invariants upgraded to
  assert the relocation (last history image carries it; exactly one marker total)
2026-06-19 16:39:46 -04:00
teamchong b20c976f61 fix(dashboard): restore Saved + Details on replayed rows after restart
replay() rebuilt RecentRow from the JSONL but never set
session_saved_so_far_delta, img_id, or contextHistory — so after a restart
old rows showed "-" in Saved and had no Details link, while live rows did.
Live update() does all three; replay() now matches.

- replay() sets session_saved_so_far_delta (baselineInputEff - actualInputEff,
  same cache-weighted math as live) so the Saved column survives a restart.
- replay() rebuilds the Context Map breakdown from persisted fields
  (baseline_tokens, bucket_chars, image_count, usage) with a synthetic
  nextImageId, and sets img_id/img_ids so the Details link resolves.
- The PNG ring is in-memory and not restorable; restored entries carry
  imageIds:[] and a `restored` flag, and the Details panel shows
  "thumbnails expired when the proxy restarted" instead of dead <img>s.
- +2 replay tests (restart-restore.test.ts).

376 tests pass; typecheck + build clean.

Claude-Session: https://claude.ai/code/session_01RV8uiMYNhrAdTUzJ9fkpcp
2026-06-19 16:06:59 -04:00
teamchong ad526fce31 fix(ci): regenerate pnpm-lock.yaml after canvas moved to devDependencies
@napi-rs/canvas was moved dependencies -> devDependencies (edge-safe
packaging) but the lockfile still listed it under dependencies, so CI's
`pnpm install --frozen-lockfile` refused the mismatch. Regenerated with
--lockfile-only; no version changes.

Claude-Session: https://claude.ai/code/session_01RV8uiMYNhrAdTUzJ9fkpcp
2026-06-19 16:01:38 -04:00
teamchong 48f82ffee2 fix(dashboard): hero uses cache-weighted tokens (match Details/Saved)
The session hero computed (1 - sent / raw_count_tokens_baseline) — the same
cache-blind math just removed from the Details panel. It priced the all-text
baseline at full rate, ignoring that most of it would have been cheap
cache-reads, so a warm net-loss session showed a big "fewer tokens" win up top
while Details/Saved showed a loss. Two panels, one session, opposite signs.

- renderSessionSummaryFragment now reads baselineInputWeighted /
  actualInputWeighted (the same cache-weighted pair as Details + the Saved
  column) instead of rawActualTokens / rawBaselineTokens.
- Headline is input-only; dropped the output-on-both-sides lumping (output is
  never "sent" and only dampened the %).
- Honest labels: "effective tokens … after normal cache discounts"; still no $
  assumptions (0.1x/1.25x are token multipliers, not $/Mtok).
- types.ts: corrected stale field comments (raw fields are math-drawer only).
- +4 hero tests pinning direction to the weighted pair and that output can't
  move the headline %.

374 tests pass; typecheck + build clean.
2026-06-19 15:58:22 -04:00
teamchong c899d7d836 fix(dashboard): Details headline uses cache-weighted tokens (match row)
The Details panel divided the raw count_tokens baseline by sent tokens
(cache-blind), over-claiming "X% smaller" even when the cache-aware row
showed a net loss — the two panels could flatly contradict each other.

- dashboard.ts: feed baselineInputEff/actualInputEff/haveBaseline into
  ContextMapData (same weighted pair the recent row uses); push after
  the eff-tokens are computed so the breakdown matches.
- fragments.ts: headline divides weighted baseline by weighted actual,
  says "bigger" on a net loss, with a clarifying sub-line; no savings
  claim when the baseline probe did not resolve.
- +4 tests (context-map.test.ts) pinning headline direction to the row.

370 tests pass; typecheck + build clean.
2026-06-19 14:38:17 -04:00
teamchong 97d50513aa feat(openai): GPT-5.x family + Responses API support
- Generalize isPxpipeSupportedGptModel to the GPT-5 family (gpt-5, 5.x,
  -mini/-nano), default-on; [variant] tags stripped.
- OpenAI render switches to the 768px portrait strip (GPT_STRIP_COLS=152)
  so the API's mandatory shortest-side->768 downscale never shrinks our
  5px glyphs; downscale-free in both tile and patch regimes.
- Add OpenAI vision-token cost model (resolveVisionCost/openAIVisionTokens)
  and rewire the Chat Completions profitability gate to use it instead of
  the Anthropic 750px/token model.
- Add transformOpenAIResponses for the /v1/responses (Responses API) shape
  used by Codex: instructions + input items + flat tools -> rendered
  input_image parts; wire it into the proxy (was passthrough before).
- Tests: +12 (gate family, vision-token math incl. downscale path, chat +
  responses transforms). 366 passing; Anthropic path unchanged.

README intentionally not updated (GPT OCR fidelity unverified until tested).
2026-06-19 13:47:44 -04:00
teamchong ef9ddac58a feat(library): caller fidelity + recoverable channel, edge-safe; trim comments
- keepSharp: caller predicate pins exact-match-critical blocks (IDs, hashes,
  paths) as text, overriding the chars/token heuristic. Reported via
  info.keptSharpBlocks. Never forces imaging; throwing predicate = false.
- emitRecoverable: optional recovery map (block -> original text + provenance)
  so a stateful caller can re-inject or re-fetch imaged content. The recover
  half of the fidelity contract; documented mitigation for lossy recall.
- edge/Workers-safe: process.env read is typeof-guarded; @napi-rs/canvas moved
  to devDependencies (atlas is build-time only, runtime PNG encoder is pure-JS).
- comments: trimmed ~1,640 lines of essay/derivation/changelog narration across
  src/ (transform.ts -73%), keeping one-line rationale; deep math points to docs/.
- New exports/types in index.ts + library.ts; +12 tests (keep-sharp, recoverable).

Build clean; 354/354 tests pass.
2026-06-19 13:02:30 -04:00
teamchong 8eb035025a docs: lead messaging with token reduction, not dollar savings
Tokens are the durable invariant; the dollar figure is a pricing-dependent
consequence that evaporates if Anthropic reprices.

- README: headline + intro now lead with input-token reduction; the
  ~59-70% end-to-end bill is demoted to a downstream effect 'at current
  Fable list prices', with the dollar math pointed to the FAQ
- dashboard: stop claiming Claude's replies are 'never compressed' (false
  for any session past keepTail) — scope to latest messages + live output
2026-06-19 11:26:39 -04:00
teamchong cf4188625a docs: add HISTORY_CACHE_MODEL.md, conceptual companion to caching/savings
Captures why imaging conversation history stays cache-safe as a session
grows: the quantized collapseChunk boundary, the one-time cache-create
burn + amortization gate, the protectedPrefix slab anchor, and the
relocate-don't-add cache_control rule. Grounded in src/core/history.ts
and transform.ts; prose mirror of the verified code.
2026-06-19 11:26:39 -04:00
teamchong e5f5bc3d31 fix(node): exit cleanly on Ctrl+C during in-flight streams and idle keep-alive
server.close() waits for open connections to drain, so it never finishes while
the dashboard tab polls every 2s or while an SSE response is mid-stream - the
first Ctrl+C printed 'shutting down' but the process never exited.

Drop idle keep-alive sockets immediately, force-close anything still streaming
after a 1.5s grace, and make a second Ctrl+C an instant hard exit.
2026-06-19 09:34:35 -04:00
teamchong 13456812b1 redesign dashboard: light flame theme, plain-language hero, image/text transparency
Full rewrite of src/dashboard/fragments.ts (CSS + render functions + page
shell). Server JSON contracts, htmx endpoints, element IDs, and test-pinned
strings are all preserved.

- Light theme with flame-orange accent (no dark mode)
- One plain-language hero answer on top; all cc=1.25 + cr*0.10 formulas and
  counterfactual diagnostics moved behind a 'Show the math' drawer
- Image vs Text Breakdown panel: 'Became an image / Stayed as text' tabs plus
  an Image + Source inspector showing the verbatim text behind each page
- Recent table gains a color-coded 'Sent as: image/text' column
- Wide tables (#frag-recent, #frag-stats) and the latest-image inspector
  (#frag-latest) scroll inside their card instead of pushing the border out
2026-06-19 09:34:35 -04:00
teamchong de3f756f16 deps: patch 6 dependabot advisories (vite, undici, esbuild)
Bump dev/build tooling to patched versions and add pnpm overrides
for transitive deps. All advisories are Windows-specific or
dev-server issues; none touch the runtime proxy path.

- vite    8.0.13 -> ^8.0.16   (GHSA-fx2h-pf6j-xcff high, GHSA-v6wh-9g99-6wx3 moderate)
- esbuild 0.28.0 -> ^0.28.1   (GHSA-g7r4-m6w7-qqqr low)
- undici  override ^7.28.0    (GHSA-vmh5-mc38-953g high, GHSA-pr7r-676h-xcf6 moderate; via wrangler>miniflare)
- esbuild override ^0.28.1    (via wrangler>esbuild)

pnpm audit: No known vulnerabilities found.
typecheck clean, build ok, 342/342 tests pass.
2026-06-18 23:47:54 -04:00
teamchong 7af6c200c3 eval+demo: route harnesses through interactive cci.py; opus → claude-opus-4-8[1m]
eval:
- Add eval/lib/cci.py: drop-in interactive Claude session shim (pexpect/PTY)
  replacing headless `claude -p`. Drives the real TUI on Max-subscription auth
  (no ANTHROPIC_API_KEY), scrapes the reply + /context + /cost, and is
  argv/stdin/--output-format json compatible with the old call sites.
- Wire every call site to cci.py: gist-recall (run/run2/run3),
  needle-haystack (sweep/crux), gsm8k (bench), swe-bench (run_pilot),
  swe-bench-pro (rep_nav/run_pro), and eval/lib/anthropic-client.mjs.
- Isolation per call: --setting-sources project --strict-mcp-config
  --dangerously-skip-permissions. Recall evals keep --allowedTools Read;
  swe-bench keeps the full agentic toolset. Each trial is a fresh process.

demo:
- Resolve the `opus` shorthand to the full claude-opus-4-8[1m] model in
  cost-ab and effective-context a.sh/b.sh. setup.sh intentionally keeps the
  base id: the proxy scope gate strips [1m] from the request before matching,
  so [1m]-on-launch + base-in-scope is the correct pairing.

docs:
- FINDINGS.md: drop the 2026-06-16 Opus 4.8 re-test sections.
2026-06-18 23:43:37 -04:00
teamchong 74e2b97b1b docs: exact-recall escape hatch — route byte-exact subtasks to a non-Fable subagent 2026-06-18 21:34:03 -04:00
teamchong af1e838f0f chore: release 0.3.1 — Drive-hosted demo, keep preview thumbnail
Demo clip moved to Google Drive (committed copy was too low-res); README keeps the preview thumbnail linking to the Drive video. Restoring the thumbnail also un-breaks the demo image on the npm 0.3.0 page. Version 0.3.0 -> 0.3.1 + CHANGELOG.
2026-06-17 15:42:42 -04:00
teamchong 85aed799e5 docs(readme): drop blurry committed demo video, link to Drive instead
The recorded clip was too low-res to read; remove the 8.9MB mp4 + thumbnail from the tree and point the Demo section at the Drive copy instead.
2026-06-17 15:15:10 -04:00
teamchong a9ff92d3e1 docs(readme): add side-by-side A/B demo video (Opus 4.8)
Attach the recorded plain-vs-pxpipe demo (cost-ab fix + effective-context recall) plus a poster thumbnail, embedded in a new Demo section with an honest caption (Opus opt-in; demo 2 shows pxpipe surfacing the lossy-count limit rather than fabricating). Fable headline clip to follow when Fable is back.
2026-06-17 13:02:37 -04:00
teamchong fab35ed2d2 docs(readme): fix stale constants table + test count; note Opus opt-in
The library-use constants table still showed the pre-0.3.0 values (DENSE_CONTENT_CHARS_PER_IMAGE 5000, MAX_HEIGHT_PX 1568), contradicting the 'how it works' section and the shipped code. Updated to 1932 / 92160 + DENSE_CONTENT_COLS 384; test count 323->342; 'Fable-5 only' -> 'Fable-5 by default, Opus opt-in'.
2026-06-17 12:21:48 -04:00
teamchong d2d410ffc8 chore: scrub private data from repo; honest doc tweaks
- Remove eval/corpus/{sessions,text-blocks}.json from the repo — they are real captured transcripts (file paths, project names, message content). gitignore eval/corpus/; local copies kept for the eval scripts.

- gen_sweep.mjs: replace a hardcoded /Users/<name> absolute import with a relative path.

- README: 'holds ~92k readable chars' -> 'up to ~92k chars' (don't overclaim legibility at the new page density).

- CHANGELOG: note OCR legibility at the larger page size is not independently re-eval'd.
2026-06-17 12:08:16 -04:00
teamchong 716d9e0965 test: cap corpus L0 test to an 8MB per-file prefix
The real-corpus reflow check read whole transcript files; on a large ~/.claude/projects (multi-hundred-MB files) that blew the 30s timeout and aborted 'pnpm test' (hence prepublishOnly). Read only the first 8MB per file — enough for the per-file sample; a truncated trailing line just fails JSON.parse and is skipped. Corpus test now runs in ~5s.
2026-06-17 10:44:46 -04:00
teamchong dadee3b09e feat(demo): setup.sh takes a model arg; default Fable-only (Opus off)
setup.sh resolves a model arg (default fable) and sets PXPIPE_MODELS to Fable plus the chosen model, so the demo matches the production default (Opus OFF) and 'setup.sh opus' enables Opus end-to-end across setup/a.sh/b.sh. READMEs updated to pass the model to all three.
2026-06-17 10:07:32 -04:00
teamchong a64768bc4e chore: release 0.3.0
Version bump 0.2.1 -> 0.3.0 and CHANGELOG for the page-ceiling, dashboard
transparency, honest-savings, and code-review-fix work on this branch.
2026-06-17 10:01:33 -04:00
teamchong ad7ef7c1ac docs(demo): cost-ab + effective-context A/B demos with a model arg
Both demos default to Fable; pass a model as the first arg (a.sh opus, sonnet,
haiku, or a full claude-... id) to override -- run both columns on the same
model for a fair A/B. Adds eval/ab token-savings scripts.
2026-06-17 10:01:33 -04:00
teamchong 1cb62c1786 feat(dashboard): context-map transparency, flexible model chips, Opus default-off
- "How your context works" panel: per-request token flow (as-text -> real) plus
  the exact-char bucket breakdown of what became images and a gallery of every
  rendered page, opened via a "view" link on each recent-requests row.
- "compress models" chips are the union of a catalog (Fable 5, Opus 4.8/4.7,
  Sonnet 4.6, Haiku 4.5), the PXPIPE_MODELS env scope, and the active scope, so
  any env-enabled model stays toggleable (off <-> on); runtime-only override.
- Opus is OFF by default (Fable-5-only scope); opt-in via env or the chips.

Fixes from the code review:
- contextHistory cap aligned to the recent table (was 30 < 50, so older "view"
  links silently showed the latest request's breakdown); an evicted or
  unrecorded request now shows an explicit "no longer available" message.
- the session headline phrases a net loss as "N% more tokens" instead of
  rendering a garbled "-7% fewer tokens".
2026-06-17 10:01:33 -04:00
teamchong a9bda51210 feat(savings): honest negative accounting; drop the false >=0 clamp claim
Per-turn and per-session savings are the real baseline_eff - actual_eff with no
floor: a net-losing turn (cache_create-heavy image rewrite that costs more than
the text prefix it replaced) now reports the real loss instead of a fabricated
0. Updates baseline.ts and the caller comments that claimed a >=0 clamp the code
never applied -- the negative is intended and covered by tests/sessions.test.ts.
2026-06-17 10:01:33 -04:00
teamchong 8af9829872 feat(render): raise page ceiling to ~1932x1932 and align gate/paging geometry
Fable 5 / Opus 4.8 accept up to 2576px / 4784 visual tokens per image, but a
request with >20 images (pxpipe always sends many) is held to the stricter
<=2000px/side rule, so the real ceiling is ~1932x1932 (1928x1928 = 69x69 = 4761
tokens). MAX_HEIGHT_PX 1568->1932; dense tool/history pages now render at
DENSE_CONTENT_COLS=384 / DENSE_CONTENT_CHARS_PER_IMAGE=92160 (1928x1928 full
page) -- fewer image blocks at the same OCR-validated 5x8 cell. The static slab
is unchanged (313 cols / 1573x1280).

Also fixes a regression the code review caught: the break-even gate,
truncateForBudget, and estimateImageCount predicted against the slab geometry
(313 cols / 159 rows) while the dense renderer emits 384 cols / 240 rows, so
large tool_results were truncated far earlier than the 10-image cap required --
silently dropping output that would have rendered. A per-page char budget is
threaded through the prediction chain (defaults to READABLE so the slab and unit
tests are byte-identical) plus a denseGateGeometry helper, so the gate prices the
same page the renderer actually produces.
2026-06-17 10:01:33 -04:00
teamchong 514a4f0b74 feat(gate): config-driven model scope (PXPIPE_MODELS) + Opus 4.8 re-test
- applicability: isPxpipeSupportedModel reads PXPIPE_MODELS (comma-sep,
  default claude-fable-5 only) and strips bracket/variant tags like [1m];
  Opus becomes opt-in with no code change. Default behavior unchanged.
- FINDINGS: Opus 4.8 re-test - arithmetic 98/100 (-2pp), verbatim 6/15
  (was 93/100, 0/15): improved but still taxed -> default stays Fable-only.
- FINDINGS + eval/glyph-matrix/sweep: root cause of the read tax is per-glyph
  RESOLUTION, not font shape (confusions scale with px and vanish by 10x16).
  Opus needs ~4x glyph area to read reliably; the 1568px ceiling makes that
  break-even, so there is no profitable Opus operating point.
2026-06-16 12:22:55 -04:00
teamchong 50f3f1f86b docs: add prompt-caching alignment + honest savings-math doc
New docs/CACHING_AND_SAVINGS.md explains the two things that keep coming up:

1. How pxpipe stays aligned with Anthropic's prompt cache when it images the
   static prefix: split static/dynamic blocks first (keep drifting <env> etc.
   as cheap text, never imaged), then relocate (never add) the caller's
   cache_control onto the last image so the breakpoint anchors at the end of
   the stable slab. Covers the cache-friendly splice, the symmetric
   anti-flapping burn term, and the horizon-amortized history-collapse gate.

2. How savings are measured without double-counting the cache discount: both
   the real request and a count_tokens counterfactual of the original body are
   priced with identical cache rates at the same moment, so the discount
   cancels in the difference and only the token reduction survives.

Also reconcile the priorWarmTokens JSDoc in transform.ts: the burn term is
applied undivided, matching all three gate call sites and the symmetric
priorWarmImageTokens doc. The '/ N' was stale (the per-turn gate has no
horizon to divide by; the horizon scales the lifetime cost, not the burn).
2026-06-16 08:35:48 -04:00
teamchong be85f58d90 demo: pivot to smart-zone demo (plain Claude rots at large context, pxpipe stays sharp)
- generate.mjs builds one large context (filler logs + small state-tracking needle)
- README: two-column run (plain vs pxpipe), same prompt, compare answers + /context
- drop the e-commerce /context demo; context/ is generated, gitignored
2026-06-16 08:18:21 -04:00
teamchong 0277facc5d docs: reframe savings as workload-dependent snapshots; add demo + render-sizing doc
- README: cost savings now presented as measured snapshots (~59-70% end-to-end,
  ~72-74% compressed) bound to trace + workload, not a fixed 59%
- add demo/ (synthetic side-by-side /context demo: data, prompt, expected answer)
- add docs/RENDER_SIZING.md (image-sizing rules, rationale, commit history)
2026-06-15 22:33:31 -04:00
teamchong 7ee263c596 docs: drop glyph-matrix resume date (reader is fable-5, currently unavailable) 2026-06-14 17:27:35 -04:00