dispatch()takes a structuredmessage, not an opaqueinput.AgentDispatchRequest.input: unknownis replaced bymessage: DeliveredMessage, the same unified shape a direct HTTP prompt uses internally:{ kind: 'user', body: string, attachments?: DeliveredAttachment[] }for a real chat turn, or{ kind: 'signal', type: string, body: string, attributes?: Record<string, string>, tagName?: string }for a structured event/webhook payload.bodyis always a string — JSON-stringify structured payloads yourself.dispatch()can now deliver akind: 'user'message with image attachments, the same way a direct HTTP prompt does (attachments onkind: 'signal'are not supported).messageis validated the same way as a direct prompt's body (a malformedmessagethrowsInvalidRequestError) instead of being forwarded unchecked. On the adapter surface, the submission input types collapse into oneAgentSubmissionInputinterface ({ kind: 'dispatch' | 'direct', submissionId, agent, id, message, acceptedAt }) —DispatchAgentSubmissionInputandDirectAgentSubmissionInputare removed, a dispatched submission's persisted input no longer duplicatesdispatchId(it always equaledsubmissionId; the publicDispatchReceipt.dispatchIdis unchanged), and the persisted-attachment helpers exported from@flue/runtime/adapterare renamed:prepareDirectSubmission→prepareSubmissionAttachments,hydratePersistedDirectSubmission→hydratePersistedSubmissionAttachments,matchesPersistedDirectSubmission→matchesPersistedSubmissionAttachments. Every first-party channel example and blueprint is updated to the new shape, following one convention:bodycarries the message itself and structured metadata (sender identity, ids, titles) goes inattributesas flat strings.- The direct agent HTTP wire body is a
DeliveredMessage.POST /agents/:name/:idnow accepts the same validatedDeliveredMessageshapedispatch()admits —{ "kind": "user", "body": "...", "attachments"?: [...] }for a chat turn (or akind: 'signal'event) — replacing the{ message, images? }wire body and the internal mapping layer behind it.@flue/sdk'sAgentPromptOptions.messageis now aDeliveredMessage(wasstring; theimagesoption folds intoattachments), andAgentPromptImageis renamedDeliveredAttachmentin@flue/sdkand@flue/react. TheDirectAgentPayloadtype is removed from@flue/runtime.flue run --input '{"message": "..."}'is unchanged — the CLI delivers it as akind: 'user'message. - Persisted storage is reset-only schema v5. The persisted agent-submission payload changed shape (both transports now persist one unified
messageinput; direct rows previously persistedpayload, dispatch rowsinput), so stores written by earlier versions are rejected at open withPersistedSchemaVersionErrorand must be cleared. There is no migration, consistent with the pre-1.0 reset-only policy. - Signal
tagNamemust be a valid XML tag name. The optionaltagNameon akind: 'signal'message is rendered as the signal's XML envelope in model context, so it is now validated (letters, digits,_,-,.; must not start with a digit,-, or., and must be non-empty) and a malformed value throwsInvalidRequestErrorat admission instead of injecting markup past the body/attribute escaping. - Direct agent prompts are fire-and-forget only. The
?wait=resultsynchronous mode on agent HTTP POSTs is removed; agent prompts always return a 202 admission. The in-process observer registry (createAgentSubmissionObserverRegistry,AgentSubmissionObserver,AgentSubmissionObserverRegistry), theDirectAttachedOptions/invokeDirectAttached/runDirectSyncModeadmission path, and theresultfield onSubmissionSettledRecord,AgentConversationSettlement,AgentSubmissionSettledEvent, and thesubmission_settledFlueEvent variant are all removed. Callers that need the actual assistant reply should read it from the conversation transcript viaclient.agents.history()or the live conversation stream. client.agents.prompt()is removed. Useclient.agents.send()(fire-and-forget) plusclient.agents.wait()(completion-await, nowPromise<void>) plusclient.agents.history()(to read the reply).AgentPromptResultandAgentPromptResponsetypes are removed from@flue/sdk.client.agents.wait()no longer resolves with a result. It resolvesvoidon completion and throwsFlueExecutionErroron failure or abort. A remote abort is now distinguishable from a real failure:FlueExecutionError.failureis'aborted'for an aborted settlement (previously both classified as'failed').AttachedAgentEventCallbacktype removed from@flue/runtime. TheonEventcallback parameter on admission no longer exists.reconcileInterruptedSubmissionreturn type simplified. ReturnsAgentSubmission | undefined(the replacement submission, orundefined) instead of the 5-variantReconciliationResultdiscriminated union. Custom coordinator implementations that inspected.dispositionshould branch on truthiness instead.
- Public conversation messages now expose typed
purpose(user,assistant,dispatch, oradvisory) anddisplay(visible,hidden, ordiagnostic), plus optionalturnIdgrouping and asignaldescriptor, so clients can distinguish public chat from internal, control, and advisory activity without parsing message text, timestamps, or ordering. The classification is applied identically acrossclient.agents.history()snapshots and live updates, and@flue/sdk/@flue/reactshapes are updated in lockstep (#404). @flue/react'suseFlueAgent()now exposesrefresh(), so apps observing an agent conversation that may be created out-of-band (a server-side wakeup, queue worker, or webhook) can re-check on their own schedule instead of faking an empty history snapshot. Retry policy stays in userland (#403).
- Terminalizing a durable agent submission (retry exhaustion, timeout, post-input interruption, or abort) now settles its conversation to a deterministic rest state instead of leaving dangling tool calls behind. Every tool call without a confirmed outcome gets an explicit interrupted-error
tool_outcomeand the batch is committed — recorded outcomes are preserved and nothing is re-executed or resumed — so atasktool call can no longer rest as "still running" forever in history projections, and the settled turn stays visible to future model context instead of being silently dropped. For ataskcall the marker includes the retained child conversation id; the child transcript itself is untouched. An interrupted in-progress assistant stream is likewise completed as aborted at terminalization. The terminal advisory now carries the interrupted-call list as structuredattributes.interruptedTools(also onSubmissionRetryExhaustedError/SubmissionInterruptedErrormeta), so apps can settle their own run state without parsing text. Conversations already left dangling by earlier versions self-heal on their next prompt: a new submission settles abandoned trailing state before appending its input (#419). - MCP tool connections no longer crash on Cloudflare Workers when a connected server advertises a tool
outputSchema. JSON Schema validation now uses a codegen-free strategy compatible with the workerd runtime instead of runtime code generation (#400). - Completed assistant messages now preserve their
submissionIdinclient.agents.history()snapshots, so clients that group a user turn with its assistant answer by submission id keep that grouping after reload (#402). - Tool-call duration is now durably recorded and surfaced as
durationMson the resolveddynamic-toolpart in both history snapshots and live updates, instead of being available only on the ephemeral live event (#407). - The Cloudflare extension's
baseandwrapcallbacks are now typed against the concrete generated Durable Object constructor (GeneratedDurableObjectClass<TBase, TEnv>, a constructor producingTBase & DurableObject<TEnv>), replacing the unreleased generic type-preservingwrapsignature. Every class Flue passes in really is a branded Durable Object, so brand-checked platform instrumentation such as@sentry/cloudflare'sinstrumentDurableObjectWithSentrynow accepts the class straight through — no generics, unsafe casts, or runtime constructability assertions, and no need for your own base type to extendDurableObject.extend()gains an optional secondTEnvtype parameter for typing the environment your instrumentation reads. The@flue/runtime/cloudflaretypes now referencecloudflare:workerstype-only (its runtime import graph is unchanged); projects without Cloudflare workers types configured degrade toanyunderskipLibCheck(#410). - Documented the supported pattern for reaching a private Flue agent over a Cloudflare service binding: point the
@flue/sdkclient'sfetchoption at the binding, since thebaseUrlhost is never dialed and only the pathname and query drive routing (#408). - Documented the
kind: 'user'vskind: 'signal'convention:useris a direct user talking to the assistant (a 1:1 chat surface);signalmodels everything beyond that — including most channels, where a Slack thread or GitHub issue is a multi-user conversation the agent participates in as one member, with sender identity carried inattributes. - Channel examples no longer drop sender identity and event metadata during dispatch: GitHub (
sender, issue ref,title,installationId— restoring the self-reply-loop guard), Teams, Google Chat, Linear, Telegram, Twilio, and Messenger regain the fields theDeliveredMessagemigration lost, as flatattributes. Telegram media-only updates now dispatch a'[photo message]'-style placeholder body instead of an empty string. - Removed dead per-submission result computation left behind by the result-await removal (each settled submission no longer builds a response text and full-conversation usage aggregate that nothing reads), the never-populated
interruptedToolsfield onSubmissionInterruptedErrormeta and the terminal-advisory renderer, and the unusedAttachedAgentEventCallback-era boolean returns on internal settlement helpers.
- Conversation message projections now include server-authored timestamps, and SDK/React clients preserve those timestamps while maintaining optimistic send state.
- Cloudflare Durable Object agent execution now establishes the instance context at agent entry boundaries, fixing runtime paths that needed the active instance during direct prompt handling.
- Released the database adapter packages (
@flue/libsql,@flue/mongodb,@flue/mysql,@flue/postgres, and@flue/redis) on the current beta line.
This pre-1.0 release reworks how an agent's conversation is durably recorded and communicated to clients, replacing the beta session-store model with one append-only canonical stream per instance behind a single client-facing protocol. The breaking surface is concentrated in this conversation layer; agent execution, models, tools, and workflows are unchanged. Because the persisted format changed, stores are reset-only (schema v4) with no migration from beta formats, so existing data must be cleared before upgrading. For guides and API reference, see the documentation.
- Persisted storage is reset-only schema v4. Pre-1.0 persisted stores from any other schema version are rejected and must be cleared; there is no migration from the beta session-store formats. Custom
PersistenceAdapterimplementations must provideconversationStreamStoreandattachmentStorealongside execution, run, and event-stream stores, and customAgentSubmissionStoreimplementations must addrequestSessionAbort()and persist anabortRequestedAtsignal for the new agent abort path.SessionStoreand session-transcript adapter contracts are removed. - Agent conversations use one append-only canonical stream per agent instance. Session history, compaction, child topology, tool outcomes, settlement, and recovery are canonical records in that stream; operational submission rows and observable event streams are not transcripts. Sessions append for the instance lifetime, per-session deletion is removed, and retained Action or Task conversations are no longer recursively deleted. Workflow-local canonical conversation state is scoped to one workflow execution rather than shared across runs.
- Agent conversation reads expose one materialized projection, not canonical records.
client.agents.history()returns aFlueConversationSnapshotandclient.agents.observe()maintains a liveFlueConversationState; both are built from a single, strictly-validated UI chunk protocol (ConversationStreamChunk) that the runtime projects from its private canonical log. The canonical record schema, the client-side reducer,agents.updates(), and replay/offset bookkeeping (AgentConversationDeltaState,recordIds) are no longer public.@flue/reactconsumes the SDK projection directly: its message types are renamedFlueConversationMessage/FlueConversationPart(no AI SDK compatibility is claimed), tool parts aredynamic-tool, and attachments and optimistic uploads share onefilepart. - Conversation reads address the agent instance's default conversation only. The
conversationId,harness, andsessionselectors are removed from the SDK, the HTTP conversation route, anduseFlueAgent; the vestigial Reacthistory: 'all'option is removed. - Attachments are separate immutable payloads with an opt-in byte route. Canonical records carry opaque references resolved through the required
AttachmentStore. The public conversation projects them asfileparts carrying{ mediaType, id?, size?, filename?, url? }. Bytes are served from a newGET /agents/:name/:id/attachments/:attachmentIdroute that is opt-in per agent: it returns 404 unless the agent module exports anattachmentsHono middleware (which authorizes and scopes access).@flue/sdkresolves a ready-to-useurlonto durably-recordedfileparts (and exposesclient.agents.attachmentUrl(name, id, attachmentId)); a local optimistic echo instead carries adata:URL preview of the bytes being uploaded. - Free-floating conversation data events are removed.
emitData()and standalonedata-*message parts are removed from runtime, tool, Action, SDK, and React APIs. Structured tool output remains available on the owning tool part; workflow Actions continue to return validated structured output. - The
model: falseagent configuration is removed. Every agent definition and profile must declare a concrete default model string; per-callmodeloverrides still apply. TheModelConfigtype andModelNotConfiguredErrorare removed.
- Agent work can be aborted.
client.agents.abort(name, id)(andPOST /agents/:name/:id/abort) stops the in-flight attempt and settles queued work for an agent instance as a distinct aborted outcome, on both the Node and Cloudflare runtimes. Waiters reject with the newSubmissionAbortedError, and the abort surfaces inobserve()/history()as asubmission_abortedadvisory. Crash-interrupted work found during recovery settles as aborted rather than resuming. - Cloudflare AI binding models can route to Anthropic through AI Gateway. Binding-backed models whose id begins with
anthropic/now use the Anthropic Messages wire format, alongside the existing OpenAI-compatible Workers AI path.
- Canonical tool outcomes are now durably recorded before one atomic commit publishes a complete tool-result batch. Recovery reuses known outcomes and materializes unknown interrupted outcomes without exposing partial tool-result prefixes.
- Recovery now resumes an in-flight, model-invoked
task()subagent in-process from its durable conversation and resolves the parent's tool call from the resumed result, instead of leaving a generic interrupted marker (#378). - Direct agent prompts now record the user message in the canonical conversation (with its submission id) on both the Node and Cloudflare runtimes, so a page refresh reconstructs the full transcript — including the user's prompt — from
client.agents.history()/observe()instead of dropping it. @flue/reactkeeps a stable message id across the optimistic→confirmed transition (the canonical user message is re-keyed to the optimistic id), so keyed/virtualized transcripts no longer see a remove+add that breaks auto-scroll. Failed sends are retained in the transcript and surfaced via a newfailedSendssnapshot field for retry affordances instead of silently disappearing. Optimistic image sends render an instant local preview.flue devnow serves permissive, credential-safe CORS (reflecting the requestOrigin, answering preflight with 204, and exposing theStream-Next-Offset,Stream-Up-To-Date, andLocationheaders) so a separate-origin SPA can call the dev server and advance its durable-stream resume offset without configuration. Deployed Node servers are unchanged; CORS there remains an application concern.@flue/sdkbinds its defaultfetchtoglobalThis, fixing aTypeError: Illegal invocationwhen calling the client from a browser without a pre-boundfetch.- JSON output snapshots now drop
undefined-valued object properties to matchJSON.stringify, so Actions, tools, and workflows can return idiomatic unset optional fields (#364). flue devnow rebuilds on source edits on Windows, where path-separator differences previously made edits look like output-directory changes and skipped the rebuild (#377).- Updated
@earendil-works/pi-aiand@earendil-works/pi-agent-coreto 0.80.2.
- Added durable structured data parts. Workflows, custom tools, and model-invoked Actions can emit validated JSON activity with
emitData(), while@flue/reactexposes AI SDK-compatibledata-*message parts and reconciles lifecycle updates by name and id.
- Direct prompts now emit their persisted user message before model output so
@flue/reactcan reconstruct it after refresh.
- Packaged Agent Skill resources now remain available when a sandbox adapter does not provide a filesystem
readtool. - Interrupted stream recovery now persists compact, linearly growing segments, omits streamed tool-call arguments, and rejects segments larger than the persistence-safe 1.9 MB limit.
@flue/reactnow accepts compatible@flue/sdkprereleases instead of requiring one exact prerelease.
flue devnow resolves attributed Markdown and Agent Skill imports exported by workspace packages.- The built-in
readtool now reads files only and returns the filesystem error when given a directory. - Cloudflare projects now require
agents@^0.14.2, whose schema migration repairs upgraded Durable Object SQLite databases missing the Agents SDK's MCP server table.
useFlueAgent()now publishes requested durable history atomically, exposeshistoryReady, continues live observation from the exact hydrated checkpoint, and keeps optimistic messages in their canonical transcript position when durable echoes arrive.
- The incomplete public
/openapi.jsonroute is removed. It described only agent and workflow invocation while omitting Durable Streams reads, run metadata, and channels, so it was not a reliable contract for the mounted public API. Use the documented HTTP routes or@flue/sdk; a public OpenAPI document may return once it can describe the complete surface accurately. flue runnow executes agents and workflows through the normal HTTP application. Local runs temporarily expose route-free resources through an existing authoredflue()mount and executeapp.tsplus application and resource middleware. Use--server <path>to select an authored local mount or an absolute--server <url>withagent:<name>orworkflow:<name>to attach remotely; the earlier private child-process invocation path is removed.- Workflows are now definitions built around Actions. Workflow modules must default-export
defineWorkflow({ agent, action })ordefineWorkflow({ agent, input?, output?, run }). Every workflow requires an agent definition. The runner now owns root harness initialization, so the legacy namedrun(ctx)export, publicctx.init(), named workflow harness options, and workflow payload passed to agent initializers are removed. Movectx.payloadto a declared Actioninput, bind the agent on the workflow and use the suppliedharness, and move environment- or resource-dependent policy to the agent initializer. Action context does not exposectx.id,ctx.env, orctx.req; validate transport data before admission and pass required values explicitly as input. - Agent and workflow declaration APIs use consistent
define*naming.createAgent()is renamed todefineAgent()and its returned type is nowAgentDefinition;createAgent()remains as a deprecated compatibility alias.createWorkflow()is renamed todefineWorkflow()and its returned type is nowWorkflowDefinition, with no compatibility alias.CreatedAgentandCreatedWorkfloware removed. - Tool definitions now use
input,output, andrun. Replaceparameterswith an optional Valibotinputschema and replaceexecute(args, signal)withrun({ input, signal }). The removedparametersandexecutefields now produce a migration error. Return structured data directly fromrun()instead of callingJSON.stringify(); Flue validates and transforms declared output, snapshots the result, and JSON-serializes it for the model. - Workflow invocation data is consistently named
input. Replace SDK{ payload }with{ input },flue run --payloadwithflue run --input,RunRecord.payloadwithRunRecord.input,CreateRunInput.payloadwithCreateRunInput.inputin customRunStoreimplementations, andrun_start.payloadwithrun_start.input. HTTP request bodies remain unwrapped JSON. Built-in persistence adapters continue reading existing physicalpayloadcolumns and keys. Persisted non-v3 product events, including legacyrun_start.payloadevents, are rejected and must be cleared or migrated. - Workflow and delegation types are simplified.
ExtractedWorkflowandInlineWorkfloware removed; bothdefineWorkflow()forms return specializedWorkflowDefinitiontypes.TaskDepthExceededErrorandtask_depth_exceededare renamed toDelegationDepthExceededErroranddelegation_depth_exceededbecause the limit applies across nested Tasks and Actions. - Runtime context types now describe their actual roles.
FlueContextis renamed toFlueEventContextforobserve()subscribers, andAgentCreateContextis renamed toAgentInitializerContext. - Session persistence moves to version 8. Custom
SessionStoreadapters must replacetaskSessionswithchildSessions: ChildSessionRef[], using discriminated Task and Action references. Existing version 6 session data is unsupported and must be cleared or migrated. - Workflow HTTP exposure and receipts are simplified. A workflow's
routeexport now controls onlyPOST /workflows/:name; existing runs are private over HTTP unless the workflow separately exportsruns: WorkflowRunsHandler. When upgrading, addrunsto every workflow whose runs must remain available toclient.runs,useFlueWorkflow(), or raw/runs/:runIdrequests, and move or share the previous run-read authorization fromroute. Omittedruns, unknown runs, and runs owned by removed or renamed workflows now return the same404. Workflow HTTP and SDK admission receipts are now{ runId }, and waited results are{ runId, result }; remove uses of workflowstreamUrlandoffset, and use the knownrunIdwithclient.runsor/runs/<runId>instead. Workflow responses no longer includeLocationorStream-Next-Offsetheaders. Continue forwarding required credentials through SDK client headers. CustomRunStoreadapters must return only{ runId, workflowName }fromlookupRun();listRuns()continues returning fullRunPointervalues. Agent receipts are unchanged. - The
flue logscommand is removed. Use SDKclient.runs.get(),client.runs.events(), orclient.runs.stream()for typed run inspection, or consume the raw/runs/:runIdAPIs. The owning workflow must still exportrunsmiddleware for HTTP access. - Event indexes are no longer stream offsets.
eventIndexremains the identity and ordering coordinate for events within a runtime context, but workflow consumers must not convert it into a Durable Streams resume offset. CheckpointFlueEventStream.offsetor the rawStream-Next-Offsetheader instead. - Model telemetry is canonicalized in FlueEvent v3.
turn_requestnow requiresrequest: ModelRequest, while terminalturnrequires aModelRequestInfosummary andModelResponse. Provider registration identity (providerId) and semantic provider identity (providerName) are distinct; output, usage, finish reason, and normalized errors live underresponse. Removed top-level model/provider/API/input/reasoning/compaction/output/usage/stop-reason/error fields have no aliases or fallback reads. Runtime and SDK readers reject every non-v3 product event with structured upgrade guidance.FlueObservationnow reuses the product event'svand adds live-only caller and tool detail; the unusedFlueTelemetryRecordprojection and redundant requeststreamfield are removed. The OpenTelemetry GenAI projection revision is 5 and Flue telemetry extension revision is 3; the semantic-convention revision remains unchanged. - Agent Skill names now follow the specification's ASCII naming rules. Imported skills, workspace-discovered skills, and skills created with
defineSkill()must use 1–64 lowercase ASCII letters, numbers, and single hyphens, with no leading or trailing hyphen. Rename previously accepted Unicode skill names and their directories before upgrading.
useFlueAgent()now accepts aliveoption so React clients can select SSE or long-poll Durable Streams transport.- Actions now serve as reusable finite orchestration for both workflows and model tools, with invocation-scoped harnesses, strict JSON output serialization, and one execution path for schema validation and transformed values.
- Agent definitions can expose Actions through
actions. Model-invoked Actions run as framework-owned tools in isolated child scopes while sharing the parent policy, sandbox, filesystem, and environment. Action sessions are retained with their parent, recursively deleted, cancellation-aware, and governed by the same mixed Action/Task delegation-depth limit. - Added top-level
invoke(workflow, { input })for admitting discovered workflow definitions programmatically. It returns{ runId }after real run and event-stream admission, does not wait for completion, bypasses route middleware, supports route-free workflows, and preserves Node and Cloudflare's existing execution topology. - Added
defineSkill()for defining Agent Skills entirely in TypeScript, including instructions, standard frontmatter metadata, and supporting text or binary files. Defined skills use the same progressive-disclosure activation and lazy file access as importedSKILL.mddirectories, enabling single-file agents without build-time skill imports.
flue devnow has a quieter, timestamped server output with discovered resources and concise agent and workflow lifecycle logs.flue devnow uses the existing Vite development-server watchers for Node and Cloudflare instead of a separate recursive project watcher. Projects can export a namedviteconfiguration fromflue.config.*to customize native Vite behavior such asserver.watch.ignored.- Workflow execution now validates input before initializing the agent or sandbox, waits for active operations to settle before terminal run persistence, and compensates failed stream or scheduler admission so runs are not left active indefinitely.
- Fixed Cloudflare sandbox shell calls failing before execution because an
AbortSignalwas sent across the Durable Object RPC boundary. - OpenTelemetry now projects complete Flue workflow, agent, inference, delegated-task, and tool execution into pinned GenAI semantics with persistent session conversation IDs, active trace context, provider and usage metadata, metrics, exception Logs, and documented
flue.*extensions. Content capture uses one default-off global policy with a detached transform, deterministic structural and UTF-8 byte limits, independent external delivery, safe diagnostics, and bounded truncation/omission markers. Provider inference spans exclude local tool latency, compaction calls activate their own chat spans, workflow recovery creates a new trace segment, and caller shell execution has one active Flue-owned span. Generated Node applications dispose instrumentation during shutdown and reload. The GenAI projection revision is 5 and Flue telemetry extension revision is 3; the pinned semantic-convention revision and schema are unchanged. - Updated
@earendil-works/pi-aiand@earendil-works/pi-agent-coreto 0.79.10.
- Fixed model-invoked
taskcalls being unable to pass images from the current conversation to a child agent. Flue now exposes stable attachment IDs alongside image prompts and acceptsattachments: [{ id }], including across session reloads while the image remains visible in the calling session's model context.
- Persistence adapters now use one async
connect()contract. Custom adapters returnexecutionStore,runStore, andeventStreamStoretogether;RunRegistryis removed in favor ofRunStore, and adapters must stamp and check schema versions. - Workflow run APIs are simplified. Run IDs are now opaque
run_<ulid>values, invocation responses use one flat{ streamUrl, offset, runId? }envelope, andGET /runs/:runId?metareplaces the removed admin run API.admin(),client.admin.*,adminBasePath, and related docs are removed. - Tool and timeout APIs changed.
defineTool({ parameters })now uses valibot instead of TypeBox, the rootTypeexport is removed, duration fields aretimeoutMs, and durabilityretrybecomesmaxAttempts. - Cloudflare and sandbox cleanup.
cloudflareSandbox()replaces the workerd stub heuristic;getVirtualSandbox,sandbox: false, and expired sandbox migration shims are removed. - Session and event contracts are tightened. Public session operations expose
FlueSession, subagent profiles are self-contained, session errors are typed, and durable events now carryv: 1without persistingturn_request,message_update, or rawassistantMessageEventpayloads. Streaming deltas are best-effort live progress;message_endis authoritative for completed assistant messages, and late attachment may miss earlier partial output until it arrives. Internal interrupted-turn recovery is unaffected. - Cloudflare extension imports moved. Generated-entry plumbing now lives under
@flue/runtime/cloudflare/internal; user-facing Cloudflare imports remain authoring-only. - GitHub handlers now receive provider-native deliveries. Replace
{ c, event }with{ c, delivery }; branch ondelivery.nameand nativedelivery.payloadfields instead of Flue's normalizedevent.type,event.payload, andevent.rawwrappers. The fixed event allowlist, syntheticunknownvariant, form-encoded ingress, andhandlerTimeoutMsare removed. - Slack handlers now receive provider-native payloads.
events,interactions, andcommandsuse{ c, payload }; Events API callbacks expose the officialSlackEventunion, and normalized wrappers, fixed-workspace filtering, package timeouts, and legacy interaction types are removed. - Discord handlers now receive provider-native interactions. Callbacks preserve Discord API v10 fields and numeric discriminants; normalized wrappers, redundant application-id filtering, the non-cancelling package timeout, and redundant guild channel/thread identity are removed.
- Google Chat handlers now receive provider-native deliveries. Direct interactions use
{ c, payload }, wrapped Workspace Events use{ c, delivery }, and normalized event wrappers and the non-cancelling package timeout are removed. observe()now receives every event directly. Thetypesfilter and per-subscriber JSON snapshots are removed; callbacks should branch onevent.typeand treat events as read-only.
- Built-in
sqlite()now persists workflow runs and indexes, matching PostgreSQL and Cloudflare durability; all built-in SQL stores now schema-version stamp. @flue/runtimeexportslistRuns(),getRun(), andlistAgents(); SDKruns.get()uses public?meta; workflowwait=resultand typed direct-agent prompt responses are supported.CallHandlenow implements the full Promise interface, and SDK stream coordinates are taken from server responses rather than fabricated.FlueFs.writeFile()now guarantees parent directory creation in every sandbox mode;ShellOptions.timeoutMsis available for shell operations.- OpenTelemetry spans and attributes now align with GenAI semconv.
- Added
@flue/reactwithFlueProvider,useFlueAgent(), anduseFlueWorkflow()for live agent transcripts and workflow-run observation. Agent messages use an AI SDK v5-compatible parts shape without a runtime dependency onai. - Added first-party
@flue/stripe,@flue/notion,@flue/resend,@flue/shopify,@flue/intercom,@flue/zendesk,@flue/salesforce,@flue/teams,@flue/google-chat,@flue/linear,@flue/telegram,@flue/whatsapp,@flue/twilio, and@flue/messengerpackages for verified HTTP ingress, constructor-owned typed handlers, canonical provider identity where available, and discoveredchannels/<name>.tsrouting. Existing@flue/github,@flue/slack, and@flue/discordpackages were rewritten and expanded around the same channel contract. Namedflue addblueprints create editable project code using provider SDK or Fetch clients and application-owned tools. flue add <kind> <name|url>now serves categorized channel, database, and sandbox blueprints.flue update <kind> <name|url>returns the same current guide with versioned primary-file markers and cumulative upgrade instructions so coding agents can update generated integrations while preserving application customizations.- Added driver-free
@flue/mysql,@flue/redis, and@flue/mongodbpersistence adapters with durable sessions, submissions, workflow runs, event streams, and image chunks. New database blueprints and ecosystem guides cover MySQL, Supabase, Redis, Valkey, and MongoDB. - Durable event-stream reads accept
tail=Nto start from the beginning while reading at most the latest N events. Direct agent prompt receipts and their emitted events now expose asubmissionIdfor reliable correlation. @flue/sdkaccepts browser-relative base URLs such as/api, exposes typed message snapshots, and supportstailacross stream APIs.
- The WhatsApp channel now accepts Business-Scoped User ID webhook payloads when Meta omits phone-number fields, preserves BSUID and parent-BSUID metadata, and uses collision-safe phone, BSUID, and group conversation identities. Its editable client example sends BSUID messages through the SDK's authenticated low-level request path.
- Channel routing now accepts valid Fetch responses across JavaScript realm boundaries while continuing to reject tagged non-response objects.
- Recovery now resumes shutdown-interrupted turns, settles completed work before budget or timeout checks, repairs partial tool batches without replaying completed tools, and emits durable submission-settlement events for waiters.
- Cloudflare attempt markers are now Flue-owned rather than querying private Agents SDK tables.
flue logstreats--sinceas an opaque Durable Streams offset, supports--format ndjson, and uses public run metadata.- Many bug fixes landed across Node and Cloudflare execution, SDK stream iteration, CLI shutdown and reload, Workers AI streaming, sandbox filesystem behavior, skill parsing, docs, and test coverage.
- Updated
@earendil-works/pi-aiand@earendil-works/pi-agent-coreto 0.79.4, and aligned the documented Node.js minimum with their>=22.19.0requirement. - Skills can now be imported from npm and workspace packages through Vite resolution; package-manager symlinks are supported, and packaged skill identity is derived from deployed content.
- Added a same-origin React chat example with agent conversation and workflow log views.
- Direct agent HTTP requests and
@flue/sdkprompts can include images with up to 14 MiB of encoded data per image. Node and Cloudflare SQLite persistence stores image data in safe chunks and restores it for future turns and after restarts.
- The grep tool now uses ripgrep when available, falls back to grep, treats patterns as extended regular expressions by default, and supports literal matching.
- SQL-backed sessions now store each history entry in its own row instead of rewriting the entire session history as one JSON value. Session saves remain transactional and preserve ordered history across Cloudflare Durable Object SQLite, Node SQLite, and PostgreSQL.
flue docsbrowses the documentation offline. The docs markdown already shipped inside@flue/cliis now reachable from the command line:flue docslists every page,flue docs read <path>prints one page as Markdown, andflue docs search <query>prints ranked JSON results. Content requires no network access and always matches the installed CLI version. Designed for coding agents (search → read), per Documentation.
- Runtime events no longer carry raw image bytes (#221). Image content blocks in session events (
message_*,turn_request,turn_end,agent_end,tool_call) keep theirmimeTypebut havedatareplaced with the exportedIMAGE_DATA_OMITTEDsentinel before events reach observers and persisted run history. Model context and persisted session history retain the real bytes. Events persisted before this change are unaffected. - Updated
@earendil-works/pi-aiand@earendil-works/pi-agent-coreto 0.79.1.
- Fixed workflow run-event persistence issuing one durable storage write per streamed chunk. Per-chunk streaming events (
text_delta,thinking_start,thinking_delta,thinking_end) are now buffered and flushed to the event stream store at most once every 3 seconds instead of on every chunk. Live stream readers still see deltas; history replay and interrupted-stream recovery are unaffected.
- Durable Streams protocol replaces WebSocket and SSE transports. Agent instances and workflow runs are now URL-addressable durable event streams. Clients consume events via DS-compliant
GET(catch-up, long-poll, SSE) with automatic offset-based reconnection.POST /agents/:name/:idnow returns202 { streamUrl, offset }; add?wait=resultfor200 { result, streamUrl, offset }.GET /agents/:name/:idreads the event stream.GET /runs/:runIdreplaces both/runs/:runId/eventsand/runs/:runId/stream. WebSocket transport,AgentSocket,WorkflowSocket, and all socket-related types are removed from@flue/runtimeand@flue/sdk. - Named sessions removed from agent public API. The
sessionparameter is removed from prompt submission, dispatch, SDK, and CLI. Agent instances always use the"default"session internally. An agent instance is now a single conversation with a single event stream. - SDK rewritten with
@durable-streams/client.@flue/sdknow exportsagents.prompt(),agents.send(),agents.stream(),runs.stream(),runs.events(), andworkflows.invoke().agents.prompt()waits for the result;agents.send()returns stream coordinates immediately.FlueEventStream<T>wraps the DS client'sjsonStream()as an async iterable withcancel()andoffsetsupport. connectEventStreamStore()is now required onPersistenceAdapter. Custom adapters must implement this method and provide durable event-stream storage. The built-insqlite()and@flue/postgresadapters provide implementations.client.runs.get()now reads from the admin mount. Applications using that SDK method must mountadmin()and configure the client with the matching admin base path.
@flue/postgressupports durable event streams.PgEventStreamStoreprovides a Postgres-backed implementation ofEventStreamStorewith transactionalappendEvent, in-process subscriber hooks, and full DDL in the existing migration transaction. Postgres deployments now have workingGETstream endpoints for agents and workflow runs.- DS protocol read endpoints.
GETsupports catch-up (JSON array), long-poll (30s timeout withStream-Cursor), and SSE (with 15s heartbeat and control events).HEADreturns stream metadata. Responses includeStream-Next-Offset,Stream-Up-To-Date,Stream-Closed,ETag, andCache-Controlheaders per the DS protocol spec. Reads useCache-Control: no-store; there is no fallback polling path when a live subscription is unavailable.
RunStorereduced to metadata only.appendEvent()andgetEvents()removed; events are exclusively inEventStreamStore.RunSubscriberRegistrydeleted.- Agent POST responses are now split by wait mode. Default agent POST returns
202with stream coordinates;?wait=resultreturns the terminal result. Event observation is decoupled from POST responses via the DS stream read path. SqliteEventStreamStorecreates its own tables in the constructor. No separateensureEventStreamTables()call required; removed fromensureSqlAgentExecutionTables()and@flue/runtime/internalexports.flue logsrewritten to use@flue/sdkDS streaming. Removed dead--sessionflag.- Fixed stale WebSocket references in documentation, README, and generated entry code.
- Fixed generated Node and Cloudflare app entrypoints by avoiding collisions with application-owned
appbindings. - Updated
@earendil-works/pi-aiand@earendil-works/pi-agent-coreto 0.79.0. - Fixed typos in documentation: "truely", "exited", and "suitible" (#211).
- Fixed docs search dialog throwing
InvalidStateErrorwhenCmd/Ctrl+Kis pressed while already open (#214). - Fixed SSE parser missing frame boundaries when CRLF is split across stream chunks or when using CR-only line endings (#216).
- Added
deleted_classesandrenamed_classesmigration examples to the Cloudflare target documentation (#203).
This is a large pre-1.0 release that establishes Flue's durability model across Node.js and Cloudflare. Rather than cataloging every intermediate beta change, this entry highlights the final APIs and the most important upgrade work. For guides and API reference, see the documentation.
- Cloudflare durable deployments require a migration. Generated Durable Object bindings and class names changed to
FLUE_<NAME>_AGENT,FLUE_<NAME>_WORKFLOW,FLUE_REGISTRY,Flue<Name>Agent,Flue<Name>Workflow, andFlueRegistry. Existing deployments must add authored Wranglerrenamed_classesmigrations for already-deployed agent and workflow classes, update direct binding access such asenv.Assistanttoenv.FLUE_ASSISTANT_AGENT, and introduce fresh SQLite-backed agent classes throughnew_sqlite_classes; existing KV-backed classes cannot be converted in place. Installagents >=0.14.1 <0.15.0for the audited Agents SDK behavior used by this release. - Runtime surface cleanup. Removed
tool_execution_*event types, the@flue/runtime/app,@flue/runtime/client, and@flue/runtime/sandboxcompat subpaths, publicAgentConfig/DirectAgentPayloadexports, public Cloudflare agent WebSocket adapters,store()from@flue/runtime/cloudflare, and the old Cloudflare shell migration stubs. - Persistence adapter contracts changed. Custom adapters now implement
connectRunStore()andconnectRunRegistry()onPersistenceAdapter, useSubmissionClaimRefforclaimSubmission(), and providerenewLeases(),listExpiredSubmissions(), anddeleteSession()onAgentSubmissionStore. - Session state changed. Ordinary session names beginning with
task:are now reserved for framework-owned delegated-task history, and existing version-4 beta session state is rejected because provider affinity now uses one opaqueaff_<ULID>key instead of derived instance/harness/session identifiers. - OpenTelemetry sanitization changed.
captureContentis replaced by an application-ownedsanitize(event)callback; metadata and generic failure messages are exported by default.
-
Unified durable agent execution. Direct HTTP, SSE, WebSocket, local CLI, and
dispatch(...)inputs now share one SQL-backed submission lifecycle on Node and Cloudflare: admission, same-session ordering, claiming, journaled execution, conservative recovery, and retained terminal receipts. -
Pluggable persistence. Add source-root
db.tsadapters, built-insqlite(path?)persistence for Node, the new@flue/postgrespackage, and the@flue/runtime/adapter/@flue/runtime/test-utilssubpaths for custom backend authors.// src/db.ts import { sqlite } from '@flue/runtime/node'; export default sqlite('./data/flue.db');
// src/db.ts import { postgres } from '@flue/postgres'; export default postgres(process.env.DATABASE_URL!);
-
Signal messages and mid-turn recovery. Stream chunks are persisted during provider output so interrupted turns can resume from partial assistant text. Framework-injected context now uses signal messages for stream interruption/continuation, terminal submission advisories, dispatched input, compaction summaries, and branch summaries.
-
Cloudflare extension hooks. Agent and workflow modules may export
cloudflare = extend({ base, wrap })from@flue/runtime/cloudflareto add native Agents SDK lifecycle hooks beneath Flue-owned routing or wrap generated Durable Object classes with integrations such as Sentry.
- Node execution is concurrent and shutdown-aware. Different sessions now process in parallel through a concurrent claim loop. Claimed submissions carry renewable leases, and SIGINT/SIGTERM drains active work at turn boundaries before reclaiming unfinished submissions on next startup.
- Postgres workflow history is durable.
@flue/postgresstores runs, run events, and the run registry in SQL-backed tables; built-in SQLite keeps run history in memory. - Cloudflare routing and recovery are stricter. Generated agent and workflow bindings are resolved explicitly instead of inferred from the Agents SDK environment scanner, workflow event identity is append-only by
(runId, eventIndex), and Cloudflare workflow storage preserves same-ID reset behavior and explicit terminalnullresults. - Observability is more accurate. OpenTelemetry now closes interrupted workflow spans correctly, exposes event indexes and compaction usage, and supports
resolveRootContext(event, ctx)for parenting Flue roots under application-owned spans. - Runtime resilience improved. Prompt operations retry transient provider failures with abortable exponential backoff, generated import paths are escaped safely, and Bun compatibility diagnostics now point users at the right runtime upgrade.
- Fixed agents' ability to activate skills autonomously with the
activate_skilltool.
- Fixed relative cwd double-scoping in custom sandbox connectors. Flue now applies a created agent's
cwdexactly once duringinit(), relative to the connector's provider-owned base directory.SandboxFactory.createSessionEnv()now receives only{ id }; connector implementations should stop consumingcwdthere. - SDK: Export reusable option types.
@flue/sdknow exports option types for direct agent invocation, socket prompts, workflow-run event retrieval and streaming, and admin run listing, plus theRunStatustype. - MCP tools discovered through paginated listings now preserve output-schema validation and required task-execution metadata across every page.
GET /admin/agentsandclient.admin.agents.list()return one unpaginated list of all built agents. The unusednextCursorresponse field was removed from the SDK and OpenAPI schema.- Workflow run stores no longer prune completed histories implicitly after 50 runs. Retention is now owned by the deployment or configured store.
- Cloudflare agent WebSockets now return a correlated error frame when persisted session restoration fails before a prompt.
- Cloudflare WebSocket attachments strip query strings and fragments before persistence so URL-carried handshake credentials are not retained.
- Agent and workflow WebSocket frames reject blank or whitespace-only
requestIdvalues, including optional agent ping IDs. - Published the Message-Driven Agents guide, Sandbox Connector API, and Daytona integration guide on the documentation site. Replace saved root-guide or raw GitHub links with Message-Driven Agents, Sandbox Connector API, and Daytona.
- Refreshed homepage and documentation canonical URLs and social-preview metadata.
- Cloudflare: Extend generated deployments and addressable agents. Add an optional source-root
cloudflare.tsmodule to export application-owned Durable Objects and compose non-HTTP Worker handlers. Addressable agent modules may exportcloudflare = extend({ base, wrap })from@flue/runtime/cloudflareto add native Agents SDK lifecycle hooks beneath Flue-owned routing or wrap the final generated Durable Object class with integrations such as Sentry. - Cloudflare Sandbox exports are now explicit. Export Cloudflare Sandbox aliases from your source-root
cloudflare.tsmodule instead of relying on the removedSandbox-suffix auto-wiring.
- Move application routing imports out of
@flue/runtime/app. Importflue,admin, andFetchablefrom@flue/runtime/routing. Import provider APIs andobservefrom@flue/runtime, and Workers AI binding types from@flue/runtime/cloudflare. Rename theProviderSettingstype toProviderConfiguration. - Check your authored source directory. Flue now selects exactly one source directory in priority order:
.flue/,src/, then the project root. If your project already has asrc/directory, move root-level agents and workflows into the selected source directory so Flue continues to discover them. - Cloudflare: Own Cloudflare Durable Object migrations in your project Wrangler config. Flue still generates classes and bindings, but no longer appends migrations automatically. Before upgrading an existing deployment, copy its complete ordered
flue-class-*migration history from the previously generated.flue-vite.wrangler.jsoncor builtwrangler.jsoninto the project-root Wrangler config. Keep deployed tags unchanged, and append a uniquely taggednew_sqlite_classesentry whenever you add an agent or workflow class. - Workflows: Retry interrupted Cloudflare workflows explicitly. Flue no longer starts a replacement workflow run automatically after an interruption. The interrupted run is recorded as failed; invoke the workflow again when retrying is appropriate. Restart-link fields and their legacy OpenTelemetry attributes were removed.
- Agents: Clear or migrate persisted beta session state before upgrading. Session and dispatch records from earlier beta releases are rejected rather than resumed with the new record shape. This does not apply to Flue workflows.
- Providers: Use provider IDs consistently. Model values use
provider-id/model-id.registerProvider(providerId, ...)no longer accepts a separateprovideroverride,configureProvider(providerId, ...)uses the same ID, and binding-backedcloudflare/...models now report provider IDcloudflare. Prompt responses now exposemodel: { provider, id }. - SDK: Configure SDK mount paths through
baseUrl. Its pathname is now used for public HTTP, SSE, and WebSocket routes. RemovewebsocketBasePath; keepadminBasePathonly for an independent admin mount.
- Load local environment values before configuration. Flue application commands load project-root
.envvalues automatically. Use--env <path>to select one alternate file; shell values still take precedence. - Restart
flue devafter configuration changes. Creating, editing, or deleting an auto-discoveredflue.config.*file restarts the development session with freshly resolved settings. Explicit--config <path>files are watched too. - Forward authentication headers with
flue logs. Repeat--header 'Name: value'to send application-owned headers when inspecting workflow runs. Redirects are rejected so credentials stay on the selected server. - Inspect admitted workflow runs from WebSocket clients.
WorkflowSocket.runIdresolves after admission, before the workflow result arrives. - Catch SDK HTTP failures with
FlueApiError.@flue/sdknow exports the error type with the HTTP status and parsed response body when available. - Forward Workers AI reasoning effort. Binding-backed
cloudflare/...models now pass reasoning effort toenv.AI.run(...)for models that support it.
- MCP connections now follow paginated tool listings so all server tools are available.
- Workflow run streams now avoid duplicate events during replay-to-live handoff and validate reconnect event IDs more strictly.
- Session lifecycle requests for the same name are serialized to avoid deletion races.
observe()subscribers now receive isolated event snapshots, and rejected async callbacks no longer interrupt runtime execution.- Improved Cloudflare upgrade safety, local development diagnostics, and handling for unusual session names.
- Fixed cwd scoping for created agents using Node
local()sandboxes. - Pass at most one
--envfile.flue build,flue dev,flue run, andflue connectreject repeated--envflags. Combine values into one file or use shell environment overrides. session.delete()andharness.sessions.delete()now reject while the selected session has an active operation.- Testing: Import
registerFauxProvider(...),fauxAssistantMessage(...),fauxText(...), andfauxToolCall(...)from@earendil-works/pi-aiinstead of@flue/runtime.
- OpenTelemetry tracing integration. Added
@flue/opentelemetryfor tracing Flue model turns through OpenTelemetry-compatible observability tooling.
- Reduced routine runtime console logging and expanded the published documentation and website guidance.
This is a large pre-1.0 release that establishes Flue's model for building persistent agents and finite workflows. Rather than cataloging every intermediate beta change, this entry highlights the final APIs and the most important upgrade work. For guides and API reference, see the documentation.
- Distinct agents and workflows. Files in
agents/now define persistent, addressable agent instances withcreateAgent(...); files inworkflows/define finite executions withrun(...). Agents maintain sessions across direct interactions and dispatched inputs, while workflows own persisted runs and results. - Message-driven and live application surfaces. Agents support direct HTTP prompts, asynchronous
dispatch(...), and WebSocket conversations. Workflows support HTTP and WebSocket invocation, and@flue/sdknow includes typed clients for connecting to deployed Flue applications. - Composable agent capabilities.
createAgent(...),defineAgentProfile(...),defineTool(...), and named subagents provide explicit reusable building blocks for model configuration, runtime resources, tools, skills, and delegation. - Packaged Agent Skills and Markdown imports. Applications can import
SKILL.mddependencies as validatedSkillReferencevalues, bundle their supporting files for Node or Cloudflare, and import attributed Markdown through the shared Vite build pipeline. - Observability and integrations. Public model-turn telemetry enables tracing integrations such as the new Braintrust example. This release also adds a documentation app and examples for Chat SDK, Node WebSockets, Cloudflare WebSockets, and imported skills.
- Applications must adopt the agent/workflow split. Move one-shot request/result modules from
agents/toworkflows/; long-lived agent modules now default-exportcreateAgent(...). Workflows create harnesses withinit(agent)rather than inlineinit({ ... })configuration. - Routing and run semantics changed. Public HTTP and WebSocket exposure is declared through
routeandwebsocketmiddleware exports. Runs,/runs, andflue logsnow describe workflows only; direct or dispatched agent interactions correlate by instance, session, operation, anddispatchIdinstead ofrunId. - Roles and older agent definitions were replaced. Migrate roles and
task({ role })to nameddefineAgentProfile(...)subagents andtask({ agent }); migrate reusable agent definitions to profiles andToolDefimports toToolDefinition. - Build and Cloudflare configuration changed. Node and Cloudflare builds now use a shared Vite graph; Cloudflare development follows
.dev.vars/.envandCLOUDFLARE_ENVconventions. Cloudflare workflows now receive per-workflow Durable Object bindings, so review generated Wrangler configuration when upgrading. - Cloudflare Shell is connector-owned. Install it with
flue add @cloudflare/shelland import its workspace sandbox helpers from the generated connector rather than@flue/runtime/cloudflare.
- Improved durability and retry handling for Cloudflare workflow admission and interrupted direct agent prompts, preserved authored Cloudflare environment configuration during Vite builds, and reduced Workers AI streaming parse overhead.
- Fixed model-invoked subagent task execution and expanded migrated examples and documentation for the new application model.
- Cloudflare agent route forwarding preserves the request body. Flue now forwards a cloned request into Cloudflare agent routing, preventing request body consumption from making the original request unreadable after routing.
- Cloudflare shell sandbox. Added
getShellSandbox({ workspace, loader }),getDefaultWorkspace(), andhydrateFromBucket()from@flue/runtime/cloudflare. The new sandbox wires@cloudflare/shellWorkspaces into Flue through a codemodecodetool backed by a Worker Loader binding. Agents usestate.*inside thecodetool instead of bash/read/write/grep/glob. Use@cloudflare/shelldirectly for primitives likeWorkspace,WorkspaceFileSystem, andcreateGit.
getVirtualSandbox()now throws with a migration message. The previous API described R2 as if it were mounted directly as the harness filesystem, but@cloudflare/shellWorkspaces are SQLite-indexed filesystems with optional R2 blob spillover; raw bucket keys uploaded outside Workspace were invisible. Migrate bucket-backed agents togetShellSandbox({ workspace, loader })plushydrateFromBucket(workspace, env.BUCKET)beforeinit(). If you used zero-arggetVirtualSandbox(), remove it and omitsandboxfrominit()to use Flue's default in-memory sandbox.
-
init({ cwd })with a relative path now resolves against the sandbox cwd. Previously,init({ cwd: 'relative/path' })was treated as if absolute against the sandbox root ('relative/path'→/relative/path), so agents ran in the wrong directory — potentially discovering the wrongAGENTS.md, skills, or pointing shell/file operations at unintended paths. Relativecwdvalues now resolve against the parentSessionEnv'scwd, matching the pattern already used for task sessions. Absolute paths are unchanged. Fixes #152. -
flue --config <path>resolves against the caller's cwd, not--root. The explicit--configflag was being resolved againstsearchFrom(effectively--root), contradicting the CLI help text and the config-module doc comment, and diverging from Vite/Astro behavior. Explicit--configpaths now resolve againstprocess.cwd(). Auto-discovery (no--configflag) still scanssearchFrom, so--rootcontinues to influence where the config is looked up when one wasn't named explicitly. Fixes #152. -
isBashLikeduck-check no longer acceptsfs: null. Becausetypeof null === 'object', an object like{ exec, getCwd, fs: null }slipped pastassertBashLike/isBashLikeand crashed later insidecreateBashSessionEnvon the firstfs.readFile(...)call instead of failing with the clear"BashFactory must return a Bash-like object"validation error. The check now rejectsfs: nullexplicitly, and the predicate is shared betweensandbox.tsandclient.tsso the two copies can't drift. Fixes #149.
- Runtime dependencies now use the maintained
@earendil-works/*package scope. Replaced deprecated@mariozechner/pi-aiand@mariozechner/pi-agent-coredependencies and imports with@earendil-works/pi-aiand@earendil-works/pi-agent-core, and updated the website model registry endpoint to read from the new package scope. Fixes #143.
-
Compaction tuning on
init({ compaction })and on-demandsession.compact(). Compaction (the mechanism that summarizes older messages when context approaches the window limit) is now configurable from agent code.init({ compaction: { reserveTokens, keepRecentTokens, model } })lets agents shape the headroom buffer, the verbatim tail size, and the summarization model.init({ compaction: false })disables threshold compaction entirely (overflow recovery still runs).session.compact()triggers compaction on demand for Claude-Code-style/compactUX — surfaces in the event stream ascompaction_startwithreason: 'manual'and asoperation_startwithoperationKind: 'compact'. Throws if another operation (prompt/skill/task/shell) is in flight on the session. Fixes #135, #136.// Smaller models with tighter windows init({ model: 'cloudflare/@cf/google/gemma-7b-it', compaction: { reserveTokens: 1024, keepRecentTokens: 2048 }, }); // Cheap summarizer on an expensive session model init({ model: 'anthropic/claude-opus-4-5', compaction: { model: 'anthropic/claude-haiku-4-5' }, }); // Manual compact (e.g. wired to a slash command) await session.compact();
-
local()sandbox factory for host-bound agents on Node. A new factory exported from@flue/runtime/node.init({ sandbox: local() })builds aSessionEnvthat binds directly to the host:execruns through the user's shell, file methods hit the real filesystem, andcwddefaults toprocess.cwd(). Env exposure is opt-in by design — only a small allowlist of shell essentials (PATH,HOME,USER,LOGNAME,HOSTNAME,SHELL,LANG,LC_ALL,LC_CTYPE,TZ,TERM,TMPDIR,TMP,TEMP) is inherited fromprocess.env. Anything else, including API keys and tokens, must be passed explicitly via theenvoption, which keeps host secrets out of the agent'sbashtool by default. Set a key toundefinedto drop a default; passenv: { ...process.env }to opt into the full host env.import { local } from '@flue/runtime/node'; init({ sandbox: local({ env: { GH_TOKEN: process.env.GH_TOKEN }, }), });
-
Public OpenAPI spec for Flue's built-in routes.
GET /openapi.jsonnow serves an OpenAPI 3.1 document forPOST /agents/<name>/<id>andGET /runs/<runId>{,/events,/stream}. The spec is generated from Valibot schemas viahono-openapi, includes Flue's canonical error envelope, documents SSE routes withx-flue-streaming: true, and marks agent invocation payloads as user-defined. -
Read-only admin API sub-app.
admin()is now exported from@flue/runtime/appand can be mounted by user apps with their own auth middleware, e.g.app.use('/admin/*', myAuthMiddleware); app.route('/admin', admin()). It servesGET /openapi.json,GET /agents,GET /agents/<name>/instances,GET /agents/<name>/instances/<id>/runs,GET /runs, andGET /runs/<runId>relative to the mount point. Flue ships no auth opinions; middleware order in the user's Hono app controls access. -
SDK scaffold for public and admin APIs. The
@flue/sdkworkspace package now contains a private, hand-written typed client scaffold for deployed Flue apps. It covers agent invocation modes, run lookup/events/streams, and read-only admin routes. The runtime still serves OpenAPI specs, but SDK code generation is deferred until a later pass can wire real spec snapshots and generated request methods end-to-end.
-
sandboxmagic strings removed.init({ sandbox })no longer accepts the literal strings'empty'or'local'. The TypeScript union excludes both, and the runtime throws with a migration message for JS callers /any-typed inputs.- For the default in-memory sandbox, omit the
sandboxoption entirely or passfalse. - For host-bound agents on Node, use the
local()factory from@flue/runtime/node. It also lets you opt host env vars into the sandbox vialocal({ env: { ... } }).
- init({ sandbox: 'empty', model: 'anthropic/claude-sonnet-4-6' }); + init({ model: 'anthropic/claude-sonnet-4-6' }); - init({ sandbox: 'local', model: 'anthropic/claude-sonnet-4-6' }); + import { local } from '@flue/runtime/node'; + init({ sandbox: local({ env: { GH_TOKEN: process.env.GH_TOKEN } }), model: 'anthropic/claude-sonnet-4-6' });
- For the default in-memory sandbox, omit the
-
Malformed run-event query parameters now return structured 400 errors.
GET /runs/<runId>/eventsvalidates query params before reading run history.limitmust be an integer in[1, 1000];aftermust be a non-negative integer;typesmust be a comma-separated list of known Flue event type names. Previously malformedlimit/aftervalues were silently defaulted or ignored. -
Run-lookup HTTP routes are now identified by
runIdalone. The previousGET /agents/<name>/<id>/runs/<runId>{,/events,/stream}route family is removed and replaced withGET /runs/<runId>{,/events,/stream}. The new routes work end-to-end on both Node and Cloudflare for any run that exists anywhere in the deployment — the server resolves the owning(agentName, instanceId)via a new internal run registry, so callers no longer need to know which agent or instance ran a given run id. External consumers hitting the old paths will get a 404; update to the bare form. ThePOST /agents/<name>/<id>invocation route is unchanged.- curl http://localhost:3583/agents/hello/inst-1/runs/run_01H... + curl http://localhost:3583/runs/run_01H...
-
flue logsnow takes only the run id. The CLI signature simplifies fromflue logs <agent> <id> <runId>toflue logs <runId>, matching the new route shape. The<agent>and<id>positional arguments are removed.- flue logs hello inst-1 run_01H... + flue logs run_01H...
-
Cloudflare deployments gain a new
FlueRegistryDurable Object class. Auto-injected into the generateddist/wrangler.jsoncas a SQLite-backed DO binding (FLUE_REGISTRY) and a migration entry (flue-class-FlueRegistry). New deployments include it in their initial migration; existing deployments upgrading get a single appended migration entry. No user action required — the build's wrangler-merge owns the injection. -
@flue/sdkhas been renamed to@flue/runtime. The runtime library that user agent code and the generated server depend on is now published as@flue/runtime. User-facing agent, connector, MCP, and sandbox helper APIs now import from the root@flue/runtimeentry; the old@flue/sdk/clientand@flue/sdk/sandboxsubpaths are folded into root. Platform/internal subpaths remain (@flue/runtime/app,@flue/runtime/cloudflare,@flue/runtime/node,@flue/runtime/internal). To migrate, replace user-code@flue/sdkimports with@flue/runtime. Generateddist/artifacts must be rebuilt — the new build emits@flue/runtime/*imports inserver.mjs/_entry.ts.The transitional
@flue/runtime/clientand@flue/runtime/sandboxsubpaths still resolve for now, but immediately throw with migration guidance. They will be removed in a later release.- import type { FlueContext } from '@flue/sdk/client'; + import type { FlueContext } from '@flue/runtime';
-
Build tooling (
build,dev,parseEnvFiles,resolveEnvFiles,resolveSourceRoot, the build plugins, env-file helpers) has moved from@flue/sdkto@flue/cli.@flue/runtimeis now a pure runtime library with noesbuild/typescript/wranglerbaggage. Thewranglerpeer dependency moved with it and is now on@flue/cli. If you were driving the build programmatically viaimport { build } from '@flue/sdk', update to import from@flue/cli(currently via internal paths; a stable public API will land separately). -
flue.config.tsnow importsdefineConfigfrom@flue/cli/config. Update existing configs:- import { defineConfig } from '@flue/sdk/config'; + import { defineConfig } from '@flue/cli/config';
This sets up the eventual collapse to
import { defineConfig } from 'flue/config'(matching Astro/Vite).flue initnow scaffolds the new import. The@flue/sdk/configsubpath no longer exists. -
The
@flue/sdkpackage is now a migration placeholder. It keeps publishing with the old export map (.,./app,./client,./sandbox,./internal,./cloudflare,./node,./config) but has no runtime dependencies and every import throws with migration guidance. This prevents old installs from silently staying on an obsolete package while reserving the name for a future client-side SDK for talking to deployed Flue applications (send agent interactions, invoke or inspect workflow runs, stream events, etc.).
-
Structured output options use
resultagain. Theschemaoption onprompt()/skill()/task()made it unclear whether the schema described input or output, especially next toskill({ args }). Useresult: <schema>for structured output going forward. Theschemaoption remains accepted at runtime for backwards compatibility, but is deprecated in TypeScript and will be removed in a future release. Structured calls still return{ data, usage, model }; the response field alias{ result }remains deprecated in favor of{ data }. -
Compaction defaults are now model-aware. Previously every session used flat
reserveTokens: 16384andkeepRecentTokens: 20000, which were calibrated for Sonnet-class 200k windows but broke on small-window models (Gemma, Llama-3.1-8B at 8–16k windows): the reserve exceeded the window, so threshold compaction misfired on every turn, andkeepRecentTokensexceeded the window entirely soprepareCompactioncould never find a valid cut point. Defaults are now derived from the model's metadata:reserveTokens = min(20_000, model.maxTokens)capped further when it would exceed half the contextWindow, andkeepRecentTokens = 8000(matching the convention used by OpenCode and similar agents — recent-context fidelity doesn't scale with window size). Effect on existing Sonnet/Kimi-class sessions: marginally different trigger points and a smaller verbatim tail (8k vs 20k). Effect on small-window sessions: compaction actually works. -
cloudflare/<model>resolutions now carry realcontextWindow,maxTokens,cost,reasoning, andinputmetadata. Previously the binding branch ofbuildModelFromRegistrationsynthesized a model from scratch withcontextWindow: 0, which madeshouldCompactevaluatecontextTokens > 0 - reserveTokensas true on every turn after the first — spamming[flue:compaction] Threshold reached — window 0and running no-op compaction prep on every turn. Resolution now hydrates from pi-ai'scloudflare-workers-aicatalog when the model id is known. Uncatalogued ids (embeddings, image-gen, anything outside pi-ai's chat-completion subset of Workers AI) fall back to zero metadata, andshouldCompactnow treatscontextWindow <= 0as unknown and skips the threshold check — overflow recovery still runs. Fixes #132. -
registerProvider(...)now acceptscontextWindow,maxTokens, and per-model overrides for HTTP providers. Registered HTTP providers (litellm, openrouter, vLLM, custom OpenAI-compatible proxies, etc.) had no way to declare model metadata, so resolved models hardcodedcontextWindow: 0andmaxTokens: 0— same bug class as #132 on the binding side. Now the registration accepts provider-level defaults (contextWindow,maxTokens) and amodels: Record<string, { contextWindow?, maxTokens? }>map for per-model overrides. Per-model overrides win over provider-level defaults; unset stays0, whichshouldCompacttreats as unknown.registerProvider('litellm', { api: 'openai-completions', baseUrl: 'http://localhost:4000/v1', contextWindow: 128000, maxTokens: 16000, models: { 'gpt-4o-mini': { contextWindow: 128000, maxTokens: 16384 }, }, });
-
observe(...)exported from@flue/sdk/appfor isolate-global subscriptions to the Flue event stream. Cross-cutting integrations — error reporting, log forwarding, metrics — can now tap every Flue event in the current isolate from a single module-scoped call, without per-agent or per-context wiring. The subscriber receives the fully decoratedFlueEvent(withrunId,eventIndex,timestamp, and tree-correlation fields) and the originatingFlueContext. On the Cloudflare target each Durable Object is its own V8 isolate, soapp.ts(and thus theobserveregistration) is evaluated per-DO — each isolate captures its own events independently, which is the intended shape. Seeexamples/sentry/for a fully documented Sentry error-reporting integration built on top of this hook.// app.ts import { flue, observe } from '@flue/sdk/app'; import * as Sentry from '@sentry/node'; Sentry.init({ dsn: process.env.SENTRY_DSN }); observe((event, ctx) => { if (event.type === 'run_end' && event.isError) { Sentry.captureException(event.error); } });
-
Cloudflare AI Gateway is now enabled by default on the Cloudflare target. Every
cloudflare/...model call passesgateway: { id: 'default' }toenv.AI.run(...), which the Workers AI binding spins up on demand for the account. No setup required — you get caching, logs, and budget controls in the Cloudflare dashboard out of the box. Existing zero-config agents pick this up automatically on rebuild. -
Customize or opt out of the AI Gateway from
app.ts. Re-register thecloudflareprefix with agatewayfield to target a named gateway and tune its options (id,cacheTtl,cacheKey,skipCache,metadata,collectLog,eventId,requestTimeoutMs). Passgateway: falseto disable the gateway entirely. User registrations always win over the auto-registered default.// app.ts import { registerProvider } from '@flue/sdk/app'; import { env } from 'cloudflare:workers'; registerProvider('cloudflare', { api: 'cloudflare-ai-binding', binding: env.AI, gateway: { id: 'my-gateway', cacheTtl: 3360 }, });
See https://developers.cloudflare.com/ai-gateway/integrations/worker-binding-methods/ for the full options reference.
- Sessions now forward a stable affinity key to pi-ai as
sessionId. Derived from the(instanceId, harnessName, sessionName)triple as<instanceId>::<harnessName>::<sessionName>, this key is forwarded by pi-ai to providers that support session-aware prompt caching and routing (Anthropic, OpenAI Responses, OpenAI Codex, Workers AI viax-session-affinity, and others). Stable across runs of the same triple, distinct across different ones. Child task sessions get their own key automatically because their session name istask:<parent>:<taskId>.
FlueAgentis nowFlueHarness. The value returned frominit()is a harness: a configured handle for model defaults, tools, sandbox, filesystem, and sessions. Rename imports/usages fromFlueAgenttoFlueHarness, and preferconst harness = await init(...)in agent files.- Harnesses and sessions are named, not id'd.
init({ id })is nowinit({ name }), defaulting to"default". The returned harness exposes.nameinstead of.id.harness.session(id?),harness.sessions.get/create/delete(id?), andFlueSession.idare now name-based APIs (name?,.name). - Session storage keys now include the agent instance id, harness name, and session name. Existing persisted sessions under the old two-part key shape are not migrated. Cloudflare Durable Object session history from earlier builds will not be read by this release.
- Webhook responses return
runIdinstead ofrequestId. Every HTTP invocation now gets a generatedrun_<ulid>exposed to handlers asctx.runId. Webhook mode returns{ status: 'accepted', runId }. - The event vocabulary changed for run observability.
tool_endis nowtool_call,operation_endis nowoperation, and session correlation fields useharness,session, andparentSessioninstead of the previous id-oriented names. Consumers of the rawFlueEventstream should update event-type checks and field names. - SSE
event: resultwas removed. Terminal result/error state is now delivered by the widerun_endevent. Sync responses still return{ result, _meta: { runId } }.
- Run history and durable event logs. Every invocation is recorded as a run with
run_start/run_endlifecycle events and a monotoniceventIndex. Cloudflare persists run history in the Agent Durable Object SQLite storage; Node keeps an in-memory ring buffer of recent completed runs. - Run-scoped HTTP endpoints. New read-only endpoints expose a known run:
GET /agents/<name>/<id>/runs/<runId>,GET /agents/<name>/<id>/runs/<runId>/events, andGET /agents/<name>/<id>/runs/<runId>/stream. There is intentionally no list-runs endpoint yet; broader run discovery remains admin-API territory. - Reconnectable live run streams.
/runs/<runId>/streamreplays durable history and then tails active runs. It honors standardLast-Event-IDresume semantics and closes whenrun_endis observed. flue logscommand.flue logs <agent> <id> <runId>replays or tails a known run from a running Flue dev server. It supports--follow/--no-follow,--since,--types,--limit, and--format pretty|json|ndjson.- Structured handler logs. Handlers can call
ctx.log.info(...),ctx.log.warn(...), andctx.log.error(...)to emit structuredlogevents into the run event stream and persisted history. flue runsurfaces run ids. One-shot runs now print the generated run id to stderr and include_meta.runIdin sync responses, making it easier to inspect the same run withflue logs.
- Run lifecycle ordering is durable-before-live. Terminal
run_endevents are appended before live subscribers are notified and before the run is marked terminal, avoiding missed terminal events for clients connecting near completion. - Live event fan-out is ordered per run. Durable writes are serialized before publishing each non-terminal event to live subscribers.
- SSE streams now use a shared 15s heartbeat. Both direct agent SSE responses and run-history streams emit heartbeats to avoid idle proxy/client timeouts.
- Cloudflare run-route parsing is positional. An agent instance id of
"runs"no longer collides with the/runsroute marker. - Generated docs and examples were updated for the harness terminology and new run observability APIs.
session.shell()now redactsenvvalues in transcript history. When you pass per-call environment variables tosession.shell(cmd, { env }), the keys still appear in the recorded tool-call arguments — so the model can reason about which variables were set on a later turn — but the values are replaced with<redacted>. The real values are still passed toenv.exec(), so the command itself runs with the actual environment. This prevents API keys and other secrets from leaking into session storage.
Big release! We are working hard to stabilize our APIs and add any missing and essential features to Flue that you need. There are some breaking changes to be aware of, when upgrading from v0.3 to v0.4. Read through the list below to understand what's new and what's changed. Or, point your coding agent to this changelog URL for a more automated upgrade experience).
-
New return type for
prompt()/skill()/task()/shell(). Two changes folded into one new shape:- They now return a
CallHandle<T>instead of aPromise<T>.awaitworks exactly as before. The handle is aPromiseLikewith.signal: AbortSignaland.abort(reason?)for synchronous cancellation, replacing the removedPromptOptions.timeout/SkillOptions.timeout/ShellOptions.timeoutfields. Code that uses these as plain Promises withoutawait(e.g. raw.then()/.catch()chains) may need adjustment.
// Cancel via an AbortSignal on the options bag const result = await session.prompt('…', { signal: AbortSignal.timeout(5000) }); // Or abort the handle directly const handle = session.prompt('…'); setTimeout(() => handle.abort('user cancelled'), 5000);
- The awaited value is now
{ text | data, usage, model }instead of a bare string or schema value. Schema-typed calls returnPromptResultResponse<T>; non-schema calls returnPromptResponsewith the newusageandmodelfields. To migrate, readresponse.textorresponse.data:
// Before const text = await session.prompt('…'); const user = await session.prompt('…', { result: UserSchema }); // After const { text } = await session.prompt('…'); const { data: user } = await session.prompt('…', { result: UserSchema });
Structured results use the
resultoption and return validated data onresponse.data.Schema results are now extracted via injected
finish/give_upmodel-facing tools instead of---RESULT_START---/---RESULT_END---text markers. The unusedResultExtractionErrorclass is removed; a newResultUnavailableErroris thrown when the model invokesgive_up. - They now return a
-
commandsanddefineCommandare removed. The original idea — register first-party CLI tools the agent could shell out to — only worked under just-bash and saw little real use. The same surface is better expressed today by passingenvto scope what a connector sees, or by choosing a sandbox connector that gives you the isolation you want. just-bash itself still supports custom commands — you just register them on your bash instance directly instead of through Flue's helper. Removed: thecommands?:option oninit()/prompt()/skill()/task()/shell(); theCommand,CommandDef,CommandOptions,CommandExecutor,CommandExecutorResulttypes; thedefineCommandexport from@flue/sdk/nodeand@flue/sdk/cloudflare; thecommand_start/command_endFlueEventvariants; and theBashLike.registerCommand/SessionEnv.scopeconnector hooks. -
Default
thinkingLevelchanged from'off'to'medium'. Reasoning-capable models (e.g. gpt-5, claude-opus-4-7) will now reason by default on everyprompt()/skill()/task()call. Non-reasoning models are unaffected (clamped to'off'per the model'sthinkingLevelMap). To restore the old behavior, setthinkingLevel: 'off'explicitly oninit(), your role frontmatter, or the call options. -
sandbox: 'local'now runs locally. Originally,'local'was a half-isolated layer — ajust-bashsubprocess with aReadWriteFs/MountableFsoverlay mountingprocess.cwd()at/workspace. That made sense when every Flue agent ran on a developer laptop, but increasingly people are deploying the agent itself inside a real sandbox (a container, a microVM, a Cloudflare Sandbox), where wrapping the host in a second virtual filesystem is pure overhead — and actively confusing, because paths get remapped twice.sandbox: 'local'now binds directly to the host:execruns through the user's shell with fullprocess.env, file methods hit the real filesystem, defaultcwdisprocess.cwd(), and there are no path remappings or command restrictions. Agents that hard-coded/workspacepaths must migrate to real host paths. If you want isolation on a developer laptop, reach for a real sandbox connector (Daytona, E2B, Mirage, etc.). -
commands/defineCommandremoved. ThecommandsAPI let you register user CLIs (gh,npm, etc.) into a sandbox-scoped$PATH, isolating secrets from the model. In practice it only ever worked when the sandbox was aBashFactory(the default in-memory sandbox orgetVirtualSandboxon Cloudflare), and threw a runtime error on'local', every remote connector (Daytona, E2B, Mirage, etc.), and Cloudflare Containers — so the documented "CI agent withdefineCommand('gh', { env: { GH_TOKEN } })" pattern has been broken for most users since'local'was rebuilt. We're collapsing the API:- With the new
'local'sandbox, the host shell is exposed directly. The agent'sbashtool can rungh issue view,npm test, etc. with whatever's on$PATHand whatever env you launched flue with. The runner / container / VM is the isolation boundary. - For non-
'local'sandboxes, install the binaries inside the sandbox image, or wrap the operation as a custom tool withinit({ tools: [...] }). Tools have a structured parameter schema, are visible to the model directly, and recover the "secrets stay on the host" property — the tool readsprocess.env, the agent only sees the tool's params and result.
Removed:
Command,CommandDef,CommandOptions,CommandExecutor,CommandExecutorResulttypes;defineCommandfrom@flue/sdk/nodeand@flue/sdk/cloudflare;commands?:field oninit(),prompt(),skill(),task(),shell();BashLike.registerCommand?,SessionEnv.scope?; the deadcommand_start/command_endFlueEventvariants. - With the new
-
init({ providers: { … } })has moved. Provider configuration moved to the newapp.tsruntime registration model (see below). Migrate by creating anapp.tsat your project root and callingconfigureProvider()(to patch a built-in catalog provider'sbaseUrl/apiKey/headers/storeResponses) orregisterProvider()(to register a brand-new URL-prefix provider). Both are exported from@flue/sdk/app. TheProvidersConfigtype andprovidersfield are removed fromAgentInitandAgentConfig. -
FlueAgent.destroy()andSessionEnv.cleanup()are removed. Flue no longer manages sandbox lifetime — sandboxes are user-owned. Connectors that previously took acleanupoption (Boxd, Daytona, E2B, Exedev, islo, Modal, Vercel) no longer accept it; some lose their options argument entirely (e.g.daytona(sandbox)instead ofdaytona(sandbox, { cleanup })). If you were relying on automatic teardown, destroy your sandbox explicitly when your handler is done. -
CLI
--workspaceflag renamed to--rootacrossflue dev/flue run/flue build. The corresponding programmatic options also moved:BuildOptions.workspaceDir→BuildOptions.root,BuildContext.workspaceDir→BuildContext.root. Theflue.config.tskey isroot, notworkspace. -
outputDirrenamed tooutputacrossBuildOptions,BuildContext, andDevOptions. Build plugin authors readingctx.outputDirmust update toctx.output. The CLI flag remains--output. The default is<root>/dist, and--outputis now the literal output directory (previously it was a parent directory into whichdist/was written).BuildOptions.outputis now optional. -
Built-in
/healthand/agentsHTTP endpoints removed. Projects that need them must author the routes inapp.ts.flue devandflue runno longer probe/health;flue runretries SSE POST onECONNREFUSEDfor ~5s instead. -
Skill.instructionsfield removed from the public type. Skill bodies are no longer cached in memory — at call time the model readsSKILL.mdfrom disk via its filesystem tools. This means relative references inside a skill resolve correctly, and edits are picked up mid-session without re-init. If you were readingskill.instructionsfrom the SDK types, read the file from disk yourself. -
Sandbox connector contract:
SandboxApi.execis now timeout-primary, signal-optional. Connectors are expected to forwardtimeoutto their provider's native timeout option (E2BtimeoutMs, Daytonatimeout, etc.); signal-aware SDKs may additionally forwardsignalfor true mid-flight cancellation.BashLike.execoptions gainedsignal?: AbortSignal. If you maintain a sandbox connector, see Sandbox Connector API for the dual contract. -
Long-running agents on Node no longer time out at ~300s. The generated Node server now sets
requestTimeout: 0on the underlyinghttp.Serverand emits a 25s SSE heartbeat, which keeps undici'sbodyTimeoutand reverse-proxy idle timers satisfied. Multi-minutebashcalls and other long handlers that emit no Flue-level events for >300s no longer abort with[flue] Agent error: terminated.
-
flue.config.tsproject config. Aflue.config.{ts,mts,mjs,js,cjs,cts}file at the project root is auto-discovered and can settarget('node' | 'cloudflare'),root, andoutput. CLI flags still win per-field. Authored in TypeScript via Node's native type-stripping (no bundling). New--config <path>flag onflue dev/flue run/flue build. New@flue/sdk/configsubpath export withdefineConfig,resolveConfig,resolveConfigPath,UserFlueConfig,FlueConfig,ResolveConfigOptions,ResolvedConfigResult.// flue.config.ts import { defineConfig } from '@flue/sdk/config'; export default defineConfig({ target: 'cloudflare', });
-
flue initcommand scaffolds a starterflue.config.tsin the target directory. Flags:--target <node|cloudflare>(required),--root <path>,--force(overwrite existing). -
app.tsruntime entry point. A new optionalapp.ts(also.mts/.js/.mjs) at the source root lets you take over the request pipeline with custom Hono middleware, routes, auth, etc. Mount Flue's agent handler viaapp.route('/', flue()). New@flue/sdk/appsubpath export ships:flue()— Hono sub-app exposing/agents/:name/:id.Fetchable— type for the user app's default export.registerProvider(name, def)— register a new URL-prefix model provider at runtime, with platformenvin scope. Supports HTTP and Cloudflare AI binding registrations (HttpProviderRegistration,CloudflareAIBindingRegistration,CloudflareAIBinding).registerApiProvider— re-exported from pi-ai for entirely new wire protocols.configureProvider(slug, settings)— patchbaseUrl/apiKey/headers/storeResponseson an existing pi-ai catalog provider or previously registered prefix.
-
.flue/-as-source layout. When<root>/.flue/exists, source files (agents/,roles/, optionalapp.ts) are read from there; otherwise from<root>/directly..flue/wins unconditionally if present. -
AbortSignal cancellation across
prompt()/skill()/task()/shell(). Passsignal: AbortSignal(e.g.AbortSignal.timeout(5000)) on the options bag, or use the newCallHandle.abort(reason?)method on the returned handle. Aborts reject with a standardDOMExceptionnamedAbortErrorwhosecauseis the signal's reason. Aborting aprompt()also tears down in-flightbashtool commands, not just the model loop.SessionEnv.exec()also acceptssignal?alongsidetimeout?. -
Per-call reasoning effort. New
thinkingLevel?: ThinkingLevelonAgentInit,Role(also via role frontmatterthinkingLevel:),PromptOptions,SkillOptions,TaskOptions, andAgentConfig. Precedence: per-call > role > agent. Tasks inherit the parent's resolved level. Per-call'off'is rejected (init/role/agent-level only). Unknown values in role frontmatter throw at build time.ThinkingLevelre-exported from@flue/sdkand@flue/sdk/client. A single deployment can now serve a cheap classifier at'low'and a careful auditor at'high'from the same model entry. -
Images on
prompt()/skill()/task(). Newimages?: PromptImage[]option on all three (and the initial turn oftask()).PromptImageis the shape{ type: 'image', data: base64, mimeType }, re-exported from pi-ai. Requires a vision-capable model. For schema-result calls, images are attached on the first attempt only; retries are text-only. -
Token + cost usage on every response.
PromptResponse(and the newPromptResultResponse<T>) now includeusage: PromptUsageandmodel: PromptModel.PromptUsageaggregates across every LLM call dispatched by a single invocation — assistant turns, schema-result retries, the 1–2 compaction summarization calls, and the post-compaction overflow retry. Fields:input,output,cacheRead,cacheWrite,totalTokens, plus acostbreakdown (input,output,cacheRead,cacheWrite,total).PromptModel.idreflects the model Flue selected via call > role > agent precedence. -
Thinking events on the SSE stream. Three new
FlueEventvariants:thinking_start,thinking_delta(withdelta: string), andthinking_end(withcontent: string).flue runrenders them as dimmed lines under athinking:startmarker. -
fssurface onFlueAgentandFlueSession. Out-of-band sandbox filesystem access that doesn't appear in the conversation transcript — useful for staging inputs and reading back outputs around aprompt()call. Methods:readFile,readFileBuffer,writeFile(string orUint8Array),stat,readdir,exists,mkdir({ recursive? }),rm({ recursive?, force? }). Paths resolve relative to the agent'scwd. -
ctx.req: Request | undefinedonFlueContext. Standard FetchRequestfor the current invocation — read headers (req.headers.get('authorization')), method, URL, and the raw body viareq.text()/req.json()/req.arrayBuffer()/req.formData(). Body is preserved for handlers (Flue's internal JSON parser consumes a clone), so HMAC signature verification over raw bytes works directly withoutreq.clone(). Undefined when an agent is invoked outside an HTTP context. -
Cloudflare Workers AI binding provider. Models prefixed
cloudflare/<model-id>route throughenv.AI.run()on the Cloudflare target with no API tokens — the only setup is"ai": { "binding": "AI" }inwrangler.jsonc. Works across role models, sub-tasks, and compaction. Hard error on--target nodepointing users at pi-ai's URL-based providers. -
ProviderConfiguration.storeResponses?: booleanopt-in. When enabled, setsstore: trueon outgoing requests foropenai-responsesandazure-openai-responses, enabling multi-turn against reasoning models whenthinkingLevel: 'off'is explicitly set. (Codex Responses intentionally excluded — it rejectsstore: true.) -
session.shell()is now a first-class transcript citizen. It emits the sametool_start/tool_endevents as an LLM-issuedbashcall (sharedtoolCallId,toolName: 'bash') and appends a user / assistant tool-use / toolResult triple to history. Per-callcwdandenvoverrides are preserved in the synthetic tool-callargumentsso they remain visible to the model on subsequent turns. Aborted commands now produce atoolResultwithisError: trueand the error message as text (previously dropped silently). -
Skills are now read from disk on demand.
session.skill()references the skill by name and the system prompt's "Available Skills" registry tells the model where to find it (.agents/skills/<name>/SKILL.md). Relative references inside a skill (sibling markdown files, scripts) now resolve from where they live, and edits toSKILL.mdare picked up mid-session without re-init. Path-based references produce a distinct prompt naming the file path explicitly. -
createLocalSessionEnv()helper exported from@flue/sdk/node. A pure-NodeSessionEnvbacked directly bynode:fs/promisesandnode:child_process. ConfigurablecwdviaLocalSessionEnvOptions.exechonorstimeout+signaland lifts the default output buffer cap to 64 MB. This is what powers the newsandbox: 'local'behavior.