Skip to main content
A tool’s response is not the entity’s shape, and nothing is inferred. @withgateway/sdk/worlds does two things: records the calls while the agent runs against the real vendor, and puts the records into a world through the route you declare — your own transform, an ingest.toml mapping, or, for a tool whose result is the vendor’s response body, the operation’s projection.

The record

One JSON object per call. tool, args and result are required; the rest is optional and travels with the record.
A line of gateway worlds session export ({seq, kind, tool, args, result, error, completedAt}) is accepted as it is. parseToolCallRecord(raw) tells you what the runtime will do with a record before you send it: { record, skip, refusal } — a seed line or a failed call is a skip with its reason, a record with no tool name or no result is a refusal.

captureToolCalls(tools, sink, options?)

Wraps a name → async function map — the shape the Vercel AI SDK’s tools and a WorldToolkit’s impls share — and returns the same map. Each wrapped function calls the original, returns exactly what it returned, and writes one record to the sink. An error is thrown through untouched and recorded with error instead of result, which ingest later skips and counts. The agent changes nothing. options.session and options.source are stamped on every record.

ingest(world, records, options?)

world is a local directory or a platform slug (slug or slug@ref). records are tool-call records, or any JSON objects when a mapping reads them as rows. With neither transform nor map, the world’s own ingest.toml applies when it has one; else each record’s tool resolves to an [[operations]] entry of connector.toml and its result is read at the operation’s results path and projected by its [ingest] table (drop, explode, carry_args from the call’s args) as a captured response is. The report says via: "operations", and its note says that this is the operation’s projection, not a mapping. ingestToolCalls(world, records, options) is that route by name.
  • A directory is written in place (data/initial.json, the snapshot rebuilt) through the bundled schema runtime, which needs a python3 ≥ 3.12 on the machine. Mappings and projections run there; a transform runs where you are.
  • A slug is pulled to a temporary directory, the rows produced and proven there, and pushed through the chunked data batch lane as a new version.
The report:
A record no mapping source claims, or a tool that is not an operation, is a refusal naming it, and with any refusal nothing is written. A row the world’s contract refuses throws, as data import would, naming the entity, index and field. A transform that fails, times out or returns something other than {entity: [rows]} throws with its stderr. The mapping as an object:
Redaction is optional and off by default: rows land as they were produced. Use redact: "apply" for a world built from real users. See Keep real data out.

From traces

Instrumented agents already record every tool call as a TOOL observation. Convert those instead of intercepting:
toolCallsFromObservations takes name as the tool, input as the args, output as the result and startTime as the time, parses serialized JSON back, and sets source to trace:<traceId>/<observationId>. Only TOOL observations convert; an ERROR-level one becomes a failed call. With tools (the world’s operations) every other tool’s observation lands in unknownTools instead — the filter the operations route uses. The CLI form is gateway worlds data calls pull <out> --from traces, then gateway worlds data ingest; see Put data in a world.

Python

The same two halves ship in gatewaysdk:
ingest_tool_calls(world, records) is the operations route by name. Both take redact ("off" by default), dry_run, mode, message and the keys.

Where to go next