Skip to Content
Next — You're reading the unreleased preview docs. APIs and pages may change before the next release.Switch to the current docs →
How-To GuidesScalaPhantom Agents in Scala

Phantom Agents in Scala

Phantom agents allow creating multiple distinct agent instances that share the same constructor parameters. Normally, an agent is uniquely identified by its constructor parameter values — calling get with the same parameters always returns the same agent. Phantom agents add an extra phantom ID (a UUID) to the identity, so you can have many independent instances with identical parameters.

Agent ID Format

A phantom agent’s ID appends the phantom UUID in square brackets:

agent-type(param1, param2)[a09f61a8-677a-40ea-9ebe-437a0df51749]

A non-phantom agent ID has no bracket suffix:

agent-type(param1, param2)

Creating and Addressing Phantom Agents (RPC)

The native definition-derived Scala SDK client extends AgentCompanionBase and provides the following synchronous factories. This section and its UUID recipes describe native SDK clients, not CLI-generated bridges.

MethodDescription
Client.get(params...)Get or create a non-phantom (durable) agent identified solely by its parameters
Client.newPhantom(params...)Select a fresh phantom invocation/instance according to the agent mode
Client.getPhantom(params..., phantom)Get or create a phantom agent with a specific UUID

Note: Ephemeral agents only get getPhantom and newPhantom — they do not have a get method.

Example (Durable Counter)

import java.util.UUID // Non-phantom: always the same agent for the same name val counter = CounterClient.get("shared") // New logical phantom proxy: instance creation happens on first invocation val phantom1 = CounterClient.newPhantom("shared") val phantom2 = CounterClient.newPhantom("shared") // phantom1 and phantom2 are different agents, both with name="shared" // Reconnect to an existing phantom by its UUID val existingId: UUID = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") val samePhantom = CounterClient.getPhantom("shared", existingId) // Call methods on the phantom phantom1.increment().map { result => println(s"counter value: $result") }

WithConfig Variants

If the agent has config fields, additional methods are generated (getWithConfig only for durable targets):

  • Client.getWithConfig(params..., configFields...)
  • Client.newPhantomWithConfig(params..., configFields...)
  • Client.getPhantomWithConfig(params..., phantom, configFields...)

Each generated config argument is positional Option[T] = None. Supply Some(value) to override a field, and None to retain its configured value.

CLI-generated guest/JVM bridge clients do not extend AgentCompanionBase. Durable bridges expose get, getPhantom, and newPhantom; ephemeral bridges expose only newPhantom, a logical proxy whose final invocation identity is assigned on invocation. Guest factories are synchronous; external JVM factories return Future. See golem-call-from-external-scala.

Method Signature Differences

Note that in the Scala SDK, getPhantom takes the phantom UUID as the last parameter (after the constructor parameters), while newPhantom takes only the constructor parameters:

// getPhantom: constructor params first, then phantom UUID CounterClient.getPhantom("counter-name", phantomUuid) // newPhantom: constructor params only CounterClient.newPhantom("counter-name") // For DURABLE agents, newPhantom delegates with a generated idempotency key: // def newPhantom(name: String): CounterRemote = // getPhantom(name, HostApi.generateIdempotencyKey())

Key Points

  • Phantom identity and durability mode are independent. Durable phantom agents persist just like regular durable agents; ephemeral invocations discard their memory. If the executor restarts while preparing an ephemeral invocation, Golem can replay the recorded initialization so that the method can still start. Once the method starts, a failure is terminal; Golem does not replay the method.
  • The phantom ID is a java.util.UUID.
  • For durable targets, newPhantom generates a UUID via HostApi.generateIdempotencyKey(). For ephemeral targets it resolves without a phantom override; invocation metadata supplies the actual identity. Factory construction is synchronous in both cases.
  • For durable targets, getPhantom with the same UUID and parameters addresses the same persistent agent. For ephemeral targets it selects an invocation identity, not reusable persistent state.
  • Phantom and non-phantom agents with the same constructor parameters are different agents — they do not share state.
  • Durable agents get get, getPhantom, and newPhantom; ephemeral agents get only getPhantom and newPhantom.