Skip to main content

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

Set GATEWAY_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)
Example output (truncated):
{"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

Pass previous_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)
Example output from turn 2 (truncated):
{"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.
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)
Output
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}
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)
Example output (truncated):
{"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}
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)
Example output:
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.
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")
Example output:
OpenUI block ready to render:
root = Stack([title, cards, chart, table])
title = TextContent("Production Model Health", "large-heavy")
...
response complete
Each agent execution — root and subagent — has a unique 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}
Key observations:
  • The root agent has execution_id "3f564412-...".
  • Subagent bob-prs is created with its own execution_id "95f43ed3-..." and subagent alice-prs with "fbab8550-...".
  • Each agent.created event includes a parent field linking back to the root agent’s execution_id and the originating tool_call_id.
  • Events from both subagents arrive interleaved — use execution_id to demux.
  • Each subagent completes with agent.done, which contains the final output.
  • The root agent resumes after all subagents finish, producing the combined answer.
  • The response ends with response.done.