Lampson as an engine
Headless: lampson run
The same agent, without the terminal UI or the web: a message goes in, the answer comes out. For scripts, cron jobs, other programs.
lampson run "summarize today's changes" # the answer on stdout
lampson run --agent plan "review the last commit" # any agent, built-in or yours
echo "hello" | lampson run --agent tienda --thread cliente-42 # the message can also come on stdin
lampson run --session 20261016-122626 "and the tests?" # continue a known session
lampson run doesn't start the hub or the web server: it prepares the project folder and runs the turn.
Options§
| option | |
|---|---|
--agent ID | the agent that answers (default: build) |
--thread T | the conversation's identity outside Lampson: a phone number, a ticket, cto:tarea-17. Same agent + same thread = same conversation, with its history |
--session ID | or continue a session by its id |
--json | each step as a JSON line (see below) |
--ask · --strict · --yolo | the permission mode; with none, the agent's, and if it doesn't say, strict |
With nobody present, whatever would ask for approval is denied: the model sees it and explains it in its answer.
Exit code: 0 ok · 1 the turn ended in an error · 2 invalid usage (unknown agent, empty message).
--json: the events§
One JSON line per event. First session, then what the agent does, and done at the end:
{"kind": "session", "data": {"session": "20261016-125226-8941", "agent": "tienda", "thread": "cliente-42", "mode": "strict"}, "agent": "tienda", "thread": "cliente-42"}
{"kind": "tool_call", "data": {"name": "read", "args": {"path": ".lampson/tienda/catalogo.json"}}, "agent": "tienda", "thread": "cliente-42"}
{"kind": "tool_result", "data": {"call": {…}, "output": "…"}, "agent": "tienda", "thread": "cliente-42"}
{"kind": "assistant", "data": "Yes, we have whole-wheat bread at $3,200.", "agent": "tienda", "thread": "cliente-42"}
{"kind": "done", "data": {"agent": "tienda", "thread": "cliente-42", "session": "…", "text": "Yes, we have whole-wheat bread at $3,200.", "steps": 2, "usage": {"input": 1079, "output": 357, "cached": 448}, "stopped": "done"}, "agent": "tienda", "thread": "cliente-42"}
The event kinds: session, assistant, tool_call, tool_result, tool_denied, usage, inbox, error, compact and done. In done, stopped is done, max_steps, budget or error: ….
--stdio: one open process (local mode)§
lampson run --stdio stays open and handles many requests: one per line on stdin, each with an id of yours. It's what the SDK uses in local mode — Lampson inside your program, no server.
{"id": "r1", "op": "ask", "to": "tienda", "thread": "cliente-42", "text": "do you have whole-wheat bread?"}
{"id": "r2", "op": "agents"}
{"id": "r3", "op": "ping"}
Stdout carries the same events as --json, each with its request's id, and a done when each ask finishes. On start it emits {"kind": "ready", "data": {"agents": […], "protocol": 2, "warnings": […]}}: in warnings, one line per tool some agent lists but will not have (a plugin that is off, a typo). A warning event also comes before an ask to that agent. With LAMPSON_BUILTIN_TOOLS=off the process offers no tool of Lampson's own (the SDK's lampsonTools: false).
- Different conversations run in parallel; two requests on the same conversation (agent + thread) are handled in order.
"mode": "strict"or"yolo"in anasksets the permissions for that request (it's your program, on your machine).- Your app's tools (what the SDK's
lampson.tool()uses):{"op": "tools", "tools": [{"name", "description", "parameters", "timeout"}]}replaces the whole list. When the model calls one, out comes{"kind": "tool_request", "data": {"call_id", "name", "args", "ctx"}}, and your program answers{"op": "tool_result", "call_id", "output"}(or"error").ctxis the request's context (from,thread,meta…), which the model does not write. - A line that isn't JSON returns an
errorand the process keeps going. Closing stdin ends it once the requests in progress finish.