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. The Golem runtime durably suspends the agent — consuming no resources — until the promise is fulfilled.
API
All functions are on the golem.HostApi object:
| Method | Signature | Description |
|---|---|---|
createPromise | (): PromiseId | Creates a new promise and returns its ID |
awaitPromise | (id: PromiseId): Future[Array[Byte]] | Awaits promise completion (async Future) |
completePromise | (id: PromiseId, data: Array[Byte]): Boolean | Completes 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]:
| Method | Signature |
|---|---|
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.SchemaUsage Pattern
1. Create a Promise and Await Its Future
val promiseId = HostApi.createPromise()
// Pass promiseId to an external system...
// Agent is durably suspended here until the promise is completed
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 and an oplogIdx. To let an external system complete the promise via the Golem REST API, the agent must expose both fields. The external caller then sends:
POST /v1/components/{component_id}/workers/{agent_name}/complete
Content-Type: application/json
{"oplogIdx": <oplog_idx>, "data": [<bytes>]}Full Example: Human-in-the-Loop Approval
import golem.*
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).
println(s"Approval for $name: ${promiseId.agentId}, ${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