Platform Pipelines Agents Human review Self-improvement Observability Deployment Security Use cases Compare Docs Pricing Roadmap Get early access

The action protocol

An action is a program that turns one input item into zero or more output items. Millstream starts it, hands it items one at a time over stdin, and reads its answers on stdout, one JSON object per line. That is the whole contract: an action needs no Millstream library, no network access to the engine and no knowledge of the store.


Transport


A conversation

{"type":"hello","protocol":1,"action":"lab/score-listing","version":"2.1.0","config":{"minRooms":2},"vars":{"city":"Lyon"}}
{"type":"ready"}
{"type":"item","delivery":"dl_3f…","id":"it_9a…","key":"listing-42","payload":{"url":"https://example.com/42"},"attempt":1,"idempotencyKey":"7e2b…"}
{"type":"metric","delivery":"dl_3f…","name":"pages_fetched","value":12}
{"type":"emit","delivery":"dl_3f…","key":"listing-42","payload":{"score":81}}
{"type":"done","delivery":"dl_3f…"}
{"type":"shutdown"}
Message From Means
hello engine The action, its config already interpolated for the run, the run's variables, and lessons for agent steps
ready action Prepared; answered within 10 seconds
item engine One delivery: the item's id, key, payload, artifacts, run, attempt and idempotency key
emit action Stages an output; outputs publish only when the delivery is done
metric action A custom measurement, recorded with the attempt
done action The delivery succeeded
error action It failed, retryable or not
abstain action Unsure of this item: hand it to the step's fallback
shutdown engine Exit with status 0

A process handles one delivery at a time, and concurrency comes from running several. It may serve item after item (the default for code and containers, which amortises start-up) or one item each (the default for agents, so no context leaks between items).


Retries and idempotency

Outputs are staged by emit and published only on done, so a delivery that fails, crashes or times out publishes nothing, and a retry starts clean. Retryable errors, crashes and timeouts are retried with exponential backoff and jitter; a non-retryable error dead-letters the item at once.

Delivery is at least once. idempotencyKey, a hash of the step, the item and its generation, is the same on every attempt and changes on replay. An action that changes something outside Millstream declares effects: external and passes the key to the system it calls, so a retried request is applied once.


The Go SDK

The SDK reads items, decodes each payload into your Go type, calls your function and writes the answers.

package main

import "github.com/monolithiclab/millstream/sdk/go"

func main() {
    millstream.Map(func(ctx millstream.Context, in Listing) ([]Priced, error) {
        if in.SurfaceM2 == 0 {
            return nil, ctx.Abstain("no surface area")
        }
        ctx.Metric("works_estimate_eur", float64(estimate(in)), nil)
        return []Priced{price(in, ctx.Config())}, nil
    })
}

millstream.MapKeyed emits several keyed outputs per item. millstream init action <name> --lang raw writes a Python action with the standard library only, a starting point for any other language.


Conformance

millstream test action --conformance checks an action against the protocol, whatever it computes: ready in time, unknown keys tolerated, every line well framed, every item answered, several outputs keyed distinctly, silence between deliveries, a malformed config answered per item instead of crashing, and a clean exit on shutdown.

millstream test action ./actions/rentability --conformance
PASS ready: ready in 4ms
...
PASS shutdown: exited with status 0
listings/greet: conformant

Fixture tests compare an action's outputs with golden ones, and run in CI like any unit test:

millstream test action ./actions/rentability

Next

Read the quick start Pipelines Agents