How Python-Backed Skills Use Typed Host Requests in Prime Agent
Python-backed skills in Prime Agent communicate with the TypeScript host runtime by calling rlm.host_request() with a type string and JSON-serializable payload, which the kernel routes through the typed HostRequestHandlers map defined in packages/coding-agent/src/core/kernel/shared.ts.
The Prime Agent framework allows developers to write skills in Python while leveraging host-side services like web search, file system access, and sub-agent spawning. This integration relies on a strictly typed host request protocol that bridges the IPython REPL environment with the JavaScript kernel.
Understanding the Typed Host Request Architecture
The architecture separates concerns between the Python execution environment and the host runtime. Python skills request capabilities through typed messages, while the host implements handlers that fulfill these requests asynchronously.
The Host Request Type System
At the core of this system is the type definition found in packages/coding-agent/src/core/kernel/shared.ts. This file establishes the contract between Python skills and the host runtime:
// packages/coding-agent/src/core/kernel/shared.ts
export type HostRequestHandler = (
payload: Record<string, unknown>
) => Promise<Record<string, unknown>>;
export type HostRequestHandlers = Record<string, HostRequestHandler>;
The HostRequestHandler type defines a function that accepts a Record<string, unknown> payload and returns a promise resolving to another record. This loose typing on the host side allows for dynamic request routing while maintaining type safety through the dispatcher pattern.
The HostRequestHandlers type creates a registry map where keys are the request type strings (such as "websearch.query" or "http.fetch") and values are the corresponding handler implementations.
The Python Bridge
The IPython bridge in packages/coding-agent/src/core/tools/ipython.ts exposes the rlm module to Python skills. This module provides the host_request function that serializes Python dictionaries into JSON payloads and dispatches them across the language boundary:
# Inside a Python-backed skill
import rlm
async def fetch_data():
result = await rlm.host_request(
"http.fetch",
{"url": "https://api.example.com/data"}
)
return result
The bridge ensures that all payloads remain JSON-serializable, preserving type information as dictionaries on both sides of the communication channel.
Implementing Typed Host Requests in Python Skills
Python skills initiate host requests by importing the rlm module and awaiting the host_request coroutine. The function signature requires two parameters: the request type string and the payload dictionary.
Basic Request Pattern
When a skill needs to perform a web search, it issues a typed request like this:
# skills/websearch_example.py
import rlm
async def search_primes(query: str):
# Typed host request with specific request type
response = await rlm.host_request(
"websearch.query",
{"query": query, "max_results": 10}
)
# Response is a Python dict matching the handler's return type
return response.get("results", [])
The first argument "websearch.query" serves as the lookup key in the host's HostRequestHandlers registry. The second argument must be a dictionary that serializes to valid JSON, matching the expected schema for that specific request type.
Sub-Agent Creation Requests
Skills can also request the host to spawn sub-agents through typed requests:
# skills/orchestrator.py
import rlm
async def create_worker(model: str):
reply = await rlm.host_request(
"subagent.spawn",
{"name": "worker", "model": model, "timeout": 300}
)
# Typed response contains agent identifier
return reply["agentId"]
The payload structure varies by request type, but all follow the same pattern: a string key identifying the capability and a dictionary containing the parameters.
Registering Handlers on the Host Side
On the TypeScript side, developers register handlers by populating the HostRequestHandlers map. Each entry connects a request type string to an async function that implements the requested functionality.
Handler Registration Example
Consider the implementation of an HTTP fetch handler:
// packages/coding-agent/src/core/tools/http.ts
import { HostRequestHandlers } from "../kernel/shared";
export const httpHandlers: HostRequestHandlers = {
"http.fetch": async ({ url, method = "GET" }) => {
const response = await fetch(url as string, {
method: method as string
});
const body = await response.text();
return {
status: response.status,
body,
headers: Object.fromEntries(response.headers)
};
},
};
The handler receives the payload as Record<string, unknown> and must return a Promise<Record<string, unknown>>. The kernel automatically serializes this return value back to Python as a dictionary.
Integrating Handlers into the Kernel
Handlers are aggregated into a single registry during kernel initialization:
// packages/coding-agent/src/core/kernel/index.ts
import { httpHandlers } from "../tools/http";
import { websearchHandlers } from "../tools/websearch";
const hostHandlers: HostRequestHandlers = {
...httpHandlers,
...websearchHandlers,
// Additional handlers merged here
};
This centralized registration allows the dispatch mechanism to route incoming requests from Python skills to the appropriate TypeScript implementation.
Request Flow and Data Flow
The execution flow follows a strict async request-response pattern across the language boundary:
- Request Initiation: The Python skill calls
await rlm.host_request("type", payload) - Serialization: The IPython bridge serializes the payload to JSON and emits a
host_requestevent - Dispatch: The kernel receives the event, extracts the request type, and looks up the handler in the
HostRequestHandlersmap inshared.ts - Execution: The matched handler executes with the deserialized payload
- Response: The handler returns a promise that resolves to a record
- Reply: The kernel emits a
host_replyevent containing the result - Deserialization: The bridge converts the response back to a Python dictionary
- Resolution: The original
rlm.host_request()call returns the dictionary to the skill
This flow ensures that Python skills remain agnostic to the implementation details of host services while maintaining type contracts through the Record<string, unknown> interfaces.
Summary
- Type Safety: The
HostRequestHandlersmap inpackages/coding-agent/src/core/kernel/shared.tsprovides a typed registry for routing requests from Python to TypeScript. - Python API: Skills use
rlm.host_request(type, payload)where the type string identifies the handler and the payload is a JSON-serializable dictionary. - Async Communication: All host requests are asynchronous, returning promises on the TypeScript side and coroutines on the Python side.
- Handler Pattern: Host-side implementations follow the
HostRequestHandlertype signature, acceptingRecord<string, unknown>and returningPromise<Record<string, unknown>>. - Bridge Implementation: The IPython bridge in
packages/coding-agent/src/core/tools/ipython.tsmanages serialization and event transmission between the Python REPL and the JavaScript kernel.
Frequently Asked Questions
What happens if a Python skill requests an unregistered host request type?
If the rlm.host_request() call specifies a type string that does not exist in the HostRequestHandlers map, the kernel will fail to find a matching handler and return an error response to the Python skill. The skill should handle this case by catching exceptions or checking for error keys in the response dictionary.
Can Python skills pass complex objects like NumPy arrays through host requests?
No, payloads must be JSON-serializable. Complex objects like NumPy arrays, Pandas DataFrames, or custom class instances cannot pass directly through rlm.host_request(). Skills should convert such objects to primitive types (lists, dictionaries, strings) before making the request, or use alternative data sharing mechanisms provided by the framework.
How does the host handle concurrent requests from multiple Python skills?
The host request system is fully asynchronous. Each request from Python generates a unique event that the kernel processes independently. TypeScript handlers defined in the HostRequestHandlers map can execute concurrently using standard JavaScript async patterns, and the bridge ensures that responses are routed back to the correct awaiting Python coroutine without blocking the IPython REPL.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →