Cross-Agent Calls
Call reasoners and skills on other agents through the control plane execution gateway.
Call any reasoner or skill on any agent -- one line, full context propagation, automatic DAG tracking.
Building one agent is easy. Building five agents owned by different teams is where the infrastructure work starts: service discovery, routing, correlation IDs, workflow tracing, and failure debugging.
app.call() is the abstraction that collapses that distributed-systems work. The caller only needs node_id.function_name. AgentField handles the routing, workflow propagation, and trace construction behind it.
@app.reasoner()
async def triage_ticket(ticket_id: str, message: str, customer_id: str) -> dict:
sentiment = await app.call("sentiment-agent.analyze_text", text=message)
history = await app.call("customer-history.recent_issues", customer_id=customer_id)
if sentiment["urgency"] == "high" or history["issue_count"] > 3:
escalation = await app.call(
"escalation-agent.create_case",
ticket_id=ticket_id,
reason="high urgency or repeated incidents",
)
await app.call("notifications.send_alert", case_id=escalation["case_id"])
app.note(f"Escalated ticket {ticket_id} to {escalation['case_id']}", ["triage", "escalation"])
return {"status": "escalated", "case_id": escalation["case_id"]}
return {"status": "queued", "sentiment": sentiment, "history": history}agent.reasoner('triageTicket', async (ctx) => {
const sentiment = await agent.call('sentiment-agent.analyzeText', {
text: ctx.input.message,
});
const history = await agent.call('customer-history.recentIssues', {
customerId: ctx.input.customerId,
});
if (sentiment.urgency === 'high' || history.issueCount > 3) {
const escalation = await agent.call('escalation-agent.createCase', {
ticketId: ctx.input.ticketId,
reason: 'high urgency or repeated incidents',
});
await agent.call('notifications.sendAlert', { caseId: escalation.caseId });
ctx.note(`Escalated ticket ${ctx.input.ticketId} to ${escalation.caseId}`, ['triage', 'escalation']);
return { status: 'escalated', caseId: escalation.caseId };
}
return { status: 'queued', sentiment, history };
});a.RegisterReasoner("triage_ticket", func(ctx context.Context, input map[string]any) (any, error) {
sentiment, _ := a.Call(ctx, "sentiment-agent.analyze_text", map[string]any{"text": input["message"]})
history, _ := a.Call(ctx, "customer-history.recent_issues", map[string]any{"customer_id": input["customer_id"]})
if sentiment["urgency"] == "high" {
escalation, _ := a.Call(ctx, "escalation-agent.create_case", map[string]any{
"ticket_id": input["ticket_id"],
"reason": "high urgency or repeated incidents",
})
return map[string]any{"status": "escalated", "case_id": escalation["case_id"]}, nil
}
return map[string]any{"status": "queued", "sentiment": sentiment, "history": history}, nil
})What just happened
- The caller referenced services by logical target, not URL
- Workflow context flowed through every child call automatically
- Every child execution became a node in the same DAG
- The note on escalation was attached to the current execution timeline
Example workflow proof:
{
"target": "support-triage.triage_ticket",
"status": "completed",
"children": [
{ "target": "sentiment-agent.analyze_text", "status": "completed" },
{ "target": "customer-history.recent_issues", "status": "completed" },
{ "target": "escalation-agent.create_case", "status": "completed" },
{ "target": "notifications.send_alert", "status": "completed" }
]
}Service Discovery
Find agents, reasoners, and skills at runtime through the control plane discovery API, and publish selected capabilities through ARD when they need to be discoverable outside the deployment.
Shared Memory
Distributed key-value and vector memory with four isolation scopes for cross-agent state sharing.