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 GuidesScalaWaiting for External Input with Golem Promises (Scala)

Waiting for External Input with Golem Promises (Scala)

Overview

A Golem promise lets an agent suspend its execution until an external event completes it. The agent creates a promise, passes the promise ID to an external system (another agent, a webhook, a UI, an HTTP API call), and then awaits the result. Creation does not suspend the agent. For durable agents awaiting a pending promise, suspension can occur after the suspension policy delay when all active work permits it. Suspension frees guest execution resources; persisted state and other resources remain.

API

All functions are on the golem.HostApi object:

MethodSignatureDescription
createPromise(): PromiseIdCreates a new promise and returns its ID
awaitPromise(id: PromiseId): Future[Array[Byte]]Awaits promise completion (async Future)
completePromise(id: PromiseId, data: Array[Byte]): BooleanCompletes a promise with raw bytes

JSON Helpers (Schema-based)

For structured data, use the JSON variants which require an implicit zio.blocks.schema.Schema[A]:

MethodSignature
awaitPromiseJson[A](id: PromiseId, rejectExtraFields: Boolean = false)(implicit Schema[A]): Future[A]
completePromiseJson[A](id: PromiseId, value: A)(implicit Schema[A]): Boolean

Imports

import golem.HostApi import golem.HostApi.PromiseId import scala.concurrent.Future import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import zio.blocks.schema.Schema

Usage Pattern

1. Create a Promise and Await Its Future

val promiseId = HostApi.createPromise() // Pass promiseId to an external system... // Awaiting can suspend the durable agent after the suspension policy delay val data: Future[Array[Byte]] = HostApi.awaitPromise(promiseId)

2. Create a Promise and Wait with JSON Decoding

case class Decision(status: String) derives Schema val promiseId = HostApi.createPromise() val decision: Future[Decision] = HostApi.awaitPromiseJson[Decision](promiseId)

3. Complete a Promise from Another Agent

HostApi.completePromise(promiseId, "approved".getBytes) // Or with JSON: HostApi.completePromiseJson(promiseId, Decision("approved"))

PromiseId Structure

A PromiseId contains an agentId, a fingerprint and an oplogIdx. The fingerprint is a UUID that identifies the agent incarnation that created the promise. To let an external system complete the promise via the Golem REST API, the agent must expose all three fields. The external caller then sends:

POST /v1/components/{component_id}/workers/{agent_name}/complete Content-Type: application/json {"oplogIdx": <oplog_idx>, "fingerprint": "<fingerprint_uuid>", "data": [<bytes>]}

All three body fields are required. Use JsUuid.toUuid from golem.host.js to convert the fingerprint and the component ID to java.util.UUID values.

Full Example: Human-in-the-Loop Approval

import golem.* import golem.host.js.JsUuid import golem.runtime.annotations.{agentDefinition, agentImplementation} import scala.concurrent.Future import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue import zio.blocks.schema.Schema case class Decision(status: String) derives Schema @agentDefinition trait WorkflowAgent extends BaseAgent { class Id(val name: String) def startApproval(): Future[String] } @agentImplementation() class WorkflowAgentImpl(private val name: String) extends WorkflowAgent { override def startApproval(): Future[String] = { // 1. Create a promise val promiseId = HostApi.createPromise() // Publish the identity before waiting (replace with your notification integration). val componentId = JsUuid.toUuid(promiseId.agentId.componentId.uuid) val fingerprint = JsUuid.toUuid(promiseId.fingerprint) println( s"Approval for $name: component $componentId, agent ${promiseId.agentId.agentId}, " + s"fingerprint $fingerprint, oplog index ${promiseId.oplogIdx}" ) // 3. Wait for external completion HostApi.awaitPromiseJson[Decision](promiseId).map { decision => if (decision.status == "approved") s"Workflow $name approved ✅" else s"Workflow $name rejected ❌" } } }

Use Cases

  • Human-in-the-loop: Pause a workflow until a human approves or rejects
  • Webhook callbacks: Wait for an external HTTP callback to arrive
  • Inter-agent synchronization: One agent creates a promise, another completes it
  • External event ingestion: Suspend until an IoT sensor, payment gateway, or third-party API sends a signal