Agents
In Polyant, an agent is the same thing as an instance: a fully configured AI assistant that lives in PostgreSQL and is brought to life by the Supervisor, a single reasoning loop that orchestrates every turn. There are no per-instance code branches and no specialized sub-agent classes — every instance, regardless of domain, is driven by the same supervisor wired against per-instance prompts, tools, and skills.
This page covers what the supervisor actually does on each turn, how the system prompt is assembled, how tools are presented to the LLM, and the two distinct delegation paths — spawnTask (ad-hoc sub-agent inside the same instance) versus agent-to-agent calls through the in-process agent channel adapter.
Instance equals agent
An instance is the unit of multi-tenancy and the unit of personality. A single engine process can host many instances side-by-side:
- Each has its own slug, name, provider (OpenAI, Anthropic, Bedrock, Nebius), model override, and — independently of the chat provider — its own embedder.
- Each has its own row set in
instance_prompts,instance_tools,instance_skills,instance_secrets, andinstance_channels. - Each has its own memory partition (
memories.instance_id) and its own conversations. - Toggles like
memoryEnabled,langsmithEnabled, andauthEnabledare per-instance, as are the turn-shaping flags (prompt cache, datetime injection, conversation state in prompt, tool results in history) — see Settings. - Each has its own governance assignments, lifecycle hooks, opt-out configuration, and retention policy.
There is no shared global personality. The supervisor reads the active instance’s configuration on every turn (with a 30-second cache) and runs against that — so changes made in the admin panel take effect on the very next message.
The supervisor loop
The supervisor is a single agent, not a tree of specialised sub-agents. It runs in the standard tier with maxSteps = 15, meaning the LLM may chain up to fifteen reasoning/tool-call cycles before having to deliver a final response. The supervisor is invoked from the pipeline for every inbound user message, regardless of channel.
Each invocation receives:
- The instance’s system prompt (the stable prefix, see below) plus a per-turn volatile tail.
- The last fifteen messages of conversation history.
- The current conversation summary (a rolling 2–3 sentence digest).
- The set of tools enabled for this instance (filtered via
instance_tools). - Optional sub-agent delegation via the built-in
spawnTasktool.
The tier hides the underlying model: components ask for fast, standard, or heavy — never gpt-4o or claude-sonnet-4-5. The supervisor always asks for standard; the instance’s chosen provider (and optionally a pinned model) decides what that resolves to. See AI Gateway for the tier-to-model tables and the per-instance override rules.
The prompt sections
An instance’s identity is composed of modular sections stored in instance_prompts (01-identity … 07-user-identity). They are concatenated in order into the stable, cacheable part of the system prompt, and they are the only runtime-editable surface for an agent’s behavior. The section keys are stable contract — they appear in the admin UI, the management API, and seed data — and new instances are seeded from packages/engine/src/instances/defaults.ts.
Everything that changes turn by turn (current datetime, channel identity, conversation summary, conversation state) is appended as a separate volatile tail rather than into the sections, so the cached prefix stays stable across turns.
For the full table of section keys and roles, plus the runtime blocks, see Prompt.
Delegation: two distinct mechanisms
Polyant ships two unrelated delegation paths — don’t conflate them:
spawnTask— ad-hoc sub-agent in the same instance. The supervisor’s built-inspawnTasktool creates an isolated sub-agent that runs against the same instance’s prompts/tools (minusspawnTaskitself, to prevent recursion). Useful for chunking a complex task in the current instance’s context.- Agent-to-agent via the
agentchannel. A separate, in-process channel adapter (packages/engine/src/channels/adapters/agent.adapter.ts) that lets instance A invoke instance B synchronously, with no HTTP/Slack/Telegram round-trip. The callee runs its own full pipeline (history, summary, supervisor, tools) and the reply is returned as a tool result on A’s side. This is wired through dedicated supervisor tooling, not throughspawnTask.
The callee is published to every other instance as a synthetic tool named ask_<slug> (hyphens become underscores) whose description is the callee’s own description, so the caller’s model can tell when to route to it.
Agent-to-agent calls carry metadata (callerSlug, callerConversationId, depth, parentTraceId) so analytics and LangSmith traces stitch the child invocation back to its parent. Two hard bounds apply:
- No nesting. The synthetic tool refuses at depth 1: a callee cannot call a third agent. This is what keeps a fan-out from turning into an unbounded chain.
- Timeout. Each call is gated by
AGENT_CALL_TIMEOUT_MS(default60000ms) — a callee that does not return in time is aborted and the caller receives a timeout error.
Agent-to-agent is in-process only: there is no network form of it between separate deployments.
See Channels for the channel-side perspective and Tools for how delegation tools are wired.
How it works
+----------------------------+
user message ->| Channel Adapter |
+--------------+-------------+
|
v
+----------------------------+
| Pipeline (handleMessage) |
| history + summary + ctx |
+--------------+-------------+
|
v
+----------------------------+
| Supervisor (tier=standard,
| maxSteps=15) |
| |
| system = sections |
| 01-identity |
| 02-soul |
| 03-tooling |
| 04-safety |
| 05-skills + {{list}} |
| 06-memory |
| 07-user-identity |
| + volatile turn tail |
| datetime, channel, |
| summary, state |
| |
| +----------------------+ |
| | tool loop (<=15) | |
| | plan -> call -> ack| |
| | ... -> final text | |
| +----------------------+ |
+--------------+-------------+
|
v
+----------------------------+
| Outbound + fire-and-forget|
| (save msg, summary, mem) |
+----------------------------+Code reference
packages/engine/src/agents/supervisor/index.ts—supervise()/superviseStream(); tierstandard,maxSteps: 15.packages/engine/src/agents/supervisor/prompt.ts— System prompt builder that assembles the sections, renders the{{skillsList}}placeholder, and splits the stable prefix from the volatile turn tail.packages/engine/src/instances/defaults.ts— Default content for every section, seeded on instance creation.packages/engine/src/instances/prompts.store.ts— Read/write API forinstance_promptsrows (60-second TTL cache).packages/engine/src/ai-gateway/model-catalog.ts— Tier-to-model mapping and per-model capabilities for OpenAI, Anthropic, Bedrock, and Nebius.packages/engine/src/ai-gateway/index.ts—chat()/chatStream()entry points used by the supervisor.packages/engine/src/instances/config-resolver.ts— Resolves the active provider/model/secrets/channels for an instance at runtime.packages/engine/src/channels/adapters/agent.adapter.ts— In-process channel that powers agent-to-agent invocation.