Lampson

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 IDthe agent that answers (default: build)
--thread Tthe conversation's identity outside Lampson: a phone number, a ticket, cto:tarea-17. Same agent + same thread = same conversation, with its history
--session IDor continue a session by its id
--jsoneach step as a JSON line (see below)
--ask · --strict · --yolothe 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 an ask sets 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"). ctx is the request's context (from, thread, meta…), which the model does not write.
  • A line that isn't JSON returns an error and the process keeps going. Closing stdin ends it once the requests in progress finish.

View as Markdown