Erlang agent

The Erlang agent lets a caller validate source, execute a job, or bind a program to a message route. A binding turns matching messages into program inputs and sends the results to authorized output paths, allowing a small Erlang program to participate in the message flow. mesgd delegates these requests to the agent application, whose modules must be available on the node; missing modules cause the request to fail in its connection process.

This is separate from the topic names in mesgd-sdk bot.md. Those names are a convention between clients. See SDK agents.

The agent id defaults to erlang. Set erlang_agent_id in the agent application environment to replace it. The request field to must be that id. id, from, and to are one path segment: 1 to 128 bytes matching [A-Za-z0-9_.:@-].

A login session is enough to validate or execute, and to read or write the standard streams, when the session is allowed on that path. Creating a binding requires a grant token. A login session on the binding route is 400 invalid_binding_request. Empty credentials are HTTP 401.

Validate and execute source

POST /agents/erlang, or a WebSocket frame on that same path.

{"id":"1","from":"alice","to":"erlang","type":"erlang.validate","source":"..."}

type is erlang.validate or erlang.execute. Optional input is passed through. Optional stdio must be a boolean, and true is allowed only on erlang.execute. No other fields are accepted. source is at most 21848 bytes. The HTTP body and a WebSocket frame are at most 32768 bytes. A larger HTTP body is 413 request_too_large. A larger frame, or more than 32 requests in flight on one socket, returns {"error":"request_limit"}.

mesgd checks that the credential may use /from=<from>/to=<to>/type=<type>. A refusal is 403 forbidden on HTTP, and {"error":"forbidden"} on the socket. A bad shape is 403 invalid_request on HTTP. The socket sends {"error":"invalid_request"} the same way.

HTTP waits up to 25 seconds. Success is 200 and the result object. A timeout is 504 {"error":"timeout"}. The socket uses the same 25 seconds and then sends the timeout result. The result swaps from and to. erlang.validate is answered as erlang.validation. erlang.execute is answered as erlang.result. status is ok or error.

GET on this path is 405.

Bind source to a path

Use a binding when a message on a path should run Erlang you supply, and write to one to four output paths. POST /agents/erlang/bindings, or a WebSocket frame on that path. The body limit is 32768 bytes.

The credential must be a grant token. mesgd also requires that the grant allows /agents/erlang/bindings and /from=<from>/to=<to>/type=<type>.

typeWhat else you send
erlang.bindpath, outputs, source
erlang.unbindnothing more. id selects the binding
erlang.bindingsnothing more. Lists the bindings

path and each output are absolute paths of segments name or name=value, 2 to 256 bytes, using the character set above. The first segment cannot be auth, agents, or user. outputs has 1 to 4 paths. source is at most 21848 bytes. The grant must also allow path and every output.

mesgd keeps at most 16 bindings. A repeat of the same owner and id is 409 binding_exists. A full table is 429 binding_limit. A bad shape is 400 invalid_binding_request or invalid_binding. A path the grant does not cover is 403 forbidden or forbidden_path.

HTTP uses those status codes and a JSON object. The socket sends the same object.

Standard input and output

POST or a WebSocket frame on one of:

PathtypeFields
/agents/erlang/stdinerlang.stdinid, from, to, data, eof
/agents/erlang/stdouterlang.stdoutid, from, to, cursor
/agents/erlang/stderrerlang.stderrid, from, to, cursor

Any other path under /agents/erlang/ is 404 not_found. The body limit is 8192 bytes.

The credential must allow both /agents/erlang/<channel> and /from=<from>/to=<to>/type=erlang.<channel>. data on stdin is standard base64, not base64url, one chunk of at most 4096 decoded bytes. eof is a boolean. Empty data is accepted only when eof is true. A job accepts at most 32768 stdin bytes and 128 stdin messages.

cursor is a byte offset into the output collected so far. Each read returns at most 8192 bytes, as standard base64, plus cursor, more, and eof.

StatusError
400invalid_stream_request, invalid_stdin_data, invalid_cursor
403forbidden
404not_found, job_not_found
409stdin_closed
410job_finished
413stdin_limit, or request_too_large for the HTTP body
429subscriber_limit

A WebSocket subscriber is registered for later output from that job. A one-shot HTTP call passes no subscriber. More than 16 subscribers on a job is subscriber_limit.

Open the agent UI

/agent-ui and /auth/agent-state/ are served only when {agent_ui_enabled, true} is set. Any other value, including an absent key, is 404 with an empty body.

GET /agent-ui, GET /agent-ui/, and the assets under /agent-ui/ (app.js, style.css, the dictation scripts, and the whisper files) are read from the agent application's priv/ui directory. Those GETs do not check a session.

The state routes require a mesgd login session, Authorization: Bearer. A grant token is not a session and is 401. These routes are handled before the generic /auth/ handler.

Method and pathBody limit
GET /auth/agent-state/snapshot
GET /auth/agent-state/settings
GET /auth/agent-state/detail/<id>
GET /auth/agent-state/schedules
POST /auth/agent-state/command8192
POST /auth/agent-state/search8192
POST /auth/agent-state/schedule32768
POST /auth/agent-state/timeouts4096
POST /auth/agent-state/plan65536

401 is {"error":"unauthorized"}. A rejected plan or timeout write is 409 with a reload message. Schedule errors are 400 for a bad crontab or schedule, 409 when the schedule changed or a one-off already ran, 502 when the model reply was not a schedule, and 503 when the model is unavailable.