Documentation Index
Fetch the complete documentation index at: https://www.truefoundry.com/llms.txt
Use this file to discover all available pages before exploring further.
Quick start
SetGATEWAY_BASE_URL and TFY_API_KEY in your environment (see Authentication), then run:
import os
from truefoundry import AgentClient
client = AgentClient(
base_url=os.environ["GATEWAY_BASE_URL"],
api_key=os.environ["TFY_API_KEY"],
)
for event in client.responses.stream(
model="{MODEL_NAME}",
input=[{"role": "user", "content": "Say hello in one sentence."}],
):
print(event)
{"type":"response.created","response_id":"my-tenant.8ea362c2-8d31-460f-a982-11aab4adead1","created_at":"2026-05-21T10:00:00.000Z","sequence_id":1}
{"type":"agent.message","role":"assistant","content":"Hello!","finish_reason":"stop","execution_id":"exec-1","sequence_id":2}
{"type":"response.done","status":"done","sequence_id":3}
Continue a turn
Passprevious_response_id from the prior response.created event on the next request. The second turn reuses agent state from the first response.
import os
from truefoundry import AgentClient
client = AgentClient(
base_url=os.environ["GATEWAY_BASE_URL"],
api_key=os.environ["TFY_API_KEY"],
)
previous_response_id = None
for event in client.responses.stream(
model="{MODEL_NAME}",
input=[{"role": "user", "content": "What is 1 + 2?"}],
):
print(event)
if event.type == "response.created":
previous_response_id = event.response_id
for event in client.responses.stream(
model="{MODEL_NAME}",
previous_response_id=previous_response_id,
input=[{"role": "user", "content": "What was my previous question?"}],
):
print(event)
{"type":"response.created","response_id":"my-tenant.f9e8d7c6-b5a4-3210-fedc-ba0987654321","created_at":"2026-05-21T10:01:00.000Z","sequence_id":1}
{"type":"agent.message","role":"assistant","content":"Your previous question was: What is 1 + 2?","finish_reason":"stop","execution_id":"exec-1","sequence_id":2}
{"type":"response.done","status":"done","sequence_id":3}
Capability examples
These examples show how to handle specific Agent Harness capabilities from your client code. Each builds on the basic streaming pattern shown above.Sandbox: multi-turn with persistent sandbox
Sandbox: multi-turn with persistent sandbox
import os
from truefoundry import AgentClient
client = AgentClient(
base_url=os.environ["GATEWAY_BASE_URL"],
api_key=os.environ["TFY_API_KEY"],
)
previous_response_id = None
# Turn 1
for event in client.responses.stream(
model="{MODEL_NAME}",
sandbox={"enabled": True},
input=[{"role": "user", "content": "Write hello to /tmp/greeting.txt using the sandbox."}],
):
if event.type == "response.created":
previous_response_id = event.response_id
if event.type == "sandbox.created":
print(f"sandbox.created: {event.sandbox_id}")
print(f"previous_response_id={previous_response_id}")
# Turn 2 — same sandbox as turn 1 (no new sandbox.created)
for event in client.responses.stream(
model="{MODEL_NAME}",
sandbox={"enabled": True},
previous_response_id=previous_response_id,
input=[{"role": "user", "content": "Read /tmp/greeting.txt and tell me what it says."}],
):
print(event)
sandbox.created: my-tenant.a1b2c3d4-e5f6-7890-abcd-ef1234567890
previous_response_id=my-tenant.8ea362c2-8d31-460f-a982-11aab4adead1
{"type":"response.created","response_id":"my-tenant.f9e8d7c6-b5a4-3210-fedc-ba0987654321","created_at":"2026-05-21T10:01:00.000Z","sequence_id":1}
{"type":"agent.message","role":"assistant","finish_reason":"tool_calls","tool_calls":[{"id":"call_7xk2","type":"function","function":{"name":"exec","arguments":"{\"command\":\"cat /tmp/greeting.txt\"}"},"tool_info":{"mcp_server_id":"sandbox","mcp_server_name":"sandbox","original_tool_name":"exec"}}],"execution_id":"exec-1","sequence_id":2}
{"type":"agent.message","role":"tool","tool_call_id":"call_7xk2","content":"hello\n","execution_id":"exec-1","sequence_id":3}
{"type":"agent.message","role":"assistant","content":"The file says hello.","finish_reason":"stop","execution_id":"exec-1","sequence_id":5}
{"type":"response.done","status":"done","sequence_id":6}
Ask User Questions: handling tool.response_required
Ask User Questions: handling tool.response_required
import json
import os
from truefoundry import AgentClient
client = AgentClient(
base_url=os.environ["GATEWAY_BASE_URL"],
api_key=os.environ["TFY_API_KEY"],
)
previous_response_id = None
execution_id = None
pending_tool_call_id = None
for event in client.responses.stream(
model="{MODEL_NAME}",
input=[
{
"role": "user",
"content": "Deploy booking-bot to production.",
}
],
):
if event.type == "response.created":
previous_response_id = event.response_id
if event.type == "agent.message" and event.role == "assistant" and event.tool_calls:
for tool_call in event.tool_calls:
if (
tool_call.tool_info.mcp_server_id != "ask-user-question"
or tool_call.function.name != "ask_user_question"
):
continue
execution_id = event.execution_id
pending_tool_call_id = tool_call.id
args = json.loads(tool_call.function.arguments)
print("ask_user_question tool call:")
print(f" tool_call_id: {tool_call.id}")
print(f" question: {args['question']}")
for i, option in enumerate(args["options"], start=1):
print(f" option {i}: {option}")
if event.type == "tool.response_required":
execution_id = event.execution_id
pending_tool_call_id = event.tool_calls[0].id
print(event)
user_answer = "tfy-prod-us (production)"
for event in client.responses.stream(
model="{MODEL_NAME}",
previous_response_id=previous_response_id,
input=[
{
"type": "tool.client_side_response",
"execution_id": execution_id,
"tool_call_id": pending_tool_call_id,
"content": user_answer,
}
],
):
print(event)
{"type":"response.created","response_id":"my-tenant.8ea362c2-8d31-460f-a982-11aab4adead1","created_at":"2026-05-21T10:00:00.000Z","sequence_id":1}
{"type":"agent.message","role":"assistant","finish_reason":"tool_calls","tool_calls":[{"id":"call_a1b2","type":"function","function":{"name":"ask_user_question","arguments":"{\"question\":\"Which cluster should I deploy booking-bot to?\",\"options\":[\"tfy-prod-us (production)\",\"tfy-staging-us (staging)\",\"tfy-dev-us (development)\"]}"},"tool_info":{"mcp_server_id":"ask-user-question","mcp_server_name":"ask-user-question","original_tool_name":"ask_user_question","is_client_side":true}}],"execution_id":"exec-1","sequence_id":2}
{"type":"tool.response_required","execution_id":"exec-1","tool_calls":[{"id":"call_a1b2"}],"sequence_id":3}
{"type":"response.created","response_id":"my-tenant.f9e8d7c6-b5a4-3210-fedc-ba0987654321","created_at":"2026-05-21T10:01:00.000Z","sequence_id":1}
Tool Call Approvals: handling tool.approval_required
Tool Call Approvals: handling tool.approval_required
import json
import os
from truefoundry import AgentClient
client = AgentClient(
base_url=os.environ["GATEWAY_BASE_URL"],
api_key=os.environ["TFY_API_KEY"],
)
previous_response_id = None
pending_approvals = {}
for event in client.responses.stream(
model="{MODEL_NAME}",
input=[{"role": "user", "content": "Restart the billing service in prod-us."}],
):
if event.type == "response.created":
previous_response_id = event.response_id
if event.type == "agent.message" and getattr(event, "tool_calls", None):
for tool_call in event.tool_calls:
tool_info = tool_call.get("tool_info", {})
if tool_info.get("is_approval_required"):
pending_approvals[tool_call["id"]] = {
"execution_id": event.execution_id,
"tool_name": tool_info.get("original_tool_name", tool_call["function"]["name"]),
"arguments": json.loads(tool_call["function"]["arguments"] or "{}"),
}
if event.type == "tool.approval_required":
decisions = []
for tool_call in event.tool_calls:
tool_call_id = tool_call["id"]
approval = pending_approvals[tool_call_id]
print("Approval required")
print("Tool:", approval["tool_name"])
print("Arguments:", approval["arguments"])
# Replace this with your approval UI.
decisions.append(
{
"type": "tool.approval",
"execution_id": approval["execution_id"],
"tool_call_id": tool_call_id,
"approval": {"status": "allow"},
}
)
for followup in client.responses.stream(
model="{MODEL_NAME}",
previous_response_id=previous_response_id,
input=decisions,
):
print(followup)
Approval required
Tool: restart_application
Arguments: {'workspace_fqn': 'prod-us', 'application_name': 'billing'}
If multiple tool calls are waiting for approval in the same turn, send approval decisions for all pending tool calls in one batch.
Generative UI: rendering OpenUI blocks
Generative UI: rendering OpenUI blocks
import os
import re
from truefoundry import AgentClient
client = AgentClient(
base_url=os.environ["GATEWAY_BASE_URL"],
api_key=os.environ["TFY_API_KEY"],
)
OPENUI_BLOCK = re.compile(r"```openui\n(.*?)```", re.DOTALL)
content_by_execution = {}
for event in client.responses.stream(
model="{MODEL_NAME}",
input=[
{
"role": "user",
"content": "Show a dashboard comparing request volume and error rate across three production models.",
}
],
):
if event.type == "agent.message" and getattr(event, "content", None):
execution_id = event.execution_id
content_by_execution[execution_id] = content_by_execution.get(execution_id, "") + event.content
for match in OPENUI_BLOCK.finditer(content_by_execution[execution_id]):
openui_code = match.group(1)
print("OpenUI block ready to render:")
print(openui_code)
if event.type == "response.done":
print("response complete")
OpenUI block ready to render:
root = Stack([title, cards, chart, table])
title = TextContent("Production Model Health", "large-heavy")
...
response complete
Subagents: tracking parallel agent executions
Subagents: tracking parallel agent executions
Each agent execution — root and subagent — has a unique Key observations:
execution_id in the stream. Subagent lifecycle is tracked through agent.created and agent.done events. Events from parallel subagents arrive interleaved, so use execution_id to associate events with the correct agent.{"type":"response.created","response_id":"bom.4bmxkq.992b6b28-78c2-4e23-854f-4f6d909ee7b7","created_at":"2026-05-22T09:23:48.274Z","sequence_id":1}
{"type":"mcp.initialize","execution_id":"3f564412-af36-4c46-b231-8597e0f5a8e3","content":[{"mcp_server_id":"psmzgmhjj9z40tohgx6s7k7r","mcp_server_name":"github","session_id":"36e36d21-d381-4da0-8fca-e6a428484050"}],"sequence_id":2}
...
{"type":"agent.created","execution_id":"95f43ed3-e611-4476-9be4-c2a367339a5a","parent":{"tool_call_id":"toolu_01V4vWNBRHddKMGFFp7GXxTt","execution_id":"3f564412-af36-4c46-b231-8597e0f5a8e3"},"agent_info":{"type":"agent-defined","name":"bob-prs","input":"Search for pull requests merged by GitHub user \"bob\" ..."},"created_at":"2026-05-22T09:24:12.122Z","sequence_id":512}
{"type":"agent.created","execution_id":"fbab8550-7750-4e0b-b930-f180c58370a6","parent":{"tool_call_id":"toolu_014DoEbMRmcGyTYUUVM934zi","execution_id":"3f564412-af36-4c46-b231-8597e0f5a8e3"},"agent_info":{"type":"agent-defined","name":"alice-prs","input":"Search for pull requests merged by GitHub user \"alice\" ..."},"created_at":"2026-05-22T09:24:12.123Z","sequence_id":513}
...
{"type":"agent.message","execution_id":"95f43ed3-e611-4476-9be4-c2a367339a5a","content":"I found **", "created_at":"2026-05-22T09:24:16.936Z","sequence_id":610}
...
{"type":"agent.message","execution_id":"fbab8550-7750-4e0b-b930-f180c58370a6","content":"No PRs", "created_at":"2026-05-22T09:24:17.211Z","sequence_id":612}
...
{"type":"agent.done","execution_id":"fbab8550-7750-4e0b-b930-f180c58370a6","output":{"role":"assistant","content":"No PRs merged by alice in the last 7 days.","type":"agent.message","execution_id":"fbab8550-7750-4e0b-b930-f180c58370a6","finish_reason":"stop"},"parent":{"tool_call_id":"toolu_014DoEbMRmcGyTYUUVM934zi","execution_id":"3f564412-af36-4c46-b231-8597e0f5a8e3"},"sequence_id":1497}
{"type":"agent.done","execution_id":"95f43ed3-e611-4476-9be4-c2a367339a5a","output":{"role":"assistant","content":"Found 2 PRs merged by bob in the last 7 days.\n\n- #4475054266: misc: add responseUpdatedAt to agent response store\n Merged: 2026-05-20\n ...","type":"agent.message","execution_id":"95f43ed3-e611-4476-9be4-c2a367339a5a","finish_reason":"stop"},"parent":{"tool_call_id":"toolu_01V4vWNBRHddKMGFFp7GXxTt","execution_id":"3f564412-af36-4c46-b231-8597e0f5a8e3"},"sequence_id":679}
{"type":"agent.message","execution_id":"3f564412-af36-4c46-b231-8597e0f5a8e3","content":"Here's a comprehensive summary of work merged by the **agent","sequence_id":1543}
...
{"type":"agent.message","execution_id":"3f564412-af36-4c46-b231-8597e0f5a8e3","finish_reason":"stop","sequence_id":1596}
{"type":"response.done","status":"done","sequence_id":1597}
- The root agent has
execution_id"3f564412-...". - Subagent
bob-prsis created with its ownexecution_id"95f43ed3-..."and subagentalice-prswith"fbab8550-...". - Each
agent.createdevent includes aparentfield linking back to the root agent’sexecution_idand the originatingtool_call_id. - Events from both subagents arrive interleaved — use
execution_idto demux. - Each subagent completes with
agent.done, which contains the finaloutput. - The root agent resumes after all subagents finish, producing the combined answer.
- The response ends with
response.done.