Repository navigation
Releases: rocketflare-dev/rocketflare
Release list
0.24.0
A renamed copy now carries none of the kit's own machinery: its maintainer scripts, hooks, project files and kit-only tests are removed at the rename, and the example plugin's agent tool and welcome note follow its flag.
kitOnlynow also listsscripts/release.mjs, the two maintainer nudges andlib/nudge-lib,scripts/default-plugins.mjs,scripts/install.sh,CONTRIBUTING.mdandCODE_OF_CONDUCT.md; the rename deletes them (CONCEPTS §13).- The rename also drops the
kit:releasescript and the two nudge hooks from.claude/settings.json, writes the app its ownSECURITY.md, and removesretiredSurfacesfrom.rocketflare.json. - Kit-only tests moved out of
tests/config/:kit-check,kit-check-parsers,launch-kit-manifest(nowkit-only/launch-kit-schema),gate-string, the release helpers' blocks ofupgrade-lib/manifest-lib, and the manifest's "classifies every tracked file" check — an app's own new paths no longer fail its gate. tests/config/kit-manifest.test.tsnow also fails a copy whose package.json scripts or Claude Code hooks call a kit-only file.ToolCtx(the plugin tool context) gainsfeatures();example-feature'slist_example_notestool is offered only while the organisation has the flag, and itsonTenantCreatednote only when the deployment ships it.- The example agents are removable without test edits: core tests run on two fixture agents registered through mocked plugin barrels (
apps/web/tests/helpers/fixture-agents.ts),tests/api-setup.tsimports the DB client lazily so a test'svi.mockof a barrel takes effect, and each example surface'spathsnow lists its own output view, eval suite, dataset, baseline and UI tests (triage-researchrequiresresearch-topic). apps/web/src/api/services/ai/kit.tsis nowagent-loop.ts(same exports;tests/api/ai-kit.test.ts→agent-loop.test.ts), and source comments no longer carry design-decision numbers, "Ported from…" provenance or phase history — an app reads the rule, not the kit's past.- The docs say which rules are the kit repository's alone: porting notes and
pnpm kit:release(CLAUDE.md, code-quality rules, SETUP §3.7, DEPLOY's version rule). An app releases by bumping its root version and tagging. - Learned from apps built on the kit:
docs/upgrades/unreleased.mdiskitOnlytoo (copies kept the kit's pending notes and went on writing their own); the rename now reports the licence and a missingkit.commitas careful rows; package descriptions no longer call the app a kit; andupgrade-lib.test.ts's ancestry case builds its own two-commit repository instead of readingHEAD~1, which a one-commit copy or a shallow CI checkout lacks.
See the porting note — it lists what an existing copy deletes by hand (kit:upgrade never deletes files).
Full changes: 0.23.0...0.24.0
0.23.0
Plugins can start agent runs from jobs, crons and inbound email with an idempotency key, agents and tools get storage(), converted text with NUL bytes now indexes, and unlabelled plugin nav groups share one block.
ctx.startAgentRun({ agentKey, tenantId, input, idempotencyKey?, concurrencyKey?, triggeredBy? })→{ runId, status, deduplicated }onJobCtx,CronCtx,EmailCtxandPublicCtx(BackgroundMethods,apps/web/src/plugins/api/agent-runs.ts), through the route's ownenqueueRun(CONCEPTS §16).enqueueRun({ idempotencyKey }): the run id is derived from (tenant, agent, key) byidempotentRunId, so a repeat answers the first run, settled or not; no migration (CONCEPTS §9).@testkit/unit:makeJobCtx,makeCronCtxandmakeEmailCtxreturnFake{Job,Cron,Email}CtxwithstartedRuns({ input, result?, error? }[]) and accept astartAgentRunreplacement.AgentCtxandToolCtxgainstorage(), the sameFILESservice as every other context (filesStorageinapps/web/src/plugins/api/storage.ts).stripControlChars/stripControlCharsDeep(apps/web/src/api/utils/core/text.ts) remove NUL and C0 controls except tab, LF and CR beforedocuments.content,chunks.textand everyai_spansstring are written — Postgres refused them (22021 / 22P05).composeNavfolds an unlabelled plugin nav group into an unlabelled group right before it, so several one-page plugins read as one list.docs/upgrades/0.22.0.mdno longer declaresrequires_surfaces: [feature-agents], which wrongly skipped the email slot for apps without the agent runtime;docs/plugin-api.mdnow expandsBackgroundMethods.
See the porting note.
0.22.0
Agent tool calls now run concurrently under a configurable cap, and plugins can receive inbound email through a new emailHandlers slot and the Worker's email export.
meta.policy(agentPolicySchema) gainsmaxConcurrentToolCalls(default 4; cap 1 is sequential), executed concurrently byrunToolLoopwith events, transcript results and error handling preserved in model order (issue #4).ServerPlugin.emailHandlersroutes Cloudflare Email Routing mail to a plugin by recipient local part (*catch-all) viaapps/web/src/api/email.ts; plugins adapt the context withemailCtxfrom@/plugins/api(CONCEPTS §16).JobCtx,CronCtx,PublicCtxandEmailCtxgainstorage()(theFILESbucket, asRequestCtx.storage()),@testkit/unitgainsmakeEmailCtx, and@/plugins/apiexports theStorageServicetype.
Thanks to @NANDANS26 (#79, concurrent tool calls).
See the porting note.
0.21.0
Agents can run many at once, in batches and fan-outs, with a usage row per call, budgets and tool-loop policies; plugin agents can ask questions; and pnpm web agent:replay re-runs past runs with today's code.
agent_runs.concurrency_keyis the exclusive slot a run holds:''for an exclusive agent (one per organisation),enqueueRun({ concurrencyKey })for one active run per key, NULL forexclusive: false(no slot, neverdeduplicated). The unique index is(tenant_id, agent_key, concurrency_key)over active rows with a key;findActiveRuntakes the key (default'').triage-researchis nowexclusive: false. From PR #77.- The plugin
AgentCtx(@/plugins/api) gainsinterrupt(ask)andchatModel { provider, model };AgentInterruptAsk/AgentInterruptAnswerare exported.PROMPT_REGISTRYis typed so a plugin's prompt key indexes it.agent-runs,ai-promptsandagent-modelstests compare only the kit's own agents and prompts. - Replay (D38):
replay.ts(replayRun,listReplayableRuns,diffJson,ReplayMissError),scripts/agent-replay.ts+ theagent:replayscript (--candidate,--live),ctx.once(key, fn, { replay: 'rerun' | 'inner' }),decideWithShapes/toDecisionexported fromdecisions.ts, the skillrf-agent-replay, andPROCESS_TEST_TIMEOUTon theconfig/kit-onlyvitest projects. enqueueRun(db, cfg, env, input)takes the config and enforcesAGENT_MAX_ACTIVE_RUNS_PER_TENANT(default 50, 429agent_runs_limit) andAGENT_TENANT_DAILY_COST_USD(429ai_budget_exceeded).POST /api/agents/runs/batch(enqueueRunBatch,createAgentRunBatch{Request,Response}Schema,AGENT_RUN_BATCH_MAX= 100): one non-exclusive agent (400agent_exclusiveotherwise), per-input errors, onecreateBatch()with a per-instance fallback;GET /api/agents/runs?batchId=.ctx.scope(prefix)andctx.map(key, items, fn, { concurrency, keyOf })(services/agents/fanout.ts).- The ledger is one
ai_usagerow PER CALL carryingagent_run_id, written as each call returns by the run's meter (services/agents/budget.ts) for every call throughctx.chat.clientandctx.decide, so a run cancelled mid-loop keeps every turn it paid for (issue #2, from PR #74). The example agents no longer callrecordUsage;ctx.recordUsage(call)is for a client an agent builds itself;GET /api/agents/runs/:idreturnsusage. - Budgets:
meta.budget { maxTokens, maxCostUsd }overAGENT_RUN_MAX_TOKENS/AGENT_RUN_MAX_COST_USD, checked before each metered call across attempts; over →failed, event codeagent_budget_exceeded, not retried. - Policies (issue #1, from PR #73):
meta.policy { maxTurns, maxToolCalls, maxDurationSeconds, repeatedFailureLimit, onExhaustion }, passed torunToolLoopaspolicy, counted across attempts (ToolLoopCheckpoint.activeMscarries active time).'final-answer'(default) forces one call to the terminal tool (salvaged: true);'fail'throwsToolLoopExhaustedError→failed, event codeagent_policy_exhausted. No policy, no change.research-topic's hand-written salvage is now its policy;GET /api/agentsitems carrypolicy, shown on the Agents page. runToolLoop'sauthorizeTool(call)refuses a tool call by rule before it is gated or run (issue #3, from PR #75).- CLI:
rocketflare agents list,agents run <key> --input x.json | --batch x.jsonl,agents runs list [--batch id]. Rationale and known gaps:docs/CONCEPTS.md§9 "Agents", "Replay (D38)", "Volume (D39)" and "Policy (D39)".
Thanks to @benfrostmosaic (#77, the concurrency slots this is built on) and @NANDANS26 (#73, #74, #75, rolled into #78).
Breaking: enqueueRun now takes cfg (enqueueRun(db, cfg, env, …)), and an agent run's usage is several ai_usage rows (one per call, grouped by agent_run_id) rather than one summed row — delete any recordUsage / onUsage ledger write in your own agents, or it double-counts. See the porting note.
Porting note: docs/upgrades/0.21.0.md — pnpm kit:upgrade or /rf-upgrade in a copy.
Full changelog: 0.20.0...0.21.0
0.20.0
The AI adapters now follow each model's own rules, so Claude 5.x and GPT-6 models work. The openai provider moves to the Responses API, the defaults become claude-haiku-5-5 and gpt-6-luna, and stale prices are corrected.
- New
@rocketflare/shared/ai/models:modelTraits(provider, model)gives the thinking mode, what "off" sends, and whether the model takes atemperatureor a forcedtool_choice. The adapters,POST /api/ai/configand the settings form all read it. - Anthropic thinking per model:
- Claude 4.5 and earlier keep
enabled+ budget. - 4.6+ send
adaptive, and a stored budget is ignored. - "Off" sends
disabled,between_tools(Sonnet 5.5), or no field on Opus 5.5 and Fable, which always think. - Before this, a 5.5 model answered every call with a 400.
- Claude 4.5 and earlier keep
- Anthropic: a
temperatureis dropped where the model refuses one (4.7+, or any model while it thinks). A forced tool on Opus/Sonnet 5.5 or Fable 5.1 becomes a prompt instruction plustool_choice: auto.max_tokensis floored atADAPTIVE_THINKING_MIN_MAX_TOKENS(4096) while a model thinks. - First-party
openainow calls/v1/responseswithstore: false, because Chat Completions cannot call tools on GPT-6 Astra or 6.1 Sol, or on Luna while it reasons.openai_compatiblestays on Chat Completions. openaiconfigs now have a thinking switch. Off sends the lowestreasoning.effortthe model takes (none, orlowon Astra / 6.1 Sol / GPT-5.0–5.5 / o-series). On sends the model's default.- Reasoning blocks:
- Reasoning (Claude thinking blocks, OpenAI reasoning items) rides a tool loop as an opaque
ContentBlockoftype: 'reasoning'and is sent back unmodified to the provider that produced it. - Checkpoints keep it. Chat history never stores it.
- Reasoning (Claude thinking blocks, OpenAI reasoning items) rides a tool loop as an opaque
- Each adapter recovers once from a refusal the traits misjudged, then remembers it:
- a refused thinking value, sampling value, forced tool or effort;
- a replayed reasoning block whose signature is refused, which is stripped for that one call.
- Defaults and suggested models:
DEFAULT_MODELS.anthropicisclaude-haiku-5-5, which also means the platformANTHROPIC_API_KEYtier now answers with it.- The
openaichat default isgpt-6-luna. - Suggestions are Haiku/Sonnet/Opus 5.5 and Fable 5.1, and Luna / 5.6 Luna / 6.1 Sol / Astra.
claude-opus-4-1(retired on the Claude API) is gone from the list.
- Pricing (
PRICES_UPDATED2026-10-13):- Opus 4.5–4.8 dropped from $15/$75 to $5/$25. They had been priced 3× too high.
- Every Claude 5.x model, GPT-6, GPT-5.x and their
-provariants now have their own entries. - Haiku 5.5 uses its ≤ 100k-token rate.
- Why:
docs/CONCEPTS.md§9, "The request body is per MODEL".
Breaking: the platform ANTHROPIC_API_KEY tier now answers with claude-haiku-5-5, and an openai config whose base URL is a proxy without /v1/responses must become openai_compatible — see the porting note.
Porting note: docs/upgrades/0.20.0.md — pnpm kit:upgrade or /rf-upgrade in a copy.
Full changelog: 0.19.0...0.20.0
0.19.0
Agents can ask typed questions through ctx.decide (D37) — answered with probabilities by TypeSafe's Jev, a new decisions AI provider scope, or by the chat model — recorded per run, scored by evals and shown on the run page.
ctx.decide(key, { state, questions }),ctx.decide.sift,ctx.decide.pickonAgentContextand the pluginAgentCtx; recorded once per(run, key)inagent_run_decisionsand replayed on a retry.- New AI scope
decisionsand providertypesafe(Settings → AI, with a Test button); platform secretTYPESAFE_API_KEY; without either, the chat model answers on prompt keyagent-decide, recordedllm_self_report. resolveDecisionsinservices/ai/resolve.ts; readiness gainsdecisionsand the sourcefallback.- New run event type
decision, projected to AG-UI askit.agent.decision;GET /api/agents/runs/:idreturnsdecisions[]; the run timeline shows a Decided row. ctx.prompt(vars, promptKey?)may resolve another registry prompt.- New example agent
triage-research(screen → triage → answer, "not covered", or escalate toresearch-topic) with an eval suite. - Evals:
expected.decisionsonEvalCase,DecisionJudge,decisionScore(accuracy + Brier). - Retrieval: the lexical half of
searchChunksmatches ANY query word (ranked byts_rank_cd) instead of ALL of them, so a question typed as a sentence finds passages by its key terms. - Fix: the Run modal's schema-generated form (any agent with no hand-written form) remounted its fields on every keystroke, losing focus;
RunAgentModalnow builds the form once. - Why:
docs/CONCEPTS.md§9, "Decisions (D37)". scripts/lib/kit-manifest.mjs→ the kit contract's optionallaunchProvides.oidc.callbackPath(default/auth/oidc/callback) andsignedOutPath(default/login?signedOut=1) must each be a path on the app's origin: one leading/, no scheme, host, whitespace or#(launch-kit 0.1.4).
Porting note: docs/upgrades/0.19.0.md — pnpm kit:upgrade or /rf-upgrade in a copy.
Full changelog: 0.18.1...0.19.0
0.18.1
What changed
scripts/kit-check.mjs accepts a copy Launch has provisioned, and no config test judges the checkout it runs in, so a copy whose tomls carry Launch's real KV ids passes its own Gate.
scripts/kit-check.mjs→ in the kit every KV id is a<PLACEHOLDER>; in a copy an id may be a placeholder or real;--provisionedstill wants real ids (docs/CONCEPTS.md§13).apps/web/tests/config/kit-check.test.ts→ every case on a fixture kit, fresh and provisioned the way Launch'swrite_configdoes it (tests/config/fixtures/kit-fixture.mjs, shared with the meta-kit).apps/web/tests/config/launch-kit-manifest.test.ts→ the shape cases on the fixture's manifest; the checks of this kit's own manifest values move totests/kit-only/launch-kit-manifest.test.ts.apps/web/tests/config/kit-check-parsers.test.ts→ inline documents only; parsing the kit's own tomls and workflows moves totests/kit-only/kit-files-parse.test.ts.apps/web/tests/config/patch-toml.test.ts→ runs on a snapshot of the shipped tomls (tests/config/fixtures/wrangler{,.staging}.toml), whichtests/kit-only/patch-toml-shipped.test.tsholds equal to the kit's.
How to apply
- Copy
scripts/kit-check.mjsandscripts/kit-check.d.mtsfrom the kit over your own; neither names the app. - Copy
apps/web/tests/config/fixtures/kit-fixture.mjsandapps/web/tests/config/fixtures/kit-fixture.d.mtsfrom the kit into the same paths. - Copy
apps/web/tests/config/kit-check.test.ts,apps/web/tests/config/kit-check-parsers.test.tsandapps/web/tests/config/launch-kit-manifest.test.tsfrom the kit over your own. - Copy
apps/web/tests/config/patch-toml.test.tsfrom the kit over your own, and copy the kit's UNPROVISIONEDapps/web/wrangler.tomlandapps/web/wrangler.staging.toml(the release tag's, renamed to your app the waykit:upgraderenames any file) toapps/web/tests/config/fixtures/wrangler.tomlandapps/web/tests/config/fixtures/wrangler.staging.toml, each with the kit snapshot's two-line# FIXTUREheader. Never copy your provisioned tomls there. - Do not copy anything under
apps/web/tests/kit-only/: those tests are the kit's own, and a copy runs none of them. - Leave your own
apps/web/wrangler.tomlandapps/web/wrangler.staging.tomlas they are: real KV ids are now valid in a copy.
Conflicts to expect
apps/web/tests/config/patch-toml.test.ts→ reads./fixtures/instead of the tomls on disk → take the kit's file whole.apps/web/tests/config/launch-kit-manifest.test.ts→ the live-file half moved to kit-only → take the kit's file whole.
Verify
node scripts/kit-check.mjsprints✔ conforms to the Launch kit contract (schema 1)and exits 0.- Once Launch has provisioned the app,
node scripts/kit-check.mjs --provisionedexits 0. pnpm --filter ./apps/web test:configpasses.pnpm gatepasses.
0.18.0
What changed
The kit declares the Launch kit contract in a root launch.kit.json (D36), checked by scripts/kit-check.mjs in the kit and in every renamed copy, so Launch drives a copy from the copy's own manifest.
launch.kit.json→ what the kit calls things and how Launch scaffolds, develops in, gates, ships and upgrades an app from it (docs/CONCEPTS.md§13).scripts/kit-check.mjs+scripts/lib/{kit-manifest,toml-lite,yaml-lite}.mjs→ the static conformance check, shared with the meta-kitrocketflare-dev/launch-kit;node scripts/kit-check.mjs [--exec].scripts/rename.mjs→ a copy keepslaunch.kit.json; the rename rewrites it through the token map except itskitblock (renameKitManifest)..rocketflare.json→kit.id(rocketflare), the id Launch reads from a copy's app manifest;launch.kit.jsonis on themanuallist.kit.yml→Launch kit checkinkit-checks, and the same check on the renamedmy-appcopy.apps/web/tests/config/{kit-check,kit-check-parsers,launch-kit-manifest}.test.ts→ the checker's own tests, and the manifest's values checked against the bootstrap, the dev server, the tomls and the RLS role.
How to apply
- Add
"id": "rocketflare"as the first key of thekitblock in.rocketflare.json, above"name". - Add
"launch.kit.json"to themanuallist in.rocketflare.json, so later kit releases show its diff rather than patch it through the token map. - In
launch.kit.json, which this upgrade added through the token map, setkit.idback torocketflareandkit.nameback toRocketflare; every other renamed value (session.env) stays as the upgrade wrote it. - Run
pnpm lint:fixsolaunch.kit.jsonand.rocketflare.jsontake Biome's layout.
Conflicts to expect
None — every file is new except .rocketflare.json, which the upgrade never ports, and scripts/rename.mjs with its lib, which it ports only with --include-kit-tooling.
Verify
node scripts/kit-check.mjs --execprints✔ conforms to the Launch kit contract (schema 1)and exits 0.node -p 'require("./launch.kit.json").kit.id'printsrocketflare.pnpm gatepasses.
0.17.8
What changed
Wrangler moves from 4.127 to 4.147: its CLI bundle is about 30% smaller, so wrangler dev answers its first request sooner, and it no longer queries GitHub when telemetry is turned off.
apps/web/package.json→wrangler^4.147.0(miniflare and workerd follow it to1.20261001.1inpnpm-lock.yaml).apps/web/worker-configuration.d.ts→ regenerated runtime types from the new workerd; theEnvinterface is unchanged.- No wrangler change between the two versions alters the kit's tomls (
[assets],[ai],[[workflows]], queues, Durable Objects, crons) or thewrangler dev/wrangler typesoptions it passes.
How to apply
- Set
wranglerto^4.147.0inapps/web/package.json, then runpnpm installfrom the workspace root to updatepnpm-lock.yaml. - Run
pnpm typecheck, which regeneratesapps/web/worker-configuration.d.tsthroughwrangler types, and commit the regenerated file. - An app with a
[[containers]]block that read the experimentalenv.EXPERIMENTAL_CLOUDFLARE_CONTAINER_IMAGESbinding must readctx.container.imagesinstead (wrangler 4.136, workers-sdk#15699); an app without containers has nothing to change here.
Conflicts to expect
pnpm-lock.yaml→ wrangler, miniflare, workerd, sharp and undici entries moved → take the app's ownpnpm installresult rather than the kit's hunks.apps/web/worker-configuration.d.ts→ generated file → regenerate withpnpm typecheckinstead of merging.
Verify
pnpm --filter @rocketflare/web exec wrangler --versionprints4.147.0or later.pnpm lint && pnpm typecheck && pnpm test && pnpm buildpass.pnpm devstarts andcurl -s -o /dev/null -w '%{http_code}' http://localhost:3001/api/healthprints200.
0.17.7
What changed
pnpm plugin checks a plugin's minKit against .rocketflare.json kit.version in a copy, not the root package.json version the rename restarts at 0.1.0, so pnpm plugin add in a freshly renamed copy no longer refuses every plugin with exit 6.
scripts/lib/manifest.mjs: newhostKitVersion(read, packageJsonVersion)— the rootpackage.jsonversion whenisKit, elsekit.version, throwing (naming.rocketflare.json) when a copy has none; typed inmanifest.d.mts.scripts/plugin.mjs:loadHost()reads the kit version throughhostKitVersion, soadd,upgradeandcheckcompareminKitwith the kit version, and a copy with nokit.versionstops with exit 1 naming the file.apps/web/tests/config/plugin-skills.test.tsandmanifest-lib.test.ts: a renamed-copy host (package.json0.1.0,kit.version0.17.6) installs aminKit0.13.0plugin; a copy below the floor is refused naming its kit version; the kit itself still readspackage.json.
How to apply
- Take
scripts/lib/manifest.mjsandscripts/lib/manifest.d.mtsfrom the kit diff (the newhostKitVersionexport). - Take
scripts/plugin.mjsfrom the kit diff (thehostKitVersionimport and thekitVersionlines inloadHost()). - Take
apps/web/tests/config/manifest-lib.test.tsandapps/web/tests/config/plugin-skills.test.tsfrom the kit diff. - Confirm
.rocketflare.jsonhas akit.versionequal to the kit release this app was made from or last upgraded to;pnpm kit:upgradewrites it, so only a hand-edited manifest lacks it.
Conflicts to expect
None.
Verify
node -p "require('./.rocketflare.json').kit.version"prints a kit release, not the app's own version.pnpm plugin checkexits 0.pnpm --filter @<slug>/web exec vitest run --project config tests/config/manifest-lib.test.ts tests/config/plugin-skills.test.tspasses.