Inside a D2D Agent: Manifest, System Prompt, Tool Contract
In the D2D overview we covered the coordinator, worker, and killer node model. Now let's get concrete: what do the actual files look like inside one of these agents?
The Three-File Pattern
Every D2D agent — whether coordinator or worker — is fully described by three artifacts. This is what makes D2D composable: you can swap out an agent's model, budget, or tool set by editing these files, without touching the orchestration layer.
1. manifest.yaml
name: cve-enrichment-worker
domain: vuln-management
model: claude-haiku-4-5
token_budget: 8000
heartbeat_interval_s: 30
max_retries: 2
tools:
- nvd_lookup
- epss_score
- exploitdb_search
- jira_create_ticket
output_schema: ./schemas/cve-enrichment-result.json
escalation:
on_timeout: killer
on_schema_fail: killer
on_retry_exhausted: human
Key fields to notice:
- domain — scopes this worker; the coordinator uses this to route tasks
- token_budget — the killer node monitors this; if exceeded, the agent is terminated
- output_schema — every worker output is validated against this before it's accepted by the coordinator
- escalation — explicit rules: what goes to the killer, what goes to a human
2. system.md (the agent's system prompt)
# CVE Enrichment Worker
You are a specialist agent for enriching CVE data. You operate within the vuln-management domain.
## Your job
Given a CVE ID and an optional affected asset, you must:
1. Look up the CVE in NVD and return the CVSS score, vector, and description
2. Retrieve the EPSS score (probability of exploitation in the wild)
3. Search ExploitDB for any public exploit code
4. Create a Jira ticket if CVSS >= 8.0 AND EPSS >= 0.3
## Constraints
- You may ONLY call tools listed in your manifest. Never attempt to call unlisted tools.
- If NVD returns no result, emit status: "not_found" and stop.
- Do not infer or guess CVE details. Only use data returned by tools.
- Your output must conform to the output schema exactly.
## Output format
Return a single JSON object matching the schema at schemas/cve-enrichment-result.json.
Do not include any prose outside the JSON object.
The system prompt is deliberately narrow. A worker that can "infer or guess" is a liability — it will hallucinate CVE details under time pressure. The constraint is a safety property, not a limitation.
3. Tool definitions (tools.json)
{
"nvd_lookup": {
"description": "Query the NVD API for a CVE by ID",
"input": { "cve_id": "string" },
"output": { "cvss_score": "number", "cvss_vector": "string", "description": "string" },
"endpoint": "https://services.nvd.nist.gov/rest/json/cves/2.0"
},
"epss_score": {
"description": "Retrieve EPSS exploitation probability for a CVE",
"input": { "cve_id": "string" },
"output": { "epss": "number", "percentile": "number" },
"endpoint": "https://api.first.org/data/v1/epss"
}
}
Tool definitions are the trust boundary. The agent runtime enforces that the agent can only call tools declared here, with input shapes that match the schema. There is no ambient tool access.
The Coordinator's Routing Table
The coordinator keeps a routing table that maps incoming task types to worker domains:
routes:
- task_type: enrich_cve
domain: vuln-management
worker: cve-enrichment-worker
- task_type: query_siem
domain: detection
worker: siem-query-worker
- task_type: notify_slack
domain: comms
worker: slack-notify-worker
- task_type: human_review
domain: null
escalate: pagerduty
The Killer Node's Watch Loop
The killer node runs a simple watch loop against all active workers:
every 10s:
for each active worker:
if now - last_heartbeat > heartbeat_interval * 2:
emit KILL(worker_id, reason="timeout")
if worker.tokens_used > worker.token_budget:
emit KILL(worker_id, reason="budget_exceeded")
if worker.output_schema_failures > 2:
emit KILL(worker_id, reason="schema_fail")
When a KILL is emitted, the coordinator decides whether to retry (up to max_retries) or escalate to a human. The worker's last known state — including any intermediate tool outputs — is preserved in the task log for post-mortem.
Why This Pattern Is Auditable
Every message in the system is structured and logged: task assignment, heartbeat, tool call, tool result, output, KILL signal. An incident investigator can reconstruct exactly what every agent did, why it did it, and what the coordinator decided at each step. That's the audit trail a CISO can actually show to a board.
Next in this series: how the coordinator handles mission planning and re-planning under partial failure.
Frequently asked questions
What three files describe a D2D agent?
A manifest.yaml declaring name, domain, model, token budget, heartbeat interval, tools and escalation rules; a system.md system prompt with the role, constraints and output schema; and tool definitions in tools.json that set the exact API surface the agent can call.
Why are the files useful?
The post says the three-file pattern makes D2D composable: you can swap an agent's model, budget or tool set by editing the files without touching the orchestration layer.
What does the manifest's escalation field do?
It sets explicit rules for what goes to the killer node and what goes to a human. In the post's example, a timeout or a schema failure goes to the killer and an exhausted retry count goes to a human.
How does the killer node's watch loop work?
Every 10 seconds it checks each active worker, issuing a KILL if the last heartbeat is older than twice the heartbeat interval or if tokens used exceed the token budget.