Changelog
Every release of Agentwright Starter. Current version: 0.4.2. License holders get every update at no extra cost.
All notable changes to Agentwright Starter. Format: Keep a Changelog, versions follow SemVer.
[0.4.2] — 2026-11-11
Added
- Claude Fable 5.2 prices (
claude-fable-5-2@2026-11-11: $8 / $40 per MTok, cache read $0.20, cache write $10), checked on the official pricing page on 2026-11-11. Before this,AGENT_MODEL=claude-fable-5-2failed withMISSING_PRICE. Existing installs: runnpm run db:seed, which inserts missing price rows and leaves existing ones unchanged. The Fable 5.1 row stays. docs/customizing.md: adding a tool, changing the system prompt (and what that does to the prompt cache), MCP servers, eval cases, plans, caps and rate limits, switching the model. Linked from the README anddocs/setup.md.docs/ROADMAP.md: what is planned and what is not in this kit.AGENTS.md(and aCLAUDE.mdthat imports it): commands and the rules a coding agent should follow in this codebase.- Tests: Fable 5.2 price row;
assertWithinCapwith a missing plan row or user; parallel requests against the rate limiter (DB test).
Changed
- The license text now matches the terms published on the site (LICENSE version 1.1): one license covers one person or one team (the same company, or the same client), every future version is included at no extra cost, and there is a 14-day refund with no reason needed. It also adds that the license key and download link must not be shared outside your team, that a client can receive the source of the app built for them, and that a refund or termination ends updates and support. The restriction on building a competing product is gone; the license only restricts passing on the Starter's source. This applies to existing licenses too. The README was updated to match.
- The default system prompt starts with "You are the assistant inside this app." and ends with a new line that asks for plain text without Markdown, because the chat UI shows text as written. The other lines are unchanged.
.env.example: removedLEMONSQUEEZY_API_KEYandLEMONSQUEEZY_STORE_ID, which no code reads, and an example checkout link.docs/next16.md: decision line reworded; the upgrade is listed in the roadmap./pricing: the one-time license card is generic: it showsLICENSE_PRODUCT_NAME(default "License") instead of selling this kit. The Pro and Team cards list their requests per minute instead of "Priority models" and "Shared workspace", which the kit does not have.- License delivery email: the product name defaults to "your license", the link is labelled "Download", and the getting-started line is replaced by the optional
LICENSE_EMAIL_FOOTER..env.examplesetsLICENSE_PRODUCT_NAME="My Product". - Migration
0001_week3_ratelimit_licensesis renamed0001_ratelimit_licenses. Its SQL and journal timestamp are unchanged, and Drizzle tracks applied migrations by hash and timestamp, not by name, so existing databases are not affected. .env.example: addedAUTH_TRUST_HOST(needed in production behind a proxy).
Fixed
assertWithinCapthrew aTypeErrorwhen the user or the user's plan row was missing. It now throws an error with codeNO_USERorNO_PLAN; theNO_PLANmessage says to runnpm run db:seed. The agent route logs these errors on the server and sends the browser a generic message with the code. The cap itself works as before; its comment now says what it counts: input + output tokens of the current UTC month, no cache tokens, checked before each model turn, so one turn can go past the cap.- Rate limit could be exceeded by parallel requests.
checkRateLimitcounted and inserted without a lock, so parallel requests from one user could all read the same count and all pass (12 of 12 with a limit of 5 in the new test). The count and insert now run in one transaction under a Postgres advisory lock per user and bucket. No migration needed. - A comment in
lib/billing/lemonsqueezy.tspointed todocs/billing.md, which does not exist; it now points todocs/setup.md, section 4. - Removed
docs/TODO.md(internal build notes). db:migrate,db:seedandevalnow read.env.locallike the app does (and.envas a fallback). Before this, the quick start's.env.localwas not read by these scripts.- Empty values in the env file switched off the defaults.
.env.exampleshipsAGENT_MODEL=,LICENSE_TOKEN_SECRET=,APP_URL=andRESEND_API_KEY=empty, and the code fell back to its default only when a variable was missing, not when it was empty. With the example file copied as is, every chat request failed withMISSING_PRICE, license delivery failed with "Set LICENSE_TOKEN_SECRET (or AUTH_SECRET)", delivery emails had links without a host, and an emptyRESEND_API_KEY=hid a configuredAUTH_RESEND_KEY. An empty value now falls back like a missing one. - A Team purchase did not change the user's plan. Only the Pro plan could be linked to a Lemon Squeezy variant, so the webhook ignored Team orders.
npm run db:seednow links the Team plan toLEMONSQUEEZY_VARIANT_TEAM(new in.env.example,docs/setup.mdanddocs/deploy.md). An empty variant value is now stored asNULL.
[0.4.1] — 2026-11-05
Fixed
- Agent requests failed with a 400 once MCP servers added three or more tools. Every tool definition carried its own
cache_controlmarker, and the API allows at most 4 cache breakpoints per request ("A maximum of 4 blocks with cache_control may be provided"). Tools no longer carry markers; the system-prompt breakpoint already caches tools + system, because tools render first. Regression test intests/tools.test.ts.
[0.4.0] — 2026-11-04
Added
- Price snapshots on every usage row. New
model_pricestable (versioned rates in integer micro-USD per MTok,effective_from,source, long-context tier viamin_prompt_tokens) andusage_events.price_id.recordUsagepicks the price in effect at request time, stores the cost and the price id, so historical totals never change when prices do and every charge can be explained. Price changes are new rows; rows dated in the future take effect on their date. Seedocs/pricing.md. /usageshows "Prices used this month" (price version, rates, calls, cost) and the price id on each recent call.- Claude Fable 5.1 prices ($10 / $50 per MTok, cache read $0.25, cache write $12.50) and the Haiku 5.5 long-context price for prompts over 100,000 tokens ($0.50 / $2.50).
npm test: unit tests for price selection and cost math, plus a Postgres integration test (TEST_DATABASE_URL). GitHub Actions runs typecheck, tests (with a Postgres service) andnext buildon pull requests.runAgentchecks that the model has a price before calling the API (MISSING_PRICE), so no request is made that could not be metered.npm run db:seedinserts missing price rows fromlib/billing/pricing.tsand warns when an existing row differs.- Migration
0002_price_snapshots(creates and seedsmodel_prices, adds nullableusage_events.price_id; rows recorded before 0.4.0 keepNULL). lib/billing/checkout.ts:licensecheckout (NEXT_PUBLIC_LS_CHECKOUT_LICENSE) for the one-time Starter kit purchase;/pricingshows a "Buy the Starter kit" card when it is set.
Changed
lib/billing/pricing.tsis now a versioned catalog (BUILTIN_PRICES,selectPrice,costFromPrice).costMicroUsd(model, usage)andPRICE_PER_MTOKstill work for existing code.- The eval runner's
--jsonreport lists the price ids its costs were computed with.
Fixed
docs/setup.mdpointed to the wrong file for the price table.lib/billing/pricing.ts: Claude Sonnet 5.5 cache-read price corrected to $0.10/MTok (0.05x base input, per the official pricing page on 2026-11-01); it was $0.20.
[0.3.0] — 2026-11-01
Added
- Rate limiting (
lib/ratelimit/index.ts): database-backed sliding window per user, limit fromplans.requests_per_minute(seeded 10 / 60 / 200 for free / pro / team)./api/agentanswers429withRetry-AfterandX-RateLimit-*headers; the chat UI shows the wait time. Old hits are pruned on each check, no cron needed. - License key delivery (
lib/licensing/index.ts,docs/licensing.md): Lemon Squeezyorder_createdforLEMONSQUEEZY_VARIANT_LICENSEissues anAW-XXXX-…key (idempotent per order), invites the buyer's GitHub login toLICENSE_GITHUB_REPOwhen configured, and emails key + signed download link.order_refundedrevokes the key. Emails go through Resend (RESEND_API_KEY) or are logged. /api/download?token=…: HMAC-signed, revocation-aware redirect toLICENSE_DOWNLOAD_URL, counts downloads./admin/licenses(+/api/admin/licenses): list/search licenses with delivery status, resend, revoke/reactivate, issue a key by hand. Access viaADMIN_EMAILS(lib/auth/admin.ts).lib/email/index.ts: minimal Resend sender with a logging fallback.- Migration
0001_ratelimit_licenses(tablesrate_limit_hits,licenses; columnplans.requests_per_minute).
Changed
- Webhook schema accepts
user_name,order_numberandcustom_data.github. scripts/seed.tsupsertsrequests_per_minuteas well.
[0.2.0] — 2026-11-01
Added
- MCP tool sources (
lib/agent/mcp.ts): connect stdio or Streamable HTTP MCP servers viaMCP_SERVERS; their tools are registered as<server>__<tool>and callable by the agent like local tools. Per-serverallowToolsfilter, graceful failure per server. - Eval runner (
evals/run.ts,evals/cases.ts): runs golden cases through the real agent loop without a database, checks tool calls / text / turn count, prints cost per case, exits non-zero on failure.npm run eval -- --model … --only … --json …. - Usage dashboard (
/usage,lib/billing/usage.ts): monthly tokens and cost against the plan cap, per-model split, 30-day chart, last 20 calls. Link from the chat header. - Magic-link sign-in via Resend: enabled automatically when
AUTH_RESEND_KEYis set;/loginshows the email form only then. - Pricing page (
/pricing,lib/billing/checkout.ts): Free / Pro / Team cards; Pro and Team buttons become Lemon Squeezy checkout links (withcheckout[custom][user_id]for webhook matching) onceNEXT_PUBLIC_LS_CHECKOUT_*are set. lib/billing/pricing.ts: pure pricing table andcostMicroUsd/formatUsd, usable without a database.docs/deploy.md(Vercel + Neon, other hosts) anddocs/next16.md(upgrade evaluation — deferred).defineRawTool,listTools,removeToolsFromSourcein the tool registry.
Changed
- Pricing table corrected to current list prices (Haiku 5.5 $0.10/$0.50, Sonnet 5.5 $2/$10, Opus 5.5 $4/$20 per MTok).
runAgentno longer requiresuserId; without it, metering is skipped (used by evals and smoke tests). Metering is imported lazily so the loop works withoutDATABASE_URL..env.exampledocuments the new optional variables.
[0.1.0] — 2026-11-01
Initial release: Next.js 15 app, Drizzle + Postgres schema and migrations, Auth.js v5 GitHub OAuth, streaming agent loop with parallel tool calls and prompt caching, SSE agent route + chat UI, per-user token metering with plan caps, Lemon Squeezy webhook with signature verification, commercial LICENSE.