Two BREAKING changes — the
crewaiextra is removed and the license tier ladder drops its two top rungs — alongside a large pass over what the CLI reports. Surfaces that printed constructed numbers now print a measurement or say none was taken.On the version number. Eleven public symbols are removed, which by strict SemVer calls for a major. This is shipped as a minor on an explicit project ruling, and is recorded here rather than left for you to discover: if you depend on
tokenpakwith>=1.15,<2.0or without a pin, read the upgrade notes before taking this one.Measured against 1.15.0 as published, by the project's own snapshot gate (
scripts/release_gate/api_snapshot_diff.py), the removals are:tokenpak.licensing.TIER_TEAM tokenpak.licensing.TIER_ENTERPRISE tokenpak.cli.commands.license_cmd.DEFAULT_UPGRADE_URL tokenpak.cli.commands.status.DEFAULT_UPGRADE_URL tokenpak.cli.commands.preview.cmd_preview # module removed tokenpak.cli.commands.preview.register_preview # module removed tokenpak.companion.codex.launcher.FallbackDecision tokenpak.companion.codex.launcher.PreflightEvaluation tokenpak.companion.codex.launcher.PreflightEvidence tokenpak.companion.codex.launcher.PreflightStatus tokenpak.companion.codex.launcher.TemporarySessionChoiceOnly the first two are discussed below as breaking; the rest are internal surfaces the snapshot gate counts as public, and they are listed so the number can be checked rather than taken on trust.
tokenpak previewitself is unaffected — the command works and is on the supported surface; what moved is the module that registered it.
Changed
- BREAKING — the tier ladder is now
free < pro. The two tiers above Pro are retired and Pro introduces every feature they used to:tokenpak_server,seat_management,team_analytics,audit_logandslaall resolve topro. This supersedes the ladder described under 1.15.0 below.
No license above Pro could be issued, so every feature gated above Pro was
unreachable by anyone holding a real license — including tokenpak_server,
which gates the daemon that the paid tier is defined by. A valid Pro licensee
could not run it.
required_tier_for returns "pro" for all of the above. The two retired
members are gone from the LicenseTier enum, so code that names one, or that
assumes four rungs, no longer imports; the ladder is available from
LicenseTier.ladder and the tier names from
tokenpak.licensing.known_tiers rather than being hardcoded.
The per-key rate-limit table in the (unshipped) intelligence server collapses to
free: 20/min,pro: 100/min.TokenPak advertises only the surface it has verified. The parser exposed 90 commands; the registry documented 56, and nothing reconciled the two — so 34 verbs were reachable but undocumented, and some documented verbs had never been run end to end. An explicit beta allowlist (
tokenpak/core/registry/beta_surface.json) now names the supported set and requires a written reason for every exclusion, paired against the live parser by a test so the surface cannot grow or shrink silently.
Excluded is not removed. Every excluded verb still parses and still runs.
What it loses is a place in default discovery: tokenpak help --all lists it
under its own heading with the reason attached, so a reader can tell "this is
ready" from "this is reachable". Two verbs were being advertised while broken —
watch, listed as a "live terminal savings dashboard" while its own help says
it is not implemented, and last, a stub — and both now say so.
tokenpak --help is default discovery too, and it was a separate
hand-maintained list that no test paired against the allowlist. It went on
advertising five excluded verbs, last among them, described as "Show details
of last compressed request" while the command printed a first-run welcome
banner. It now lists only supported commands, and a test holds it there.
fingerprint, optimize, prune and template work and are still reachable;
they lost the listing, not the implementation.
tokenpak statusleads with runtime, routing, and context operation; savings follow. Before "how much did this save me" comes "is it running, is my client actually routed through it, and is it operating on my context". The routing line is new: a running proxy with nothing pointed at it produces no savings and no data, and that state was previously indistinguishable from "working, but idle".One lifecycle observer across
setup,start,stop,restart,logs,doctorand the menu. Each of these previously decided for itself what "running" meant, and they did not agree — after a successfultokenpak setup,tokenpak stopanswered "No proxy PID file found. Is the proxy running?" about a proxy it had just started.startnow verifies the child before recording its PID and returns documented exit codes;stopdistinguishes no PID on record, a stale PID it cleared, and a process it signalled; a proxy started by something else is reported as a warning rather than a green "running", becausestopandrestartwill not act on it.config.yamlis canonical;config.jsonis read-compatibility only, anddoctorresolves through the shared resolver — so completing setup no longer leaves it reporting "no config" and sending you back to the wizard. An unreadable config is now distinguished from an absent one.setupis scriptable (--profile,--port,--yes) and honoursTOKENPAK_PORT. EOF is no longer consent: piping/dev/nullpreviously selected a profile, wrote config, and spawned a daemon with nothing answered.Public output describes two editions, TokenPak and TokenPak Pro. The internal entitlement taxonomy still decides what a license unlocks, but tiers above Pro are no longer presented as plans,
planno longer prints a price column against rows with no purchase path, andfeaturesspeaks in editions.Package maturity drops from Production/Stable to Alpha. It claimed Production/Stable while
previewprinted simulated numbers andsetupreported success for a proxy that had not started.TIP expands to "TokenPak Integrity Protocol", not "Integration Protocol." The prior expansion invited a transport reading that the specification and the product constitution both explicitly deny. The acronym, the wire contract, the schemas and the version are unchanged — TIP-1.0 remains TIP-1.0. Prose only.
describe_tierreturns edition names, not tier names. It now answers "TokenPak" / "TokenPak Pro" where it previously answered "Free" / "Pro", because every caller was rendering it to a user. The tier name is still available frominternal_tier_labelfor diagnostic surfaces that genuinely need it. The signature is unchanged; only the strings differ.
Added
tokenpak.licensing.known_tiers— tier names in ascending capability order, so callers rendering a tier list do not hardcode one.
Fixed
previewreported numbers nothing had measured. It computedoutput = len(text.split) * 0.65and printed four fixed block names unrelated to the input; JSON input with no whitespace reported -900% savings with negative block counts. It now runs the real compression pipeline and enforces a result contract — non-negative counts,saved == input - output, ratio in [0,1], measured duration, pipeline-assigned block identities, andapplied=falsewhen compression would expand the input. Provenance is mandatory: input digest, byte length, source, tokenizer id, stages run, version.
preview also accepts conversation input (message array, provider request
body, or JSONL), because savings come from redundancy across turns. A
single-turn preview reports 0% and says why, rather than implying the product
does not work.
statsdisplayed a 1% measured saving as "99.4% token reduction." It reported(1 - ratio) * 100while the proxy recordsratioassaved/input— already a savings fraction. It also reported a hardcoded proxy port rather than this install's.Bare
tokenpakprinted a hardcoded "5.6% compression" regardless of the data, and attributed 95% of all savings to whichever model sorted first. Both are gone: compression is reported from the measurement or not at all, and the model line reports usage, which is what it can observe.A session with zero requests reported "$0.00 saved." Nothing was measured, so there is nothing to report; it now says that. Removed the ~5%/~30%/~40% savings claims from the profile menu — savings are reported after measurement, not promised at the moment of choice.
Auto-start had been inert.
setupspawnedruntime/proxy.py, a four-line re-export with no__main__block, so the child exited 0 without serving — andsetupprinted a checkmark on both branches because it probed the port rather than the child, wrote the PID before any check, and never calledpoll. It now requires a live PID plus a healthy endpoint before claiming success, and captures startup output for diagnosis./healthreportspidso ownership can be verified rather than inferred from "something answered".No new install used the canonical home. The first-run marker was bound at import time to
~/.tokenpak/.seen_intro, so any verb — includingversion— created the legacy directory before anything else ran, pinning resolution to it for the life of the install. Path semantics are now split: reads resolve compatibility-first, writes never target the legacy directory, andresolve_existingsearches both.Importing the proxy config created a directory as a side effect.
proxy/config.MONITOR_DBresolved at import in write mode, so importing the module created<config-dir>— which, on a machine configured in the legacy home, flipped config resolution to a directory containing no config. Resolution now happens on first use, here and in the compression dictionary, instruction table, fingerprint cache, event log, debug log, goals, and vault config.The profile you chose was never loaded.
core/config_loader.CONFIG_PATHwas a module-level constant bound to~/.tokenpak/config.yamlwhilesetupwrites<config-dir>/config.yaml, so the loader read a file that did not exist and returned an empty config — andTOKENPAK_HOMEhad no effect on the proxy at all.The home directory's 0700 guarantee did not hold. The first-run marker created it at the process umask (0775) and the previous implementation deliberately never re-chmoded.
ensure_homenow targets the write home and repairs group/world bits; user config is written 0600 and the license file is written owner-only.Reading telemetry no longer creates state or reads another install's.
statusreported companion savings from a hardcoded legacy path, so an install usingTOKENPAK_HOMEor the canonical home showed$0.00prompt-side no matter what the companion had saved. It now resolves across homes and returns "unavailable" rather than zero when no journal exists anywhere.tokenpak upgradeopened a URL that returns HTTP 404. It was in top-level help, beginner help, the command registry, and a footer printed on everytokenpak statusrun — so the most-displayed call to action in the product was a dead link. The verb is now a hidden compatibility shim that opens nothing and reports that public Pro enrollment is unavailable; there is no default URL, and--print-urlexits non-zero rather than printing a fabricated destination.Every verb the parser accepts is now runnable.
cli/commands/preview.pyimportedCompressorfromcompression.core, which does not exist, so it would have raisedImportError; it is deleted rather than wired.packagingwas imported unguarded at two undeclared callsites, which crashedupdatewith a bare traceback and silently degradeddoctor's update check.Companion wrappers answered the wrong question.
tokenpak claude --helpprinted Claude Code's help, not TokenPak's;tokenpak codex --helpdid the same and provisioned 23 files for a client that may not be installed. Launching with the client absent provisioned 21 files and failed with exit 120 — a code TokenPak never chose and documented nowhere. A preflight now names the missing prerequisite and exits 4, andcli/exit_codes.pydefines every code TokenPak assigns.doctoremitted 27 raw escape sequences into every piped run, including the paste-your-output bug-report flow. Its colour output now routes through the shared formatter, which honoursNO_COLOR,TOKENPAK_NO_COLOR, andisatty. Two remediation hints that could not work are corrected:doctorpointed atsetupfor routing (setup routes no client) and at a vault subcommand that does not exist.
Removed
- BREAKING — the
crewaioptional extra.pip install tokenpak[crewai]no longer installs crewai.
It still succeeds, which is the part worth knowing. pip treats an extra a package does not
declare as a warning, not an error: the command exits 0 and installs TokenPak without crewai.
So the failure does not appear at install time where you would see it — it appears later as an
ImportError from your own code. If you rely on that extra to pull crewai in, install it
yourself.
It was the only path by which chromadb entered the dependency graph, and every published
chromadb 1.x is covered by CVE-2026-45829 with no fixed release available — crewai pins
chromadb~=1.1.0, so there was no version to move to and the constraint was not ours to relax.
Carrying an unfixable critical advisory for an integration that is not a focus was the wrong
trade, so the extra is gone rather than documented around.
No capability is lost. The CrewAI adapter under tokenpak/sdk/crewai/ never imported crewai —
it is a set of context and handoff wrappers — and it continues to work unchanged. If you use it,
install crewai yourself alongside TokenPak:
pip install tokenpak crewai
Read this before following that line. Installing crewai yourself brings chromadb~=1.1.0 and
therefore CVE-2026-45829, exactly as the extra did. The advisory left TokenPak's dependency graph;
it did not stop existing for anyone who takes this path. See SECURITY.md for what that does and
does not mean. We would rather say this plainly than let the removal read as a fix it is not.
This also removes the json-repair advisory (GHSA-xf7x-x43h-rpqh), which reached the project only
through the same path, and drops the resolved dependency set from 256 packages to 196 — including
uvicorn's optional speedups (httptools, uvloop, watchfiles), which chromadb was the sole
requester of. Nothing imports them and installs from PyPI never had them.
On the absence of a deprecation window. This project normally requires notice before removing a published surface. Removal here was directed by the project owner on the record, on the grounds that the integration is not a focus and carrying an unfixable critical advisory for it is not a trade worth making. That ruling is what authorises the compressed timeline; it is recorded here rather than left implicit.
Upgrade notes
pip install --upgrade tokenpak. No data migration is required and no stored
state is rewritten on upgrade.
Breaking changes. Two, both listed above:
LicenseTier.TEAMandLicenseTier.ENTERPRISEno longer exist. Code that names either, or that assumes the ladder has four rungs, will not import. Read the ladder fromLicenseTier.ladderand the names fromtokenpak.licensing.known_tiersinstead of hardcoding them. No entitlement is lost: every feature those tiers introduced now resolves topro.pip install tokenpak[crewai]no longer resolves. The CrewAI adapter undertokenpak/sdk/crewai/is unaffected and continues to work — it never imported crewai. If you use it, install crewai yourself, and read the security note above and inSECURITY.mdbefore you do.
Migration — where TokenPak keeps its files. This release repairs path
resolution that was binding at import time, and the practical effect is that a
new install now uses the canonical home (<config-dir>) where previously any command
— including tokenpak version — created the legacy ~/.tokenpak first and
pinned resolution to it.
Existing installations are not moved and do not need to act. Reads resolve
compatibility-first and search both locations, so an install that already lives
in ~/.tokenpak keeps working, keeps its journals readable, and keeps its
configuration. Writes go to the canonical home. If you want a single location,
tokenpak home migrate moves it explicitly. Directory permissions are repaired
in place on first use: the home becomes 0700 and configuration 0600, which
the previous implementation created at the process umask and then deliberately
never corrected.
Behaviour you may notice, and should. Several surfaces that printed numbers
now print less. preview, stats, status, diff and bare tokenpak report a
measurement or state that none was taken; they no longer fall back to a
constructed figure. A session with no requests reports that nothing was measured
rather than $0.00 saved. start, stop and setup return documented exit
codes where they previously returned None on every path, so a script doing
tokenpak start && tokenpak status will now see a failed boot instead of
succeeding through it. If you have automation that greps for the old strings or
relies on exit code 0 from a failed start, it needs a look.
tokenpak upgrade no longer opens a URL. Public Pro enrollment is not open, and
the address it used returns HTTP 404; the verb remains as a shim that says so.
Rollback: pip install tokenpak==1.15.0. Configuration and stored telemetry
written by 1.16.0 remain readable by 1.15.0 — the format is unchanged and only
resolution order and permissions differ. An install that 1.16.0 moved to the
canonical home is still found by 1.15.0's own compatibility search; if you would
rather be explicit, set TOKENPAK_HOME.
Known issues. Package maturity is declared Alpha in this release, down from
Production/Stable. That is a correction, not a regression: the previous claim was
made while preview printed simulated numbers and setup reported success for a
proxy that had not started. Both are fixed here; the classifier moves back up when
a candidate passes the beta gates end to end.
Deprecations. config.json is read-compatibility only — config.yaml is
canonical and is what setup writes. The legacy home ~/.tokenpak remains
readable and is not scheduled for removal in this release.