How to delegate all AI work to the agent chat. Use when delegating AI work from UI or scripts to the agent, when a user asks for agent behavior or LLM-powered features, when tempted to add inline LLM calls, or when sending messages to the agent from application code.
73
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
The UI never calls an LLM directly. Product workflows are delegated to the
agent through the chat bridge so users can see, steer, and audit the work.
Server-side one-shot model calls are an explicit escape hatch for narrow text
transforms only; use completeText() from @agent-native/core/server when the
work intentionally does not need tools, chat history, or run state.
The agent is the single AI interface. It has context about the full project, can read/write any file, and can run scripts. Inline LLM calls bypass this — they create a shadow AI that doesn't know what the agent knows and can't coordinate with it.
From the UI (client):
import { sendToAgentChat } from "@agent-native/core/client/agent-chat";
sendToAgentChat({
message: "Generate a summary of this document",
context: documentContent, // optional hidden context (not shown in chat UI)
submit: true, // auto-submit to the agent
});From the UI, in the background:
import { sendToAgentChat } from "@agent-native/core/client/agent-chat";
sendToAgentChat({
message: "Analyze this import and create any missing records",
context: `Import batch id: ${batchId}`,
submit: true,
newTab: true,
background: true,
openSidebar: false,
});This is still a full agent run: tools, actions, thread state, and run tracking all remain active. It simply does not focus or open the sidebar.
From scripts (Node):
import { agentChat } from "@agent-native/core";
agentChat.submit("Process the uploaded images and create thumbnails");For narrow server-side text transforms:
import { completeText } from "@agent-native/core/server";
const result = await completeText({
systemPrompt: "Return exactly one sentiment label.",
input: messageBody,
maxOutputTokens: 12,
temperature: 0,
});Wrap user-facing uses in actions so the UI and agent share the same operation. Do not call provider SDKs directly.
From the UI, detecting when agent is done:
import { useAgentChatGenerating } from "@agent-native/core/client/agent-chat";
function MyComponent() {
const isGenerating = useAgentChatGenerating();
// Show loading state while agent is working
}submit vs PrefillThe submit option controls whether the message is sent automatically or placed in the chat input for user review:
submit value | Behavior | Use when |
|---|---|---|
true | Auto-submits to the agent immediately | Routine operations the user has already approved |
false | Prefills the chat input for user review | High-stakes operations (deleting data, modifying code, API calls with side effects) |
| omitted | Uses the project's default setting | General-purpose delegation |
// Auto-submit: routine operation
sendToAgentChat({ message: "Update the project summary", submit: true });
// Prefill: let user review before sending
sendToAgentChat({
message: "Delete all projects older than 30 days",
submit: false,
});Buttons that produce new content ("New Design", "Create Dashboard", "Make Deck", "Generate Form") need the user's prompt as input. Never hardcode a generic message — the result will be a generic generation the user didn't actually ask for.
Bad — auto-submits a placeholder message; the user never said what they wanted:
<Button
onClick={() =>
sendToAgentChat({ message: "make a design", submit: true })
}
>
New Design
</Button>Good — Popover anchored to the button captures the prompt, then submits it:
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button>New Design</Button>
</PopoverTrigger>
<PopoverContent className="w-96">
<Textarea
autoFocus
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="What do you want to design?"
/>
<Button
onClick={() => {
sendToAgentChat({ message: prompt, submit: true });
setOpen(false);
setPrompt("");
}}
>
Create
</Button>
</PopoverContent>
</Popover>Always ask for input first when the output depends on a prompt the user must provide — "design what?", "deck about what?", "dashboard for which metric?", "form for which use case?".
Auto-submit without input is fine when intent is unambiguous:
If you find yourself writing submit: true with a hardcoded creative verb ("design a...", "write a...", "build a..."), stop and add a Popover.
sendToAgentChat() delegates from app code to the agent. The other axis of
delegation is the agent handing work to a sub-agent through the Agent Teams
run-manager. The main chat stays the orchestrator: it spawns sub-agents, then
reads and integrates their results.
Every sub-agent brief must specify four things, or the sub-agent will guess:
Background sub-agents must use the core run-manager / Agent Teams infrastructure rather than ad-hoc LLM calls.
import Anthropic from "@anthropic-ai/sdk" in client or server codeimport OpenAI from "openai" in client or server codegenerateText(), streamText(), etc.completeText() for workflows that need tools, database writes,
auditability, user steering, or multi-step reasoning. Use the agent chat
instead, optionally with background: true.Scripts may call external APIs (image generation, search, etc.) — but the AI reasoning and orchestration still goes through the agent. A script is a tool the agent uses, not a replacement for the agent.
completeText() is allowed for small server-side transforms such as
classification, extraction, rewriting a short string, or normalizing messy
provider text. It deliberately runs with tools: [] and does not create chat
thread state.
sendToAgentChat() delegates work to the local agent — the one running alongside your app. When the work should go to a different agent entirely (e.g., asking an analytics agent for data, or a calendar agent for availability), use the A2A (agent-to-agent) protocol instead.
import { callAgent } from "@agent-native/core/a2a";
// Call a different agent — not the local agent chat
const stats = await callAgent(
"https://analytics.example.com",
"What were last week's signups?",
{ apiKey: process.env.ANALYTICS_A2A_KEY },
);See the a2a-protocol skill for the full pattern.
pnpm action <name> to perform complex operationsc1ee18b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.