ToolLoopAgent with FrogBot model resolution, request context, access, and hooks. Register code-defined AgentConfig objects in the root configuration.
Define one
Provide a URL-safeslug and instructions. Set model to a provider-prefixed model ID or router alias for a single choice, use the object form for selectable models, or omit it to use ai.defaultModel.
Agent options
Model options
Use
model: { default, options } to offer model choices. Omit default to use ai.defaultModel; if neither is set, startup fails. The default is always included first, and duplicate IDs are removed. An empty options: [] offers only the default, as does a string model.
A provider’s models list controls what the app can use; an agent’s model.options controls what users can pick for that agent. options: '*' includes every configured chat-capable model:
- For a built-in provider with a
modelslist, only chat models from that list. - For a built-in provider without a
modelslist, every chat model in that provider’s catalog. - For a custom provider, entries declared with
mode: 'chat'. - For configured routers, aliases whose target is chat-capable.
models list, without copying that list:
'*' with ['bedrock/amazon.nova-lite-v1:0', 'bedrock/us.anthropic.claude-sonnet-4-6']. You do not need to repeat the default in that array.
Put a models list on OpenRouter and Vercel AI Gateway provider entries to keep the picker focused. Without a list, '*' includes their entire chat catalogs, which can contain hundreds of models.
FrogBot validates the default and every explicit option. Unknown models or IDs outside a provider’s models list fail startup with is not configured; configured non-chat models fail with is not chat-capable. frogbot dev and type generation warn for these model checks instead of throwing. ai.access.generate further filters the choices each user sees and may use, for example through the AI policy plugin.
allowModels has been removed. Configs that still set it fail with a message naming the agent and showing the replacement. Migrate model: 'provider/id', allowModels: [...] to model: { default: 'provider/id', options: [...] }.
The chat composer shows the active model name and its reasoning level, for example gpt-5 · High. Clicking it opens a reasoning slider for that model, and the trigger reads Select effort; clicking the model name inside switches to the list of allowed models, and the trigger reads Select model. A model without reasoning options opens straight on the list. The slider starts at Default, which leaves the level to the provider, and offers only the options that model supports. The selector is hidden when the agent allows one model and that model has no reasoning options.
FrogBot saves the chosen model as a user preference and remembers one reasoning level per model. Switching models restores the level last used with the new model, or Default if it has none; levels never carry over from one model to another. A saved model or level that is no longer allowed or offered falls back to the agent’s default model or to Default. Opening an existing chat starts from the model and level of its latest sent message instead of the saved preference, as long as that model is still allowed.
Use the general preset
general() provides FrogBot’s default general-purpose instructions and the general slug. It sets model: { options: '*' }, offering every configured chat model with ai.defaultModel as the default, subject to ai.access.generate. Configure that default and its provider. The chat composer shows a model picker whenever more than one model is available.
general slug is fixed, but options such as tools, profile, and access can be replaced.
model to narrow the choices. general({ model: 'openai/gpt-4o-mini' }) offers only that model; an object form can provide a curated options array instead.
Add a profile
Useprofile for client-facing agent identity. Each field is optional, but provided values must be non-empty strings.
avatar accepts a URL or app-relative path. It is a plain string rather than an admin component or media-document reference. The chat UI uses name for avatar alt text and renders initials when avatar is omitted. Without a profile, chat messages render as before.
Profile options
Accessible profiles appear in both
GET /api/agents and the chat manifest. An agent rejected by its access rule exposes neither its slug nor its profile.
Add tools
Agents accept FrogBotTool objects, not raw AI SDK tools. FrogBot converts them internally and supplies request, runtime, slug, and run ID context to every execution.
To add integration actions, pass individual piece actions or a whole piece instance. To add current web results, use the web search pieces. To give an agent the tools of an MCP server, such as Sidekick’s browser, add an mcpServer().
Tool options
Client tools replace
execute with outputSchema and client; see Build a client tool.
Tool context
Attach tools directly to the worker.
ToolCtx, and the loop continues until a stop condition matches.
Set component to an import-map component reference when a tool needs purpose-built chat UI.
{ part }, including the tool input, output, state, and error details. Tools without component use the generic tool render. frogbot dev updates the import map automatically; build-only workflows must run frogbot generate:importmap before the app build.
Components resolve from the selected agent’s effective tools. When an agent tool shadows an inherited root slug, the agent’s component wins and FrogBot logs both definitions during initialization. Omitting component on the shadowing tool intentionally restores the generic render for that agent.
Add skills
Skills keep playbooks and reference material out of the prompt until the agent needs them. Each skill adds a short discovery line to the agent instructions and makes three tools available:list_skills, load_skill, and load_skill_resource.
instructions and resource content accept a string or a function receiving { req, frogbot }. Function content is evaluated only when the agent loads it. Unknown skill and resource names return the available names so the agent can correct its next call.
Each resource takes these options:
license and compatibility are top-level fields. metadata is a flat string map. The three generated tool slugs are reserved when an agent has skills. inheritTools: false disables root tool inheritance but does not disable that agent’s skill tools.
Folder-based source skills are not supported yet. Declare skill content in configuration; a future generated skills map will package folder content without runtime filesystem access.
Share tools across agents
Add tools to the root configuration to attach them to every agent.tools: [...todoTools]. If an agent declares a tool with the same slug, its tool replaces the root tool and FrogBot logs a warning during initialization.
Set inheritTools: false when an agent should only use its own tools.
Track chat todos
FrogBot provideswrite_todos and read_todos as an optional toolkit. write_todos replaces the entire list, and read_todos returns the current chat’s list. Both render a task-list card with pending and completed counts by default.
content and a status of pending, in_progress, or completed. Keep one item in_progress at a time. Todos are stored on the current chat, survive continuations, and are deleted with that chat. Chat persistence must be enabled and the tool must run within a persisted agent chat.
Ask the user a question
Add the optionalquestion tool when an agent should pause for a decision instead of guessing. The reply stops at the question and continues in the same message once the user answers.
Organize files
Use folders for organization, then import every config explicitly. FrogBot v0 does not scan a directory or loadAGENT.md files.
pnpm frogbot generate:types to augment GeneratedTypes with configured agent slugs, matching collection typing; each configured model must resolve to a provider enabled under ai.providers.
Invoke locally
Registered instances live atfrogbot.agents[slug] and expose generate and stream.
prompt or messages. Messages may be AI SDK UIMessage[] or ModelMessage[]; UI messages are converted before inference.
overrideAccess: true. To enforce the configured rule, pass a request and overrideAccess: false.
chatId to generate to persist the new user message and assistant response against server-owned history.
UIMessage[]; it does not accept ModelMessage[]. Local stream() calls remain stateless.
Run options
generate and stream accept these options:
Schedule runs
Add schedule triggers next to the agent that owns the recurring work. Each trigger requires a URL-safe slug, exactly one schedule form, and exactly one ofprompt or handler.
every accepts positive whole durations in seconds, minutes, hours, or days when they divide evenly into the corresponding cron field. Use cron for other intervals. Cron expressions use UTC; timezone-specific schedules are not yet supported.
A handler receives:
FrogBot runs schedules and skips an occurrence while the same trigger already has an active or runnable scheduled job. User-defined jobs, workflows, queues, and automatic runners remain configured alongside FrogBot schedules.
Each prompt trigger creates a fresh chat with
user: null and persists its user and assistant messages. A handler may invoke the owning agent, another agent, or no agent. Every handler receives { frogbot, agent, req, job }, and req.context contains source, agentSlug, triggerSlug, and jobId. Removing an agent or trigger makes already-queued work for it complete without running.
Invoke over HTTP
Configuring at least one worker mounts two FrogBot endpoints at the root API path. They are served under the normal/api prefix.
POST exactly one non-empty
prompt or non-empty messages array. Add model to select one of the agent’s allowed models; FrogBot rejects other models with a 403 response. Add reasoning to select one of the options the manifest lists under reasoning for that model, or for the default model when model is omitted; an option the model does not offer returns 400. A request without model uses the agent’s default model. Each user message keeps the model and reasoning option it was sent with, and every turn it starts uses them. Authenticated calls create a chat when chatId is absent. Without an explicit event-stream accept header, the response is { text, usage, finishReason, chatId }.
Accept: text/event-stream.
X-FrogBot-Chat-Id header. Continue the conversation by sending that ID with only the next user message.
400, an unknown slug returns 404, and an aborted request returns a bodyless 499.
See Chat persistence for collection customization and durability behavior.
Control access
access receives { req, agent }, where agent is the agent instance being checked, and may return a boolean or promise. Its default is Boolean(req.user).
overrideAccess: false. The GET endpoint evaluates each worker’s rule and omits denied entries or entries whose rule throws.
Custom rules may allow anonymous requests by returning true. Local calls bypass the rule unless you explicitly set overrideAccess: false with a request.
Follow hooks
Worker runs use the five hooks configured underai.hooks. Every hook receives agent: { slug, runId, chatId? }, plus req and user when available.
beforeOperation and afterOperation run once for the whole generate or stream call. Agent HTTP calls use that same lifecycle rather than adding another one.
beforeUpstream, afterUpstream, and afterError run around each model’s doGenerate or doStream call, so a tool loop can produce multiple upstream phase pairs.
Access is checked before beforeOperation, so denied calls run no hooks. Stream completion, failure, and abort each finalize afterOperation at most once.
Tool execution has no separate lifecycle hook in v0. Match ToolCtx.agent.runId with the hook argument’s agent.runId to correlate tool work.
See AI hooks for phase arguments, mutation rules, and failure isolation.