AgentContext API Reference
Complete reference for the AgentContext class — task input, credentials, logging, progress, browser control, and result submission.
The AgentContext is the primary interface your agent uses to interact with the Grexal platform. It is passed to your run() function automatically.
ctx.task()
Returns the task input provided by the orchestrator.
# Python
task = await ctx.task()
ticker = task["ticker"]// TypeScript
const task = await ctx.task();
const ticker = task.ticker;Returns: A JSON-deserialized object (Python dict, TypeScript Record<string, unknown>). The shape is entirely defined by the orchestrator's input for this specific run — there is no fixed schema.
Behavior:
- Parses
GREXAL_TASK_INPUTfrom the environment on first call, caches the result for subsequent calls. - If
GREXAL_TASK_INPUTis missing or contains invalid JSON, raisesGrexalErrorwith codeinvalid_task_input. - Can be called multiple times — returns the same cached object.
- Does not make an HTTP call. The task input is available locally in the environment.
ctx.connect(connection_id)
Returns credentials for a declared connection. The return type depends on the connection's auth_mode as declared in grexal.json.
# Python
weather = await ctx.connect("weather_api")
api_key = weather.get("api_key")// TypeScript
const weather = await ctx.connect("weather_api");
const apiKey = weather.get("api_key");Parameter:
connection_id(string) — Must match theidof a connection declared ingrexal.json.
Returns: A ConnectionCredential object with a .get(field_name) method. The available fields depend on the auth mode:
secret_input connections
Fields match the fields[].name values declared in the manifest.
conn = await ctx.connect("weather_api")
conn.get("api_key") # → "wk_live_abc123"
conn.get("api_secret") # → "ws_live_xyz789"oauth_redirect connections
Standard OAuth token fields:
conn = await ctx.connect("google_drive")
conn.get("access_token") # → "ya29.a0ARrdaM..."
conn.get("token_type") # → "Bearer"
conn.get("scopes") # → "https://www.googleapis.com/auth/drive.readonly"The access token is guaranteed to be valid at the time of injection — the platform handles refresh before the sandbox starts.
file_upload connections
Returns the filesystem path where the uploaded file was injected:
conn = await ctx.connect("gcp_service_account")
conn.get("file_path") # → "/tmp/grexal/gcp_service_account/service-account.json"The file is written to /tmp/grexal/{connection_id}/{original_filename} inside the sandbox.
browser_login connections
ctx.connect() is not used for browser_login connections. Browser sessions are not extractable credentials — they live in the sandbox's browser. Use ctx.request_takeover() instead.
Calling ctx.connect() with a browser_login connection ID raises GrexalError with code invalid_auth_mode.
Behavior:
- Does not make an HTTP call. Credentials are pre-injected into the sandbox environment before execution starts.
- Can be called multiple times with the same ID — returns the same cached object.
- Never blocks. The pre-collection model guarantees all credentials are present before the agent starts.
Errors:
connection_not_found— theconnection_iddoes not match any connection declared ingrexal.json.invalid_auth_mode— called with abrowser_loginconnection ID (userequest_takeover()instead).
ctx.submit(result)
Submits the agent's result to the platform and signals task completion.
# Python
await ctx.submit({"recommendation": "buy", "confidence": 0.82})// TypeScript
await ctx.submit({ recommendation: "buy", confidence: 0.82 });Parameter:
result— Any JSON-serializable value. Objects, arrays, strings, numbers, booleans, and null are all valid. Binary data must be base64-encoded. Maximum serialized size: 10 MB.
Behavior:
- Makes an HTTP POST to the platform with the result payload.
- Can only be called once per run. The first call succeeds and marks the run as completed. Subsequent calls raise
GrexalErrorwith codealready_submitted. - Does not terminate the process. The agent can perform cleanup after calling
submit(). However, the sandbox will be destroyed shortly after. - Awaiting
submit()confirms the platform received the result. If the HTTP call fails, the SDK retries up to 3 times with exponential backoff. If all retries fail, raisesGrexalErrorwith codesubmit_failed.
Relationship with return:
- If
submit()is called explicitly, the return value ofrun()is ignored. - If
submit()is never called, the return value ofrun()is used as the result (the SDK callssubmit()internally). - If neither
submit()is called nor a value is returned, the run fails withno_result_submitted.
ctx.log(message)
Sends a log message to the platform for real-time display in the UI and developer debugging.
# Python
await ctx.log("Processing 47 invoices...")
await ctx.log("Retrying API call (attempt 2/3)")// TypeScript
await ctx.log("Processing 47 invoices...");
await ctx.log("Retrying API call (attempt 2/3)");Parameter:
message(string) — Free-form log message. Maximum length: 4096 characters. Messages exceeding this limit are truncated.
Behavior:
- Fire-and-forget. The SDK sends the request but does not block on a response.
awaitresolves as soon as the message is queued for delivery, not when the platform acknowledges it. - Logs are batched internally — the SDK buffers messages and sends them in batches (every 500ms or every 20 messages, whichever comes first) to reduce HTTP overhead.
- Can be called after
submit(). Post-submission logs are still delivered (useful for cleanup diagnostics) until the sandbox is destroyed. - Log messages are visible to the agent developer in the platform dashboard for debugging. They are also forwarded to the orchestrator, which may use them to assess agent progress.
ctx.progress(value)
Reports execution progress to the platform. Displayed in the UI as a progress indicator.
# Python
await ctx.progress(0.25) # 25% complete
await ctx.progress(0.80) # 80% complete// TypeScript
await ctx.progress(0.25);
await ctx.progress(0.80);Parameter:
value(number) — A float between0.0and1.0inclusive, representing the fraction of work completed. Values outside this range are clamped.
Behavior:
- Sent alongside the log batch. Progress updates are coalesced — if multiple
progress()calls occur within the same batch window, only the latest value is sent. - Fire-and-forget, same as
log(). - Progress is informational. It does not affect execution flow, billing, or the result. An agent that never calls
progress()works fine — there's just no progress indicator in the UI.
ctx.browser()
Returns a browser automation handle for agents running in a desktop sandbox. Only available when runtime.sandbox_template is "desktop" in grexal.json.
# Python
browser = ctx.browser()
await browser.goto("https://maps.google.com")
element = await browser.query_selector(".search-box")
await browser.click(".search-box")
await browser.type(".search-box", "coffee shops nearby")// TypeScript
const browser = ctx.browser();
await browser.goto("https://maps.google.com");
const element = await browser.querySelector(".search-box");
await browser.click(".search-box");
await browser.type(".search-box", "coffee shops nearby");Returns: A BrowserHandle object that wraps the desktop sandbox's browser automation interface.
BrowserHandle methods
| Method | Description |
|---|---|
goto(url) | Navigate to a URL |
query_selector(selector) | Find an element by CSS selector. Returns the element or None/null |
query_selector_all(selector) | Find all matching elements |
click(selector) | Click an element |
type(selector, text) | Type text into an element |
screenshot() | Capture a screenshot, returns PNG bytes |
wait_for_selector(selector, timeout_ms?) | Wait for an element to appear (default timeout: 30s) |
evaluate(js_expression) | Execute JavaScript in the page context and return the result |
url() | Get the current page URL |
content() | Get the page HTML content |
Errors:
sandbox_not_desktop— called in a standard (non-desktop) sandbox. Check thatruntime.sandbox_templateis"desktop"ingrexal.json.
ctx.browser() is synchronous — it returns the handle immediately. The handle's methods are all async. The browser instance is shared across the entire run; calling ctx.browser() multiple times returns the same handle.
ctx.request_takeover(connection_id, reason)
Pauses agent execution and requests the user to take interactive control of the sandbox's browser. Used exclusively with browser_login connections. See Browser Login for the full flow.
# Python
async def run(ctx: AgentContext):
browser = ctx.browser()
await browser.goto("https://accounts.google.com")
if await browser.query_selector(".sign-in-button"):
await ctx.request_takeover(
connection_id="google_account",
reason="Please log into your Google account"
)
# Execution resumes here after the user finishes
# Browser is now logged in — continue automation
await browser.goto("https://maps.google.com/d/")Parameters:
connection_id(string) — Must match theidof abrowser_loginconnection declared ingrexal.json.reason(string) — User-facing message explaining what they need to do. Maximum length: 500 characters.
Behavior:
- Blocks until the user completes the takeover (clicks "Finish" in the Grexal UI) or the takeover times out.
- While the user has control, the agent cannot execute code, take screenshots, or observe the browser.
- Can be called multiple times in a single run (e.g., logging into two different services sequentially). Each call blocks independently.
Timeout: The user has 5 minutes to complete the takeover. If the timeout expires, request_takeover() raises GrexalError with code takeover_timeout, the sandbox is killed, and the run is marked as cancelled.
Errors:
connection_not_found— theconnection_iddoes not match any connection ingrexal.json.invalid_auth_mode— the connection is notbrowser_login.sandbox_not_desktop— not running in a desktop sandbox.takeover_timeout— user did not complete the takeover within 5 minutes.