Skip to content

BridgeClient

The host’s side of Bridge.Rpcs, for an agent that works in a sandbox and is reached through a bridge there. The adapter’s Agent.Agent requires a sandbox and starts each session with createSession, from what the adapter says of its bridge.

A session’s own bridge is the one its options launch: its Bridge.BridgeId is a digest of them and of the sandbox’s token, so that sessions of hosts that launch a bridge alike share one, and a host with other options, such as another model, runs its own beside it. A session finds the bridge’s port in Bridge.listeningFile, in the bridge’s state directory, exposes the port, and launches the bridge when none answers there.

A session connects to its bridge while it uses it, and closes the connection when it releases the session, or once it has gone unused for a minute. Each connection launches the bridge when none answers, and starts the session there from the state the bridge last gave, so that a bridge that restarted, as after it died or exited idle, has the session again. A connection that drops is not opened again: a request it dropped is sent again over another, for up to 30 seconds, unless the bridge must not receive it twice.

A turn is two requests: StartTurn, under an id the host picks, and ReadTurn, which reads the turn’s parts, on from the last part read after the connection drops. The turn runs in the bridge whoever reads it, so suspendTurn only stops reading, keeping which bridge runs the turn, and continueTurn starts the turn again there, which the bridge continues if it did not finish it, as when it died, and reads it again from its start, after the steps the harness has. A read whose bridge died starts the turn again the same way, in the bridge launched after it, and goes on after the steps it had read. A turn streams what suspendTurn would return as its turn-start part first, so that a caller that keeps it continues the turn the same way once this host is gone without suspending it. A session resumed with other options continues the turn in the bridge that runs it and then goes on in its own; one whose turn’s bridge is gone runs the turn again from its prompt in its own. A session whose bridge cannot answer, as when its sandbox is gone, releases the session with the state the bridge last gave.

Since v0.1.0

Creates a session, as Agent.createSession does, of the agent behind the bridge that options describes, in the sandbox of the session’s workspace.

Details

It connects with the WebSocketConstructor provided, which must send headers when it opens a connection, as Bun’s and Node’s do.

Example (An adapter’s agent whose sessions run behind its bridge)

import { Agent, BridgeClient } from "@amends-dev/harness"
import type { Sandbox, Template } from "@amends-dev/sandbox"
import { Crypto, Effect, Redacted, Schema } from "effect"
import type { Socket } from "effect/socket"
// What the adapter brings: the template that installs the agent and its bridge program, the
// bridge's token for a sandbox, and the options the bridge makes its driver with.
declare const template: Template.Template
declare const tokenOf: (sandboxId: Sandbox.SandboxId) => Redacted.Redacted<string>
declare const agentOptionsOf: (options: Agent.Options) => Schema.Json
export const make = Effect.fnUntraced(function* (options: Agent.Options) {
// Sessions connect from the host, with its WebSockets and its crypto.
const services = yield* Effect.context<Socket.WebSocketConstructor | Crypto.Crypto>()
const bridge: BridgeClient.Options = {
id: "my-agent",
template,
command: "bun",
args: ["run", "/opt/my-agent/bridge.ts"],
token: tokenOf,
agentOptions: agentOptionsOf(options)
}
const agent: Agent.Agent = {
id: bridge.id,
requiresSandbox: true,
template,
createSession: (sessionOptions) => Effect.provide(BridgeClient.createSession(bridge, sessionOptions), services)
}
return agent
})

Signature

declare const createSession: (
options: Options,
sessionOptions: Agent.CreateSessionOptions
) => Effect.Effect<Agent.AgentSession, HarnessError.HarnessError, Crypto.Crypto | Socket.WebSocketConstructor>

Source

Since v0.1.0

What an adapter says of its bridge: how to install and launch it, and where to reach it.

Signature

export interface Options {
/** Names the agent, as its `Bridge.Driver` does. The bridge must answer with it. */
readonly id: string
/**
* Installs the bridge and the agent in a sandbox. A session runs it again before it launches the
* bridge, for a sandbox prepared with another version of it, so it keeps what the sandbox has and
* installs only what it lacks.
*/
readonly template: Template.Template
/** The command that runs the bridge in the sandbox, and its arguments. */
readonly command: string
readonly args?: ReadonlyArray<string>
/**
* The token the bridges in the sandbox `id` take, derived from the id with a secret every host
* shares, such as an HMAC of it, so that every host connects to a bridge any of them launched.
*/
readonly token: (id: Sandbox.SandboxId) => Redacted.Redacted<string>
/** The agent's own options, which the bridge makes its driver with when it launches. */
readonly agentOptions: Schema.Json
}

Source

Since v0.1.0