Skip to Content
InvokeBridge Libraries

Bridge Libraries

Bridge libraries let you invoke Golem agents from non-Golem applications in a type-safe way. Golem generates self-contained Rust crates, Node.js TypeScript and Effect TypeScript npm packages, Scala sbt projects, and MoonBit modules that implement a fully typed client for specific agents.

Methods without streams use the existing REST invocation API and remain compatible with previously generated clients. A method whose input or output contains a stream instead opens the public streaming WebSocket endpoint described below.

Configuring Bridge Generation in golem.yaml

To generate bridge libraries, add a bridge section to your golem.yaml:

bridge: ts: external: agents: - InboxAgent - EscalationAgent effect: external: agents: WorkflowAgent rust: external: agents: AuditAgent scala: external: agents: BillingAgent moonbit: external: agents: ReportAgent

This will generate TypeScript packages for InboxAgent and EscalationAgent, an Effect-native TypeScript package for WorkflowAgent, a Rust crate for AuditAgent, a Scala sbt project for BillingAgent, and a MoonBit module for ReportAgent.

Using Bridge Libraries

Once generated, you can use the bridge libraries like any other typed client. First configure the connection, then interact with agents:

import { CounterAgent, configure } from 'counter-agent-client/counter-agent-client.js' configure({ server: { type: 'local' }, application: 'bridgetest', environment: 'local' }) const c1 = await CounterAgent.get('c1') const value = await c1.increment()

Streaming Methods

Generated clients preserve streams wherever they occur in a method’s typed input or output, including nested values and multiple sibling streams. The examples below assume a MediaAgent with input-only upload, output-only transcribe, and mixed transform methods. The generated method names and parameter order follow the agent definition; there is no separate streaming method suffix.

use futures_util::{stream, StreamExt}; use golem_client::invocation_session::AgentStream; use media_agent_client::{configure, GolemServer, MediaAgent}; configure(GolemServer::Local, "media-app", "prod"); let agent = MediaAgent::get("studio-a").await?; // Input-only: AgentStream::input accepts a Stream<Item = Result<T, E>>. let samples = AgentStream::input(stream::iter([ Ok::<_, std::io::Error>(1_u8), Ok(2_u8), ])); agent.upload(samples).await?; // Output-only: AgentStream implements futures_core::Stream. let mut words = agent.transcribe().await?; while let Some(word) = words.next().await { println!("{}", word?); } // Mixed: input ownership moves into the invocation and output is affine. let input = AgentStream::input(stream::iter([Ok::<_, std::io::Error>(10_u8)])); let mut output = agent.transform(input).await?; if let Some(first) = output.next().await { println!("{}", first?); } output.cancel(); // Dropping an unread output also sends consumer-drop cancellation.

Raw binary lanes

Direct stream<u8> and stream<binary> values use binary WebSocket frames rather than JSON/base64 for their item data. stream<u8> treats every byte as one logical item. stream<binary> treats each frame chunk as an AgentBinary item with bytes and an optional MIME type. Streams nested inside another value still retain their structural position, while their bytes travel on the corresponding binary lane.

For example, a video-processing client can stream a multi-megabyte recording without constructing one enormous JSON value:

import { createReadStream } from 'node:fs' import { agentStream, type AgentBinary } from '@golemcloud/golem-ts-bridge' async function* videoChunks(path: string): AsyncGenerator<AgentBinary> { for await (const chunk of createReadStream(path, { highWaterMark: 1024 * 1024 })) { yield { bytes: new Uint8Array(chunk), mimeType: 'video/mp4' } } } // A 250 MiB video is sent as 1 MiB binary items with normal stream back-pressure. const preview = await agent.makePreview(agentStream(videoChunks('recording.mp4'))) for await (const audioChunk of preview) { await audioSink.write(audioChunk.bytes) // e.g. AgentBinary with audio/aac }

Use a stream of structured records instead when each item needs additional application metadata.

Public Streaming Transport

Streaming bridges connect to:

GET /v1/agents/invoke-agent-session Sec-WebSocket-Protocol: golem.agent-invocation.v1 Authorization: Bearer <token>

The URL uses ws: for an HTTP server and wss: for HTTPS. Local and Cloud select the standard server and credentials; a custom server configuration supplies its own base URL and bearer token. TypeScript and Effect TypeScript use server: { type: "custom", url: "https://golem.example.com", token }; the corresponding variants are GolemServer::Custom { url: reqwest::Url, token } in Rust, GolemServer.Custom(url, token) in Scala, and @runtime.Custom(url, token) in MoonBit. Authentication and authorization are checked both when starting and when reattaching to a session. The generated runtime negotiates the required golem.agent-invocation.v1 subprotocol automatically; applications should not open this socket directly.

The Stream Session Public Protocol v1 reference specifies every JSON message, binary envelope, stable error code, limit, token, and resume rule.

Durability, resume, and cancellation

  • Losing the WebSocket or stopping a client detaches the transport; it does not by itself cancel the durable invocation.
  • A resume presents the opaque session token and the last delivered output cursors. The server replays output after those cursors in per-stream order.
  • Delivery across a detach is at least once. If the client received an item but its cursor was not durably checkpointed, that item can be replayed. Consumers that perform external side effects should deduplicate them.
  • Normal resume requires the old attachment to be detached. An authenticated takeover advances the attachment epoch and fences a still-connected old client; stale frames are rejected.
  • Cancelling an output stream, abandoning its reader, or aborting an invocation is explicit and propagates to the session. A producer failure remains a stream-local error unless it causes the invocation itself to fail.
  • Every stream is affine: it has one consumer. Reading twice, reusing an input after passing it to a method, or concurrently pulling where unsupported fails rather than silently duplicating data.

The generated bridges reconnect transiently using in-memory session state. For an operator-controlled checkpoint that survives a CLI process exit, use the CLI workflow below. See Operating Durable Stream Sessions for service limits and operational recovery.

CLI Streaming and Checkpoints

Use - for exactly one direct stream input. The default value stdin format parses one source-language value per line; value stdout emits a sequence of structured lifecycle documents:

# JSON/value stdin and stdout for stream<string>. printf '"first"\n"second"\n' | golem agent invoke 'MediaAgent("studio-a")' index_words - \ --stdin-format value --stdout-format value --format json

Raw mode is restricted to one direct stream<u8> or stream<binary> input or output. It avoids JSON encoding, making it suitable for multi-megabyte audio and video:

# Raw binary stdin to a direct stream<binary> parameter. golem agent invoke 'MediaAgent("studio-a")' upload_video - \ --stdin-format raw < recording.mp4 # Raw binary stdout from one direct stream<u8> or stream<binary> result. golem agent invoke 'MediaAgent("studio-a")' render_audio \ --stdout-format raw > soundtrack.aac

For stream<binary>, raw stdin forms fixed 64 KiB logical items except for the final shorter item. For stream<u8>, each byte is an item. Raw stdout contains only bytes, so diagnostics remain off stdout.

--save-session is the CLI checkpoint option. It atomically records opaque session identity, pending operation, and delivered output cursors, but not credentials or stream values:

# Start and continuously checkpoint. golem agent invoke 'MediaAgent("studio-a")' render_audio \ --stdout-format raw --save-session render-session.json > soundtrack.aac # Resume only after the previous transport detached. golem agent invoke 'MediaAgent("studio-a")' render_audio \ --stdout-format raw --resume-session render-session.json >> soundtrack.aac # Or explicitly fence a transport that may still be attached. golem agent invoke 'MediaAgent("studio-a")' render_audio \ --stdout-format raw --takeover-session render-session.json >> soundtrack.aac

The CLI does not retry automatically. On resumed input, stdin begins after the server’s durable input high-water mark; bytes that had not reached that durable point before the previous process exited cannot be recovered from the checkpoint. Keep checkpoint files private.

Current Boundary

This feature is the generated external bridge and CLI invocation-session WebSocket transport. It does not provide Durable Streams URLs, catch-up HTTP endpoints, long polling, Server-Sent Events (SSE), browser streaming, or an openDurableStream API. Those are future-facing possibilities, not supported interfaces in this release.

Configuration Options

The TypeScript, Effect TypeScript, Rust, Scala and MoonBit bridge libraries require three configuration parameters:

ParameterDescription
ServerLocal for the default local Golem server, Cloud for Golem Cloud, or a custom deployment URL and bearer token
Application nameThe app: key from your golem.yaml
EnvironmentThe target environment (e.g., local, staging, prod)

Type Safety

The generated libraries define all custom data types used in parameters and return types of agent methods. This means you get full compile-time type checking and IDE autocompletion when interacting with agents from external applications.

Bridge libraries follow the same conventions as agent-to-agent RPC, providing get, getPhantom, and newPhantom constructors for agent instances.