Floret is a reusable Go engine for interactive AI agents. It owns the model loop and the complete admitted Agent lifecycle: canonical messages, threads, turns, runs, tools, approvals, todos, artifacts, context, SubAgents, recovery, provider state, prompt cache, and observable execution facts.
The host application owns product UI, credentials, provider profiles, resource authorization, routing, read state, uploads before admission, and transport diagnostics. It must not persist or rebuild a second queryable Agent lifecycle.
Floret is not a graph workflow framework, a multi-agent orchestrator, or a product persistence layer.
go get github.com/floegence/floret/v6@v6.0.1Production integrations must resolve the published module. Do not use a local
replace, go.work, or sibling repository path. Earlier major versions remain
available only from their published tags; v6 does not restore retired facades.
Every Agent uses one explicit provider.Gateway. Floret allocates durable
thread, turn, and run identities; an application supplies only a stable
identity.LogicalRequestID for each logical mutation. This production example
uses the official OpenAI-compatible Gateway and SQLite:
package main
import (
"context"
"os"
"github.com/floegence/floret/v6/config"
"github.com/floegence/floret/v6/provider"
"github.com/floegence/floret/v6/runtime"
"github.com/floegence/floret/v6/storage"
)
func main() {
ctx := context.Background()
gateway, err := provider.NewOpenAICompatible(provider.OpenAICompatibleOptions{
Provider: "openai", Model: "gpt-4.1-mini",
BaseURL: "https://api.openai.com/v1", APIKey: os.Getenv("OPENAI_API_KEY"),
StateCompatibilityKey: "openai:gpt-4.1-mini:chat-completions:v1",
Capabilities: provider.Capabilities{
Reasoning: provider.ReasoningUnsupported,
AttachmentPayload: provider.AttachmentDescriptors,
},
})
if err != nil { panic(err) }
agent, err := runtime.NewAgent(config.AgentConfig{
Profile: config.AgentProfile{ID: "assistant", Name: "Assistant"},
SystemPrompt: "Answer clearly and concisely.",
Context: config.ContextPolicy{ContextWindowTokens: config.DefaultContextWindowTokens},
}, gateway)
if err != nil {
panic(err)
}
host, err := runtime.Open(ctx, runtime.Options{Storage: storage.SQLite("floret.db")})
if err != nil {
panic(err)
}
defer func() {
if err := host.Shutdown(context.Background()); err != nil {
panic(err)
}
}()
service, err := host.ThreadService(runtime.AgentFactoryFunc(func(context.Context, runtime.AgentRequest) (*runtime.Agent, error) {
return agent, nil
}))
if err != nil {
panic(err)
}
created, err := service.Create(ctx, runtime.CreateThreadInput{RequestKey: "create-conversation-42"})
if err != nil {
panic(err)
}
_, err = service.Send(ctx, runtime.SendInput{ThreadID: created.ThreadID, RequestKey: "send-message-42", Input: runtime.UserInput{Text: "Hello"}})
if err != nil {
panic(err)
}
}Hosts may also set runtime.SendInput.SupplementalContext for material that is
needed only by the current provider turn. Floret validates and renders that
context for the provider without adding a second canonical conversation
message.
Run it with OPENAI_API_KEY=... go run ./cmd/examples/openai-sqlite. The complete
example also reads the authoritative assistant projection. florettest remains
test-only; use it for deterministic provider scripts and florettest.NewIDSource
for deterministic lifecycle identities.
| Package | Responsibility |
|---|---|
identity |
Thread, turn, run, prompt-scope, trace, logical-request, and artifact identities |
config |
Provider-neutral Agent profile, prompt, context, and reasoning policy |
runtime |
Immutable Agent construction, durable Host lifecycle, commands, queries, and subscriptions |
observation |
Sanitized runtime events and host-facing projections |
tools |
Local tool definitions, permissions, resources, effects, and results |
tools/webfetch |
Secure public-text HTTP/HTTPS fetch tool with fixed network and output limits |
provider |
Model Gateway contract and official provider constructors |
storage |
Opaque storage values and official memory and SQLite constructors |
storage/spi |
Advanced physical storage implementation contract |
florettest |
Scripted gateways and public conformance suites for tests only |
Ordinary applications use identity, config, runtime, observation,
tools, the official provider constructors, and opaque storage.Source
values. Custom provider transports and physical storage implementations are
advanced integration surfaces with separate conformance suites. Downstream
code must never import internal/*.
Tool definitions may provide InvalidActivity when a host needs to preserve a
safe display label from a parseable JSON object that fails the input schema.
Floret uses that callback only for presentation: the invalid invocation still
fails closed before permission, effect dispatch, and handler execution.
Question activity may include host-authored answer summaries for completed
prompts. A secret answer is represented only by Redacted: true; Floret
rejects a redacted answer that also carries values. These fields are display
data, not an alternate input-response or durable message authority.
Structured activity may include bounded, ordered Rows containing
host-sanitized text, Markdown, or code. Floret validates and preserves these
display rows without interpreting product tools or accepting arbitrary JSON
payloads.
Activity presentation is cumulative for one tool invocation. A result may add terminal status and output, while non-empty display facts from the matching tool call remain available in events, canonical views, and reopened threads.
SubAgent management activity uses a dedicated operation payload that preserves the exact action, ordered child targets, and bounded outcome counts. The existing single-SubAgent payload remains the durable child-thread fact; hosts do not need to infer management actions from labels or collapse multi-child results.
tools/webfetch.New supplies the product-neutral web_fetch implementation.
It performs GET-only public HTTP/HTTPS reads, revalidates redirects, DNS, and
dial targets, rejects non-text bodies, and returns Markdown or text under fixed
limits. Its typed Activity carries the requested URL, response metadata, a
2,000-character preview; complete content remains in the tool result and
artifact. It does not discover or request page icons. Hosts own static tool
iconography, visibility, current product permission policy, and UI.
Authentication, custom headers, non-GET requests, binary downloads, and browser
rendering remain separate host capabilities.
runtime.Host belongs in the composition root. Host.ThreadService returns the
single typed lifecycle boundary. Its Create, Fork, Delete, SetTitle,
List, View, History, Send, Respond, Cancel, Retry,
queue, import, and Subscribe methods all operate on stable thread and request
identities. Child agents are ordinary child threads with explicit parent
identity, so they use the same current-view and command contracts.
Each thread has one in-memory runtime owner. Send first commits canonical turn
acceptance, then publishes and returns the user item and active current view
before provider dispatch. The canonical journal is the only durable fact
source. Provider and tool I/O execute outside the thread lock.
Cancel is idempotent for every known thread. It commits the terminal turn
before returning, releases pending interactions, and fences late provider or
tool output without waiting for those goroutines to exit. If an irreversible
effect outcome cannot be confirmed, Floret atomically fails the turn with
effect_outcome_unknown, closes every unfinished tool and interaction, clears
provider continuation, and never replays the effect.
Respond resolves the matching approval or input interaction in place.
runtime.NewAgent snapshots the resolved Agent profile, system prompt,
Gateway, tools, capabilities, reasoning policy, and execution policy. The
effective snapshot and continuation state used by each run are Floret-owned
durable facts. Provider credentials and editable profile sources remain in the
host.
Current views contain one Floret-ordered sequence of directly renderable user,
thinking, assistant, tool, and interaction items, plus pending interactions and
the accepted queue. Each item has a stable ID and ordinal; live deltas grow the
same item in place, and tool approval, dispatch, and result state update the
original tool item. Canonical reload derives the same sequence without a
presentation ledger or draft mirror fields.
Every item and interaction carries its exact TurnID and RunID, so multi-turn
history and same-turn continuation never borrow identity from the current run.
ThreadView.RunID identifies only the current execution; hosts must never use
it to fill historical items. RunProgress is the actor-owned, process-local phase
for an advancing run and is absent while awaiting interaction or after the run
settles.
ViewVersion is process-local notification ordering, not a durable replay
cursor. Production hosts leave runtime.Options.IDSource nil; deterministic
identity injection belongs to florettest.NewIDSource.
ThreadService.View returns one complete replaceable current view.
The value returned by Host.ThreadService also implements
ThreadContextReader; Context returns Floret's canonical context policy,
usage, and one latest lifecycle record per compaction operation, including
terminal state restored after runtime restart. The snapshot also exposes
conversation-wide disjoint input, output, cache-read, and cache-write totals
folded from canonical final provider usage records.
Each successfully committed final provider_usage runtime event also carries
ThreadUsageTotals. It is the live form of the same canonical fold; projected
requests, stream-only usage, rejected attempts, and failed writes omit it.
ThreadService.Subscribe publishes workspace summary and current-view updates;
reconnecting clients refresh summaries and the currently visible view. There is
no durable cursor, replay ledger, materialized projection, or second lifecycle
authority.
For ordinary hosts, storage.Source is an opaque value consumed exclusively by
runtime.Open:
runtime.Open(ctx, runtime.Options{Storage: storage.Memory()})
runtime.Open(ctx, runtime.Options{Storage: storage.SQLite("agent.db")})Applications cannot use a Source as a lifecycle query path. Teams implementing
a physical backend use the advanced storage/spi contracts and their
conformance suite. SPI records remain opaque Floret data; a backend must not
decode them into a second Agent model. Memory, SQLite, and third-party backends
all run the same Floret-owned domain kernel.
New SQLite stores use incremental auto-vacuum so deleted pages can be reclaimed
without rebuilding the database. A host that owns an older SQLite file may call
storage.MaintainSQLite before runtime.Open. The maintenance boundary checks
the exact Floret physical schema and database integrity, refuses an open
runtime, and uses SQLite's native VACUUM or incremental_vacuum; it never
copies records or exposes their contents.
Floret exclusively owns admitted messages and references, thread/turn/run lifecycle, titles, approvals, todos, tool invocation and outcome, pending-work settlement, artifacts, control signals, context and compaction, provider ledgers and state, prompt cache, SubAgent hierarchy, and Activity projections.
Hosts may persist product authorization and audit, routing, credentials,
editable persona sources, resource catalogs, read state, unadmitted commands,
upload staging, and transport diagnostics. Those records must not contain a
serialized Floret DTO or support reconstruction of Agent state. Canonical
message references are opaque durable facts; rich material needed only for the
current provider turn belongs in SupplementalContext and never becomes
conversation history or continuation state.
Host.Shutdown(ctx) stops admission, cancels Host-managed provider and tool
execution, waits for it to finish, and then closes storage. If the context
expires, Shutdown returns ctx.Err() and Host remains closing; a later call
continues waiting. After completion, every retained handle returns
ErrHostClosed.
GOWORK=off go test ./...
GOWORK=off go vet ./...
GOWORK=off go test -race ./...
scripts/check_candidate_release_adoption.shRepository workflow and compatibility rules are defined in AGENTS.md. Architecture and maintenance knowledge lives in okf/.