Mod API

A mod talks to Syntic Code through its standard input and standard output. Each message is a single JSON object on its own line. This page lists every message in both directions, the two API methods a mod can call, and the environment a mod starts with.

The protocol is deliberately small. A mod can draw, choose which events it hears, post a note and read a summary of the session. It has no way to run tools, send text to the model, read the conversation or change settings.

Protocol version

The current protocol version is 1. It is sent in the hello message and in the SYNTIC_MOD_PROTOCOL environment variable. Check it at start-up if you want your mod to refuse to run against a protocol it does not know.

Messages Syntic Code sends

typeFieldsWhen
helloprotocol, mod, events, ui, grantedOnce, always first
eventevent, dataA subscribed event happened. See Events.
resultid, ok, and value or errorThe answer to one of your call messages
byenoneThe session is ending. Exit within half a second.

hello

{"type":"hello","protocol":1,"mod":"turn-clock","events":["turn.start","turn.end"],"ui":["status","pane"],"granted":["notes"]}
FieldTypeMeaning
protocolnumberProtocol version (1).
modstringThe mod’s name.
eventsstring arrayEvents from the manifest. This is the starting subscription.
uistring arraySlots from the manifest that the mod may draw in.
grantedstring arrayPermissions the user actually granted. This can be shorter than the manifest’s permissions list.

Use granted to decide which features to turn on. For example, skip note.post calls when notes is missing, instead of sending calls that will be refused.

result

{"type":"result","id":1,"ok":true,"value":{}}
{"type":"result","id":2,"ok":false,"error":"permission `session.read` not granted"}

id is copied from your call, so you can match answers to requests. It can be any JSON value: a number, a string or an object. On success ok is true and value holds the answer. On failure ok is false and error holds a short message.

Messages a mod sends

typeFieldsEffect
drawslot, optional text, lines, titleSets or clears one slot. See Screen areas.
subscribeeventsReplaces the event subscription with a subset of the manifest list.
callid, method, optional paramsCalls an API method. Always answered with a result.

draw

FieldTypeMeaning
slotstringstatus, band or pane. Must be listed in the manifest’s ui.
textstringThe content. For band and pane, newlines split it into rows.
linesstring arrayThe content as rows. For band and pane, lines wins when both are sent; for status, text wins and only the first row of lines is used.
titlestringPane title. Ignored for other slots. Defaults to the mod name.

A draw with neither text nor lines clears the slot.

subscribe

{"type":"subscribe","events":["turn.end"]}

Names that are not in the manifest’s events are ignored.

call

{"type":"call","id":7,"method":"note.post","params":{"text":"Build finished."}}

There are two methods.

note.post

Posts one short line into the conversation view. The line is shown to you only; it is never sent to the model and never becomes part of the conversation history the agent sees.

Permissionnotes
Paramstext (string, required)
Returns{}

How the note is handled:

  • Newlines are turned into spaces, and the text is cleaned the same way as drawn text (control and direction-override characters removed).
  • It is cut to 500 characters.
  • It appears as [mod <name>] <text>.
  • Each mod may post at most 5 notes in any 60-second window. A sixth note gets an error result: too many notes; try again in a minute.
  • If the screen falls far behind, only the newest 50 waiting notes (from all mods) are kept.

Errors:

errorCause
permission `notes` not grantedThe user did not grant notes.
`text` is requiredparams.text is missing or not a string.
`text` is emptyNothing was left after cleaning.
too many notes; try again in a minuteRate limit reached.

session.state

Returns a summary of the session.

Permissionsession.read
Paramsnone
{"type":"result","id":2,"ok":true,"value":{"session_id":"8c1f…","model":"amara-1","permission_mode":"default","turns":4,"busy":false,"cwd":"/home/sam/src/shop"}}
FieldTypeMeaning
session_idstringThe session’s id.
modelstringThe model the session is using.
permission_modestringThe current permission mode, with the same values as mode.change.
turnsnumberThe session’s turn counter.
busybooleantrue while the agent is answering.
cwdstringThe folder the session was started in.

The values are refreshed by the interface on every screen tick, so they are current to within a moment.

Errors and refused messages

Some messages are counted as bad lines:

  • a line that is not valid JSON, is not UTF-8, or has an unknown type;
  • a line longer than 64 KB (it is skipped whole);
  • a draw to a slot the manifest does not list;
  • a call to an unknown method, or to a method whose permission was not granted.

Blank lines are ignored. A call that fails for another reason, such as a missing text or the note rate limit, still gets an error result but is not a bad line.

When a mod sends more than 20 bad lines in a session, Syntic Code stops it, clears its slots and shows a warning line in the conversation saying the mod was stopped because it sent too many messages it is not allowed to send.

An unknown method still gets a result with ok: false and the error unknown method `<name>` , so a mod written for a later protocol can detect what is missing.

The mod’s environment

A mod starts with an empty environment plus a short allow-list. Your shell’s other variables, including any tokens or keys, are not passed.

VariableValue
PATHA cleaned search path: only absolute folders that exist and are not inside the opened project, plus standard system folders.
HOME, USER, LOGNAMECopied from your environment, if set.
LANG, LC_ALL, LC_CTYPE, TZ, TMPDIRCopied, if set.
SYSTEMROOT, WINDIR, PATHEXT, TEMP, TMP, USERPROFILECopied on Windows, if set.
SYNTIC_MOD_NAMEThe mod’s name.
SYNTIC_MOD_DIRThe mod’s folder.
SYNTIC_MOD_DATAA private folder for the mod’s own files, ~/.syntic/mod-data/<name>/. Created before start, readable only by you on Unix. Kept between sessions.
SYNTIC_MOD_PROTOCOL1

Other facts about the process:

  • Its working folder is the mod’s own folder, not the project.
  • Standard error is discarded.
  • On Unix it runs in its own process group, so pressing Ctrl+C in the terminal does not reach it.
  • When it is stopped, any processes it started in its group are stopped too.

Next steps