Implementation
A build prompt for an AI coding agent: exactly what to implement for the Worker subsystem, in what order, against which contracts. Hand the block below to an agent; the sections after it are the reference it should follow.
This page is an agent prompt
Every section in these docs gets an Implementation page — a precise, copy-paste task spec the agent executes. Read Deploy, Use and Destroy first; they are the source of truth this prompt points to.
The prompt
ROLE: You are implementing the Worker subsystem of Agentfy.ai 2.0 (NestJS + Prisma + CleanSlice).
GOAL: An agent's "hands" — an on-demand worker (bash / fs / browser) that runs one obligation as an
ephemeral k8s Job, streams progress back to the brain over a WebSocket, persists outputs, and
disappears. The brain (api) keeps the LLM loop; the worker has NO LLM.
DELIVERABLES
1) `worker` app (the worker image): an agent-less, LLM-less **MCP server**.
- Embeds an MCP server; dials CONTROL_URL over a WebSocket (reverse transport) and runs the MCP
`initialize` handshake with WORKER_TOKEN, then signals `ready`.
- Advertises via `tools/list` ONLY the TOOL_ALLOWLIST tools: exec, process_exec, file, unzip,
browser, browser_screenshot, browser_play, http, web_fetch, pdf_analyze. Executes `tools/call`.
- Sticky session state: cwd, browser context (Playwright, per-tenant --user-data-dir), env,
background procs — the MCP server instance is stateful for the session.
- Large outputs → object storage via system/file, returned as an MCP `resource_link` (never inline).
- MCP `sampling` DISABLED. Heartbeats; self-terminate on idle-timeout or lost control connection.
2) `api` → `runtime/worker` slice (the manager / dispatcher): sessions · k8s (manifest builder +
create/delete) · browser (in-pod Chromium lifecycle) · idle (reaper) · profiles.
3) `api` → `runtime/task` slice (the queue): BullMQ on Redis; standard, delayed, repeatable jobs.
CONTRACTS: implement exactly the schemas, profiles, frames and job kinds in the reference below.
Follow CleanSlice conventions (gateway pattern, `I`-prefixed DI tokens, singular slice folders, `#`
aliases, no `any`).
SECURITY (non-negotiable): no long-lived secrets in the pod; short-lived per-session token only; KEK
never leaves api; NetworkPolicy default-deny + egress allowlist; read-only root fs, drop ALL caps,
non-root, automountServiceAccountToken:false; activeDeadlineSeconds hard cap; audit every session.
ACCEPTANCE: see the checklist at the bottom. Start at the v0.3 MVP scope.Reference — contracts to implement
Session row
AgentRuntimeSession { agentId, taskId?, status, runtimeType, cpu/mem/storageLimit, ttlSeconds, k8sJobName, namespace, workerUrl, logsUrl, startedAt, lastActivityAt, stoppedAt } with states pending → starting → running → idle → stopping → stopped (+ failed). See Destroy.
Runtime profiles
A preset catalog → RuntimeProfile { mode, cpu, mem, storage, maxExecSeconds, idleSeconds } for None / Light / Browser / Heavy / Warm. The manifest builder reads it. See Deploy → runtime modes.
Control channel = MCP (channel 3)
MCP over a worker-initiated WebSocket (reverse transport). The worker is the MCP server, the api worker-gateway is the MCP host/client; the MCP session id ↔ our sessionId.
{ "method": "tools/list" } // host → worker (discover, allowlist-gated)
{ "method": "tools/call", "params": { "name": "browser.click",
"arguments": { "selector": "#buy" } } } // host → worker
{ "method": "notifications/progress", "params": { "progressToken": "c12",
"message": "navigated to /cart" } } // worker → hostPlus the initialize handshake (auth via WORKER_TOKEN), ready, heartbeat, release. MCP sampling disabled. The channel is sketched in Use → the protocol; the normative wire contract — who sends each frame, what answers it, what happens on silence, and the version rule — is Tool channel, and it is the one to implement against.
Queue jobs
task dispatch (standard) · idle timer (delayed) · scheduled (repeatable) · ingestion (budgeted). Consumer = the dispatcher, which creates the k8s Job. See Deploy → the queue.
Bootstrap env
SESSION_ID, CONTROL_URL, WORKER_TOKEN_FILE, TOOL_ALLOWLIST, storage scope.
The pass is a file, not a value: WORKER_TOKEN_FILE names the path a projected Secret is mounted at (/var/run/agentfy/token), and the worker reads it there. WORKER_TOKEN in the environment is still honoured for running the worker outside Kubernetes, and setting both is refused — a manifest half-migrated to the volume would otherwise keep the pass in the environment and say nothing about it (006 T104).
MAX_EXEC_SECONDS and IDLE_SECONDS carry this session's two deadlines, straight off its profile. The worker arms its first timer before the control channel exists, so without them it fell back to the Light table — 900 s and 90 s — on every profile, and a Heavy session was told it had a quarter of the hour it really had while the reaper took the pod thirty seconds before the pod expected (AGNT2-237). The initialize frame carries the same pair and the worker obeys the smaller of the two, which is why the frame alone could never correct this: 900 is smaller than 3600. Both keys are optional — absent means the Light table, which is what every harness that runs the worker by hand relies on — but a value that is present and unreadable is refused, because a deadline that quietly becomes 900 s is indistinguishable from one somebody meant.
Ordered tasks
- v0.3 MVP —
runtime/task(dispatch + delayed idle-timer) ·runtime/worker(sessions, k8s create/delete, Light profile) ·workerimage (MCP server:initialize+tools/list+exec/file) · the reverse-WS MCP transport + host-side registration · idle reaper. Proves message → Job → MCP attach → tools/call → result → reap. - + Browser — in-pod Chromium,
browser*tools, Browser profile. - + Concurrency & reliability — per-team caps, retries/backoff + dead-letter.
- + Heavy / Warm — profiles & node placement.
- + Scheduled — repeatable jobs for
agent/cron.
Acceptance criteria
- [ ] A no-tool turn never enqueues anything; a tool turn enqueues once to provision.
- [ ] One MCP session serves many
tools/callover a single reverse WebSocket (sticky cwd + browser context). - [ ] The worker (MCP server) dials back and authenticates with a short-lived token at
initialize; no other secrets in the pod; MCPsamplingis off. - [ ] Control traffic never appears in the user chat; only curated status/artifacts do.
- [ ] Idle → reaped after the profile window; outputs synced to
system/filebefore Job deletion. - [ ]
activeDeadlineSecondsand max-idle caps enforced; every session audited.
See also
- Deploy · Use · Destroy — the source of truth.
- Tool channel — the control-channel contract, in full and written once.
- Layered slices — where
runtime/workerandruntime/tasksit. - Conventions — CleanSlice rules the agent must follow.