The JSON channel provides a programmatic interface to picobot via newline-delimited JSON (NDJSON) on stdin/stdout. Use it to embed picobot in scripts, build integrations, or create custom frontends.
Activation¶
picobot agent --json
Protocol¶
All messages are single-line JSON objects separated by newlines. The channel reads from stdin and writes to stdout.
Input Messages (stdin)¶
Send a message:
{"type": "message", "content": "What tools do you have?"}
With a session ID:
{"type": "message", "content": "Continue our work", "session": "my-session"}
With attachments:
{"type": "message", "content": "Analyze this", "attachments": [{"name": "data.csv", "content": "a,b,c\n1,2,3"}]}
Ping (health check):
{"type": "ping"}
Shutdown:
{"type": "shutdown"}
Output Messages (stdout)¶
Ready (on startup):
{"type": "ready"}
Response (agent's reply):
{"type": "response", "content": "I have these tools: ...", "session": "json:my-session"}
Progress events (real-time streaming):
{"type": "progress", "event": "text_delta", "text": "I have"}
{"type": "progress", "event": "tool_call_start", "id": "1", "name": "list_dir", "arguments": {"path": "/"}}
{"type": "progress", "event": "tool_call_done", "id": "1", "name": "list_dir", "result": "..."}
{"type": "progress", "event": "reasoning_delta", "text": "thinking..."}
Error:
{"type": "error", "message": "Invalid JSON", "code": "parse_error"}
Pong (ping response):
{"type": "pong"}
Sessions¶
Sessions are scoped to json:<session_id>. If you don't provide a session ID, a new one is generated per message. Provide a session ID to maintain conversation context across messages:
{"type": "message", "content": "Hello", "session": "project-x"}
{"type": "message", "content": "What did I just say?", "session": "project-x"}
Attachments¶
Text attachments are prepended to the message content as blocks:
[Attachment: data.csv]
a,b,c
1,2,3
[/Attachment]
Analyze this
This lets you send file contents alongside a question without the agent needing to read files from disk.
Error Recovery¶
The channel recovers from errors without shutting down:
- Invalid JSON → error response, continue reading
- Unknown message type → error response, continue reading
- Agent exception → error response, continue reading
Only {"type": "shutdown"} or EOF on stdin stops the channel.
Example: Python Integration¶
import subprocess
import json
proc = subprocess.Popen(
["picobot", "agent", "--json"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True,
)
# Wait for ready
ready = json.loads(proc.stdout.readline())
assert ready["type"] == "ready"
# Send a message
msg = {"type": "message", "content": "List my cron jobs", "session": "script"}
proc.stdin.write(json.dumps(msg) + "\n")
proc.stdin.flush()
# Read response (skip progress events)
while True:
line = proc.stdout.readline()
data = json.loads(line)
if data["type"] == "response":
print(data["content"])
break
# Shutdown
proc.stdin.write('{"type": "shutdown"}\n')
proc.stdin.flush()
proc.wait()
Use Cases¶
- Build a custom CLI with different UX than the default REPL
- Embed in CI/CD for automated tasks (code review, test generation)
- Create a web frontend that proxies through the JSON channel
- Script multi-step workflows with precise control over sessions
- Build channel adapters (Telegram, Slack) — this is the foundation for Phase 9 integrations