DocumentationPixel, the team agentMarkdown
Pixel, the team agent
Pixel is the AI assistant inside the panel for the tenant's own staff (admins and agents), the same
staff assistant as in Sevenfold's other apps. It follows Snakkbar's look: its face is the Snakkbar
bubble. Staff chat
with it to write and improve newsletters, find content on their website, check who a campaign goes
to, and read results. The website assistant talks to customers; Pixel talks to the team. (Pixel is a different
assistant: the snakkbar tenant's own, for visitors on snakkbar.app.)
Its tools cover the overview and numbers, conversations with customers (read only), answer quality and knowledge, callbacks, who is on the site, the website assistant's settings (changes need a person's approval), and Mailbar. Asked about other parts of Snakkbar (people and roles, departments, notifications, webhooks, API keys, the chat's look), it says where in the panel to go.
What it can do
It works through tools on the tenant's own data, as the signed-in person. Changing the website assistant's settings is for admins only; with an agent, Pixel shows them and says an admin must change them:
| Tool | What it does |
|---|---|
list_campaigns, get_campaign | Read campaigns: fields, blocks, sender, recipient count, and what is missing before sending |
create_campaign, update_campaign | Write drafts. Blocks are validated like the editor's (campaigns.md); only drafts can change |
list_lists, count_recipients | E-mail lists with subscriber counts, and how many a send would reach |
search_website, read_web_page | The tenant's site: products, news and pages (titles, descriptions, links, images), the same search the website assistant uses |
send_test_to_me | A test of a campaign to the operator's own address only |
campaign_results | Delivered, opened (indicative), clicked, visited, unsubscribed, bounced, and clicks per link |
remember | A note about the tenant for later conversations (tone, sign-off, lists to use) |
get_template, update_template | Mailbar's template («Mal»): the design every mail gets and the blocks new campaigns start from (campaigns.md) |
create_list | A new, empty e-mail list |
overview | Oversikt's numbers for 1, 7, 30 or 90 days: visitors, conversations, how many a person took, response time, pages, answer quality |
find_conversations, read_conversation | Search or list conversations with customers, and read one (it never writes to customers) |
quality_queue | Answers to look at: thumbs down, nothing found, low ratings, handoffs |
knowledge_overview, search_knowledge | What the knowledge base holds, and what the website assistant would find for up to ten questions at once |
suggest_knowledge | A question and answer for the knowledge base, pending until a person approves it under Kunnskap → Forslag |
knowledge_candidates | In one call: the pending suggestions (made from staff replies to customers, often raw and duplicated) and the questions the assistant could not answer that nobody did (e-mail addresses and phone numbers masked) |
rewrite_suggestions | Rewrites pending suggestions into proper articles (general question, complete answer, no personal details); they stay pending |
reject_suggestions | Rejects pending suggestions (merged duplicates, one customer's matter, small talk), after the operator approves |
list_callbacks, mark_callback_handled | Callback requests, and marking one done when the user says so |
live_visitors | Who is on the site now (page, where from, device, place) |
get_assistant_settings | The website assistant's name, tone, instructions, topics to avoid, welcome, suggestions, opening hours, callbacks; the team, departments and saved replies |
update_assistant_settings | Changes to those settings, after the operator approves them in the panel (below) |
Pictures. Staff paste, drop or attach images in Pixel's composer (up to six a message). They
are uploaded first (POST /operator/campaign-images) and sent with the message; the model sees
them, so a screenshot of a newsletter or the website becomes a template in that style, and the
images can go straight into image blocks. With «Mal» open the panel says so (template: true).
It never sends, schedules or deletes anything, and cannot unsubscribe or erase contacts: those stay a person's click in the panel. It never writes to customers either.
Conversations into knowledge. Asked to go through conversations or the suggestions, Pixel takes
everything at once (knowledge_candidates), groups it by topic, checks the topics against the
knowledge base, rewrites the best suggestion per topic into an article (merging duplicates), asks to
reject the rest, and lists questions nobody has answered for the team. It never approves: articles
reach the website assistant only when a person approves them under Kunnskap → Forslag.
Templates and newsletters. A new newsletter builds on the template: with named sections
(slot) Pixel gives only each section's content (create_campaign with sections), and the
server puts it into the template, so fixed blocks, styles and columns stay exactly as designed.
Asked for a template after a model (a screenshot, a link to a newsletter's web version, the
website), Pixel hands it to its template designer (pixel/agent/subagents/maldesigner, Opus):
a subagent with a small VM of its own (Vercel Sandbox, a headless Chromium prepared once per
deploy). It looks at the screenshots, takes a screenshot of the link or the site and reads the
newsletter's HTML for exact colours and fonts, builds the template from Mailbar's blocks, draws it
(desktop and phone) and corrects it two or three times, and returns it; Pixel saves it with
update_template.
Long turns. One API request follows a turn for at most four minutes (the route's limit is five).
A turn still running then goes on in eve: the request ends with a continue event and the turn is
stored as agent_threads.pending (where its stream starts and the transcript before it). The panel
follows it with POST /operator/agent/threads/:id/resume, which replays it from its start; a thread
opened while that is so says working and is followed the same way; the knowledge review's cron
finishes turns nobody followed. A turn is stopped after 15 minutes in all.
Scheduled review. Under Innstillinger → Assistentene → Pixel an admin picks how often Pixel does
this on its own: off (default), every week, every other week or every month
(tenants.knowledge_review). An hourly cron (/internal/knowledge-review, server/knowledge-review.ts)
runs one due tenant at a time between 06 and 10 Norwegian time: a new Pixel conversation of the admin
who turned it on (else the first admin), titled «Kunnskapsgjennomgang
Approval. A change to what the website assistant tells visitors waits for the operator: the tool
has eve's approval: always(), so the turn parks before it runs (also for rejecting suggestions, which then leave Kunnskap → Forslag). The panel shows the change field
by field (the new value, the current one behind «Vis nåværende») with «Godkjenn» and «Avvis»; the
answer resumes the same turn. Writing something else instead drops the change. Settings are checked
against the same rules as Innstillinger (server/tenant-settings.ts). It writes in Norwegian, uses real pages and images from the
site, and never invents products, prices or offers.
Conversations
A conversation (thread) belongs to the operator who started it. One answer runs at a time per thread. When a campaign is open in the panel, the panel sends its id, and "the campaign" means that one; changes Pixel makes show up in the editor right away. Notes it remembers are shared by the tenant's admins and listed under Innstillinger → Assistentene → Pixel, where they can be deleted.
Model calls are metered on the tenant (agent_turns, agent_cost_usd), and
stop at the tenant's daily AI cost ceiling. Each operator can send 120 messages an hour.
How it runs
Pixel is an eve agent of its own in pixel/agent/ (tools in pixel/agent/tools/, the standing
rules in instructions.md, and per turn who it works for, what the panel has open and its memories
in instructions/denne-samtalen.ts). It sits beside the website assistant (agent/), so its tools
never exist in a visitor's session. next.config.ts mounts both: the website assistant at
/eve/kundechat/v1, Pixel at /eve/pixel/v1.
The API is Pixel's only caller (server/team-agent.ts): it checks the panel sign-in, then sends the
turn with the internal secret, the tenant, operator and thread, and what is open in the panel
(pixel/agent/channels/eve.ts). Tools check the operator again on every call (on the tenant's
team and not deactivated; an admin for the assistant's settings). Each thread is one eve session (agent_threads.eve_session_id), which
keeps the model's history and compacts it when it grows; the API keeps the transcript the panel
shows, and translates eve's stream into the panel's events below. A turn is capped at 16 model
calls and $2, a session at $25.
Evals. npm run eval:pixel runs Pixel's evals (pixel/evals/) on a local dev server as an admin
of EVAL_TENANT (default amatec), with its real campaigns, lists, template and site; tools that
write are stubbed. EVAL_MODEL=<gateway id> tries another model. Needs pixel/.env.local (a copy
of the root one).
API (anyone on the team, /api/v1; the memories are for admins)
| Method and path | Body / result |
|---|---|
GET /operator/agent | → { name, threads: { id, title, createdAt, updatedAt, busy }[] } |
POST /operator/agent/threads | → { thread } (201) |
GET /operator/agent/threads/:id | → { thread: { …, transcript } }: the operator's and Pixel's messages, with the tools each answer used |
DELETE /operator/agent/threads/:id | → 204 |
POST /operator/agent/threads/:id/messages | { text, campaignId?, template?, images? } (images: up to six URLs from POST /operator/campaign-images) → text/event-stream: text { delta }, tool { name, label, state }, campaign { campaignId, created }, template (the template changed: reload it), approval { approval } (a change waits for the operator; the item in done carries it), then done { item } or error { code, message }. 409 while another answer runs or when the daily AI limit is reached. |
POST /operator/agent/threads/:id/approval | { requestId, approve } → the same stream as a message, as the turn goes on. 409 when the change no longer waits. |
POST /operator/agent/threads/:id/resume | Follows a turn that outlived its request (after continue, or a thread with working): reset, then the turn from its start, then done (or continue again). 409 when nothing is going on. A new message while a turn goes on is 409. |
GET /operator/agent/memories | → { memories: { id, text, createdAt }[] } |
DELETE /operator/agent/memories/:id | → 204 |