code-docs/utils/ai-utils.md
Shared agent runtime that sits between provider-specific resolvers and the Vercel AI SDK.
This package provides the core orchestration for Lowdefy's agent system:
handleAgentChat — The main entry point called by all agent resolversupdate-page-state toolsupdate-page-state)import {
handleAgentChat, // Core orchestration function
AISDKAgent, // Base agent wrapper
AISDKAgentSchema, // JSON Schema for agent properties
buildAgentTools, // Tool merging function
} from '@lowdefy/ai-utils';
The main entry point called by all agent resolvers (ClaudeAgent, OpenAIAgent, GeminiAgent, AIGatewayAgent).
Parameters:
{
connection, // AI SDK provider instance
properties: {
agent, // Full agent config (tools, mcp, hooks, properties)
messages // UIMessage[] from client
},
context: {
agentContext, // { pageId, userId, conversationId, urlQuery, sharedState }
callEndpoint, // Execute API endpoint as tool
evaluateOperators,
getEndpointConfig,
getAgentConfig, // For sub-agent references
getConnectionForAgent,
resolveMcpSources
}
}
Orchestration sequence:
buildAgentTools()pageContext prepend)ToolLoopAgent with model, tools, stop conditionscreateUIMessageStream — merges agent output into SSE streamonFinish hooks (awaited for dataParts)Returns: { response } — a Web Response stream.
Merges five tool sources into a single tools object:
Endpoint tools (agent.tools[]) — Loads config via getEndpointConfig, creates AI SDK tool() with callEndpoint as execute. Optional needsApproval: true for confirm: true tools.
MCP tools (agent.mcp[]) — Creates transport (stdio or HTTP), creates MCP client, retrieves tools via client.tools(). Warns on name conflicts with endpoint tools.
Sub-agent tools (agent.agents[]) — Recursively builds tools for sub-agents (depth limit 5), creates nested ToolLoopAgent, wraps with toModelOutput to extract text.
FileSystem tools (agent.properties.fileSystem) — read-file (512KB max), list-files (with glob), search-files (case-insensitive, 200 match limit), stat-file. All scoped to basePath.
Built-in update-page-state tool — Added when the AgentChat block declares sharedState. Built via buildUpdatePageStateTool and exposed as a tool the agent can call to write back to page state. See Page state integration.
Before merging, user-defined tool names are validated against RESERVED_PLATFORM_TOOL_NAMES (currently update-page-state). A collision is a hard error — collisions silently overriding the platform tool would corrupt state-write semantics.
Returns: { tools, mcpClients }
Builds a prepareStep callback from agent config rules. Rules match by:
steps: [1, 3, 5] — Specific step numbersfrom: 2, to: 4 — Range (inclusive)First matching rule wins. Can override: activeTools, toolChoice, maxOutputTokens, temperature.
JSON Schema defining all agent properties: model, instructions, maxSteps, temperature, topP, topK, frequencyPenalty, presencePenalty, seed, stopSequences, toolChoice, activeTools, stopOnToolCall, pageContext, repairToolCall, prepareStep, prune, fileSystem, providerOptions.
File system tool implementations, all scoped to a basePath:
| Module | Purpose | Limits |
|---|---|---|
resolvePath.js | Normalizes path, validates no escape from basePath | — |
readFile.js | Reads file content as UTF-8 | 512KB max, truncates |
listFiles.js | Lists directory with optional glob filtering | — |
searchFiles.js | Case-insensitive text search | 200 matches, 1MB/file |
statFile.js | Returns file metadata (size, type, dates) | — |
Factory that constructs the update-page-state tool from the active sharedState snapshot. The tool accepts a partial state patch and is wired so the AgentChat block applies the patch to page state on the client. The factory binds against the keys present in sharedState so that the tool's parameter schema (and the agent's awareness of writeable keys) is restricted to what the block exposed.
Exports RESERVED_PLATFORM_TOOL_NAMES and a guard used by buildAgentTools to reject user tools that collide with platform built-ins. Currently reserved: update-page-state. Collisions throw at build/runtime — they're never silently shadowed.
sharedState)Agents can read and write a slice of page state declared by the AgentChat block:
sharedState object (formerly pageState) — operator-evaluated each render and shipped with each chat request.callAgent forwards sharedState into the runtime's agentContext.sharedState.handleAgentChat builds the update-page-state tool for that snapshot and includes the state as a pageContext-style block in instructions so the agent sees current values.update-page-state, the streamed result is delivered back to the AgentChat block, which writes the patch to page state via the update-page-state event (allowlisted to the originally-declared keys).Sharp edges:
Object.keys(sharedState) at request time — adding keys mid-conversation requires a fresh render.ai (Vercel AI SDK v6) — ToolLoopAgent, UIMessageStream, tool(), jsonSchema@modelcontextprotocol/sdk — MCP client and transport@lowdefy/helpers — serializer, type checksresolvePath ensures all paths resolve within basePath.context.callEndpoint, context.getEndpointConfig, context.getAgentConfig, context.getConnectionForAgent, context.resolveMcpSources| File | Purpose |
|---|---|
src/handleAgentChat.js | Core orchestration |
src/buildAgentTools.js | Tool building and merging (includes reserved-name check) |
src/buildPrepareStep.js | Dynamic step configuration |
src/buildUpdatePageStateTool.js | Factory for the built-in update-page-state tool |
src/reservedToolNames.js | RESERVED_PLATFORM_TOOL_NAMES and collision guard |
src/AISDKAgent.js | Base agent class |
src/AISDKAgentSchema.js | Agent properties schema |
src/fileSystem/resolvePath.js | Path safety validation |