Skip to content

Releases: rocketflare-dev/rocketflare

0.24.0

Choose a tag to compare

@cliftonc cliftonc released this 30 Oct 07:23
0.24.0
4a379df

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.

  • kitOnly now also lists scripts/release.mjs, the two maintainer nudges and lib/nudge-lib, scripts/default-plugins.mjs, scripts/install.sh, CONTRIBUTING.md and CODE_OF_CONDUCT.md; the rename deletes them (CONCEPTS §13).
  • The rename also drops the kit:release script and the two nudge hooks from .claude/settings.json, writes the app its own SECURITY.md, and removes retiredSurfaces from .rocketflare.json.
  • Kit-only tests moved out of tests/config/: kit-check, kit-check-parsers, launch-kit-manifest (now kit-only/launch-kit-schema), gate-string, the release helpers' blocks of upgrade-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.ts now also fails a copy whose package.json scripts or Claude Code hooks call a kit-only file.
  • ToolCtx (the plugin tool context) gains features(); example-feature's list_example_notes tool is offered only while the organisation has the flag, and its onTenantCreated note 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.ts imports the DB client lazily so a test's vi.mock of a barrel takes effect, and each example surface's paths now lists its own output view, eval suite, dataset, baseline and UI tests (triage-research requires research-topic).
  • apps/web/src/api/services/ai/kit.ts is now agent-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.md is kitOnly too (copies kept the kit's pending notes and went on writing their own); the rename now reports the licence and a missing kit.commit as careful rows; package descriptions no longer call the app a kit; and upgrade-lib.test.ts's ancestry case builds its own two-commit repository instead of reading HEAD~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

Choose a tag to compare

@cliftonc cliftonc released this 24 Oct 13:33
0.23.0
a37fb65

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 } on JobCtx, CronCtx, EmailCtx and PublicCtx (BackgroundMethods, apps/web/src/plugins/api/agent-runs.ts), through the route's own enqueueRun (CONCEPTS §16).
  • enqueueRun({ idempotencyKey }): the run id is derived from (tenant, agent, key) by idempotentRunId, so a repeat answers the first run, settled or not; no migration (CONCEPTS §9).
  • @testkit/unit: makeJobCtx, makeCronCtx and makeEmailCtx return Fake{Job,Cron,Email}Ctx with startedRuns ({ input, result?, error? }[]) and accept a startAgentRun replacement.
  • AgentCtx and ToolCtx gain storage(), the same FILES service as every other context (filesStorage in apps/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 before documents.content, chunks.text and every ai_spans string are written — Postgres refused them (22021 / 22P05).
  • composeNav folds 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.md no longer declares requires_surfaces: [feature-agents], which wrongly skipped the email slot for apps without the agent runtime; docs/plugin-api.md now expands BackgroundMethods.

See the porting note.

0.22.0

Choose a tag to compare

@cliftonc cliftonc released this 24 Oct 09:47
0.22.0
9c70a25

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) gains maxConcurrentToolCalls (default 4; cap 1 is sequential), executed concurrently by runToolLoop with events, transcript results and error handling preserved in model order (issue #4).
  • ServerPlugin.emailHandlers routes Cloudflare Email Routing mail to a plugin by recipient local part (* catch-all) via apps/web/src/api/email.ts; plugins adapt the context with emailCtx from @/plugins/api (CONCEPTS §16).
  • JobCtx, CronCtx, PublicCtx and EmailCtx gain storage() (the FILES bucket, as RequestCtx.storage()), @testkit/unit gains makeEmailCtx, and @/plugins/api exports the StorageService type.

Thanks to @NANDANS26 (#79, concurrent tool calls).

See the porting note.

0.21.0

Choose a tag to compare

@cliftonc cliftonc released this 16 Oct 20:23
0.21.0
7f31c0f

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_key is the exclusive slot a run holds: '' for an exclusive agent (one per organisation), enqueueRun({ concurrencyKey }) for one active run per key, NULL for exclusive: false (no slot, never deduplicated). The unique index is (tenant_id, agent_key, concurrency_key) over active rows with a key; findActiveRun takes the key (default ''). triage-research is now exclusive: false. From PR #77.
  • The plugin AgentCtx (@/plugins/api) gains interrupt(ask) and chatModel { provider, model }; AgentInterruptAsk / AgentInterruptAnswer are exported. PROMPT_REGISTRY is typed so a plugin's prompt key indexes it. agent-runs, ai-prompts and agent-models tests compare only the kit's own agents and prompts.
  • Replay (D38): replay.ts (replayRun, listReplayableRuns, diffJson, ReplayMissError), scripts/agent-replay.ts + the agent:replay script (--candidate, --live), ctx.once(key, fn, { replay: 'rerun' | 'inner' }), decideWithShapes / toDecision exported from decisions.ts, the skill rf-agent-replay, and PROCESS_TEST_TIMEOUT on the config / kit-only vitest projects.
  • enqueueRun(db, cfg, env, input) takes the config and enforces AGENT_MAX_ACTIVE_RUNS_PER_TENANT (default 50, 429 agent_runs_limit) and AGENT_TENANT_DAILY_COST_USD (429 ai_budget_exceeded).
  • POST /api/agents/runs/batch (enqueueRunBatch, createAgentRunBatch{Request,Response}Schema, AGENT_RUN_BATCH_MAX = 100): one non-exclusive agent (400 agent_exclusive otherwise), per-input errors, one createBatch() with a per-instance fallback; GET /api/agents/runs?batchId=.
  • ctx.scope(prefix) and ctx.map(key, items, fn, { concurrency, keyOf }) (services/agents/fanout.ts).
  • The ledger is one ai_usage row PER CALL carrying agent_run_id, written as each call returns by the run's meter (services/agents/budget.ts) for every call through ctx.chat.client and ctx.decide, so a run cancelled mid-loop keeps every turn it paid for (issue #2, from PR #74). The example agents no longer call recordUsage; ctx.recordUsage(call) is for a client an agent builds itself; GET /api/agents/runs/:id returns usage.
  • Budgets: meta.budget { maxTokens, maxCostUsd } over AGENT_RUN_MAX_TOKENS / AGENT_RUN_MAX_COST_USD, checked before each metered call across attempts; over → failed, event code agent_budget_exceeded, not retried.
  • Policies (issue #1, from PR #73): meta.policy { maxTurns, maxToolCalls, maxDurationSeconds, repeatedFailureLimit, onExhaustion }, passed to runToolLoop as policy, counted across attempts (ToolLoopCheckpoint.activeMs carries active time). 'final-answer' (default) forces one call to the terminal tool (salvaged: true); 'fail' throws ToolLoopExhaustedError → failed, event code agent_policy_exhausted. No policy, no change. research-topic's hand-written salvage is now its policy; GET /api/agents items carry policy, shown on the Agents page.
  • runToolLoop's authorizeTool(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

Choose a tag to compare

@cliftonc cliftonc released this 13 Oct 03:40
0.20.0
d8a8228

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 a temperature or a forced tool_choice. The adapters, POST /api/ai/config and 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.
  • Anthropic: a temperature is 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 plus tool_choice: auto. max_tokens is floored at ADAPTIVE_THINKING_MIN_MAX_TOKENS (4096) while a model thinks.
  • First-party openai now calls /v1/responses with store: false, because Chat Completions cannot call tools on GPT-6 Astra or 6.1 Sol, or on Luna while it reasons. openai_compatible stays on Chat Completions.
  • openai configs now have a thinking switch. Off sends the lowest reasoning.effort the model takes (none, or low on 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 ContentBlock of type: 'reasoning' and is sent back unmodified to the provider that produced it.
    • Checkpoints keep it. Chat history never stores it.
  • 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.anthropic is claude-haiku-5-5, which also means the platform ANTHROPIC_API_KEY tier now answers with it.
    • The openai chat default is gpt-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_UPDATED 2026-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 -pro variants 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

Choose a tag to compare

@cliftonc cliftonc released this 12 Oct 05:20
0.19.0
46706df

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.pick on AgentContext and the plugin AgentCtx; recorded once per (run, key) in agent_run_decisions and replayed on a retry.
  • New AI scope decisions and provider typesafe (Settings → AI, with a Test button); platform secret TYPESAFE_API_KEY; without either, the chat model answers on prompt key agent-decide, recorded llm_self_report.
  • resolveDecisions in services/ai/resolve.ts; readiness gains decisions and the source fallback.
  • New run event type decision, projected to AG-UI as kit.agent.decision; GET /api/agents/runs/:id returns decisions[]; 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 to research-topic) with an eval suite.
  • Evals: expected.decisions on EvalCase, DecisionJudge, decisionScore (accuracy + Brier).
  • Retrieval: the lexical half of searchChunks matches ANY query word (ranked by ts_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; RunAgentModal now builds the form once.
  • Why: docs/CONCEPTS.md §9, "Decisions (D37)".
  • scripts/lib/kit-manifest.mjs → the kit contract's optional launchProvides.oidc.callbackPath (default /auth/oidc/callback) and signedOutPath (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

Choose a tag to compare

@cliftonc cliftonc released this 11 Oct 04:59
0.18.1
7a2b3d5

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; --provisioned still 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's write_config does 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 to tests/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 to tests/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), which tests/kit-only/patch-toml-shipped.test.ts holds equal to the kit's.

How to apply

  1. Copy scripts/kit-check.mjs and scripts/kit-check.d.mts from the kit over your own; neither names the app.
  2. Copy apps/web/tests/config/fixtures/kit-fixture.mjs and apps/web/tests/config/fixtures/kit-fixture.d.mts from the kit into the same paths.
  3. Copy apps/web/tests/config/kit-check.test.ts, apps/web/tests/config/kit-check-parsers.test.ts and apps/web/tests/config/launch-kit-manifest.test.ts from the kit over your own.
  4. Copy apps/web/tests/config/patch-toml.test.ts from the kit over your own, and copy the kit's UNPROVISIONED apps/web/wrangler.toml and apps/web/wrangler.staging.toml (the release tag's, renamed to your app the way kit:upgrade renames any file) to apps/web/tests/config/fixtures/wrangler.toml and apps/web/tests/config/fixtures/wrangler.staging.toml, each with the kit snapshot's two-line # FIXTURE header. Never copy your provisioned tomls there.
  5. Do not copy anything under apps/web/tests/kit-only/: those tests are the kit's own, and a copy runs none of them.
  6. Leave your own apps/web/wrangler.toml and apps/web/wrangler.staging.toml as 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

  1. node scripts/kit-check.mjs prints ✔ conforms to the Launch kit contract (schema 1) and exits 0.
  2. Once Launch has provisioned the app, node scripts/kit-check.mjs --provisioned exits 0.
  3. pnpm --filter ./apps/web test:config passes.
  4. pnpm gate passes.

0.18.0

Choose a tag to compare

@cliftonc cliftonc released this 11 Oct 04:26
0.18.0
2663f7f

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-kit rocketflare-dev/launch-kit; node scripts/kit-check.mjs [--exec].
  • scripts/rename.mjs → a copy keeps launch.kit.json; the rename rewrites it through the token map except its kit block (renameKitManifest).
  • .rocketflare.json → kit.id (rocketflare), the id Launch reads from a copy's app manifest; launch.kit.json is on the manual list.
  • kit.yml → Launch kit check in kit-checks, and the same check on the renamed my-app copy.
  • 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

  1. Add "id": "rocketflare" as the first key of the kit block in .rocketflare.json, above "name".
  2. Add "launch.kit.json" to the manual list in .rocketflare.json, so later kit releases show its diff rather than patch it through the token map.
  3. In launch.kit.json, which this upgrade added through the token map, set kit.id back to rocketflare and kit.name back to Rocketflare; every other renamed value (session.env) stays as the upgrade wrote it.
  4. Run pnpm lint:fix so launch.kit.json and .rocketflare.json take 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

  1. node scripts/kit-check.mjs --exec prints ✔ conforms to the Launch kit contract (schema 1) and exits 0.
  2. node -p 'require("./launch.kit.json").kit.id' prints rocketflare.
  3. pnpm gate passes.

0.17.8

Choose a tag to compare

@cliftonc cliftonc released this 06 Oct 13:48
0.17.8
f98c5e6

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 to 1.20261001.1 in pnpm-lock.yaml).
  • apps/web/worker-configuration.d.ts → regenerated runtime types from the new workerd; the Env interface is unchanged.
  • No wrangler change between the two versions alters the kit's tomls ([assets], [ai], [[workflows]], queues, Durable Objects, crons) or the wrangler dev / wrangler types options it passes.

How to apply

  1. Set wrangler to ^4.147.0 in apps/web/package.json, then run pnpm install from the workspace root to update pnpm-lock.yaml.
  2. Run pnpm typecheck, which regenerates apps/web/worker-configuration.d.ts through wrangler types, and commit the regenerated file.
  3. An app with a [[containers]] block that read the experimental env.EXPERIMENTAL_CLOUDFLARE_CONTAINER_IMAGES binding must read ctx.container.images instead (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 own pnpm install result rather than the kit's hunks.
  • apps/web/worker-configuration.d.ts → generated file → regenerate with pnpm typecheck instead of merging.

Verify

  1. pnpm --filter @rocketflare/web exec wrangler --version prints 4.147.0 or later.
  2. pnpm lint && pnpm typecheck && pnpm test && pnpm build pass.
  3. pnpm dev starts and curl -s -o /dev/null -w '%{http_code}' http://localhost:3001/api/health prints 200.

0.17.7

Choose a tag to compare

@cliftonc cliftonc released this 06 Oct 10:23
0.17.7
c45ccdc

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: new hostKitVersion(read, packageJsonVersion) — the root package.json version when isKit, else kit.version, throwing (naming .rocketflare.json) when a copy has none; typed in manifest.d.mts.
  • scripts/plugin.mjs: loadHost() reads the kit version through hostKitVersion, so add, upgrade and check compare minKit with the kit version, and a copy with no kit.version stops with exit 1 naming the file.
  • apps/web/tests/config/plugin-skills.test.ts and manifest-lib.test.ts: a renamed-copy host (package.json 0.1.0, kit.version 0.17.6) installs a minKit 0.13.0 plugin; a copy below the floor is refused naming its kit version; the kit itself still reads package.json.

How to apply

  1. Take scripts/lib/manifest.mjs and scripts/lib/manifest.d.mts from the kit diff (the new hostKitVersion export).
  2. Take scripts/plugin.mjs from the kit diff (the hostKitVersion import and the kitVersion lines in loadHost()).
  3. Take apps/web/tests/config/manifest-lib.test.ts and apps/web/tests/config/plugin-skills.test.ts from the kit diff.
  4. Confirm .rocketflare.json has a kit.version equal to the kit release this app was made from or last upgraded to; pnpm kit:upgrade writes it, so only a hand-edited manifest lacks it.

Conflicts to expect

None.

Verify

  1. node -p "require('./.rocketflare.json').kit.version" prints a kit release, not the app's own version.
  2. pnpm plugin check exits 0.
  3. pnpm --filter @<slug>/web exec vitest run --project config tests/config/manifest-lib.test.ts tests/config/plugin-skills.test.ts passes.