Skip to content

Transport

String communicates through messages. Each message has three core elements — regardless of how they’re encoded on the wire.


Every String message consists of:

ElementWhat it is
Channelstring — identifies this as a String message
TopicWhich session receives the message (file, web, app)
PayloadThe content — a command, text, or a response

That’s the entire interface. Everything else is how these three elements are represented in a specific transport.


The topic identifies which session the message is for:

file:main file topic (default)
file:docs file topic
web:docs web topic
app:gmail:work app topic
bash:dev bash topic

Topics use the type:name format. Bare names default to file:. Empty/absent topics default to file:main. See 04-topics.md.


The payload is what goes inside the message. It is not always a command.

/open #decisions
/act.search_city --name "Seoul"
/replace :L5-L10
New content for those lines

Commands start with /. A command must be the first line of the payload, and each channel block carries exactly one command. To send two commands, use two channel blocks.

Commands with content (/replace, /append, /set) include the content starting from the next line after the command:

<𝒞=string:file:notes>
/replace ~/notes/meeting.md:L5
This line replaces line 5.
This is a second line.
</𝒞>

The command is /replace :L5. Everything from the next line to </𝒞> is the content.

All payloads must start with /. Plain text without a command prefix is rejected with COMMAND_UNSUPPORTED.

<𝒞=string:file:notes>
# Meeting Notes
Content goes here.
</𝒞>
✗ COMMAND_UNSUPPORTED: Commands must start with /. Use /help for details.

Exception: Bash topics (bash:name) accept plain text as shell input. Every line sent to a bash topic is treated as shell stdin, not a String command:

<𝒞=string:bash:dev>
echo "this goes to the shell"
</𝒞>

To send String commands to a bash topic, use the // prefix (strips one slash):

<𝒞=string:bash:dev>
//info
</𝒞>

This sends /info as a String command instead of executing //info in the shell. See 04-topics.md for bash topic semantics.

Use explicit commands for all operations:

<𝒞=string:file:notes>
/write ~/notes/meeting.md
# Meeting Notes
Content goes here.
</𝒞>

String responses are Markdown content, feedback, or errors:

## Decisions
1. Move deadline to April 1
2. Switch to weekly syncs
✓ ~/notes/meeting.md#decisions — Added 2 lines
✗ NOT_FOUND: file not found
path: ~/notes/missing.md

The current transport is ChanFlow — a protocol that routes messages between AI and multiple channels using tagged blocks.

<𝒞=string:topic>
payload
</𝒞>

The 𝒞 character identifies a ChanFlow channel tag. string is the channel. Everything after : is the typed topic.

AI tags have only channel and topic — nothing else:

<𝒞=string:file:notes>
/open ~/notes/meeting.md#decisions
</𝒞>
<𝒞=string:app:weather:korea>
/act.search_city --name "Seoul"
</𝒞>

Response tags MAY include optional attributes:

<𝒞=string:file:notes time="2026-03-19T14:30+09:00">
## Decisions
1. Move deadline to April 1
2. Switch to weekly syncs
</𝒞>
<𝒞=string:file:notes time="2026-03-19T14:31+09:00" mode=edit>
✓ ~/notes/meeting.md#decisions — Added 2 lines, removed 1 line
11 │ <!-- #decisions -->
12 │ ## Decisions
13 -│ (none yet)
13 +│ 1. Move deadline to April 1
14 +│ 2. Switch to weekly syncs
15 │ <!-- /decisions -->
</𝒞>
AttributeDescription
timeISO 8601 timestamp of the response
modeSession mode when applicable (e.g. edit)

Attributes are always optional. Additional attributes may be added in the future — AI should ignore unknown attributes.

AI can address multiple topics in one turn. String responds with separate blocks for each:

<𝒞=string:web:api-docs>
/open @authentication
</𝒞>
<𝒞=string:file:code>
/open ~/src/auth.md#oauth-flow
</𝒞>

Response:

<𝒞=string:web:api-docs time="2026-03-19T14:32+09:00">
# Authentication
OAuth2 is required for all API endpoints...
</𝒞>
<𝒞=string:file:code time="2026-03-19T14:32+09:00">
## OAuth Flow
1. Redirect to provider
2. Receive callback with code
</𝒞>

In ChanFlow, String is one of many channels:

<𝒞=string:file:main>
/open ~/report.md#summary
</𝒞>
<𝒞=slack:dev-team>
Here's the Q1 summary: ...
</𝒞>
<𝒞=email:manager@company.com>
Please review the attached Q1 report.
</𝒞>

Each channel has its own protocol. String’s:

  • Input: commands (must start with /)
  • Output: Markdown content, feedback, or errors
  • State: per-topic sessions with variables and history

When AI sends a message to a topic for the first time, String creates the session automatically:

<𝒞=string:file:project>
/open ~/new-project/index.md
</𝒞>

No explicit session creation needed.

Sessions persist across turns. Variables, history, and current location are maintained:

# Turn 1
<𝒞=string:app:weather:korea>
/act.search_city --name "Seoul"
</𝒞>
# Turn 5 (later)
<𝒞=string:app:weather:korea>
/act.get_forecast --city {city}
</𝒞>

{city} was set by the response template in turn 1. It’s still available in turn 5.

<𝒞=string:web:research>
/close
</𝒞>

/close ends the session. State is discarded. The topic can be reopened fresh.


ConceptDescription
Core elementsChannel (string) + Topic + Payload
Topictype:name — typed session identifier
PayloadCommand only (/...) — plain text rejected
ChanFlowCurrent encoding: <𝒞=string:type:name></𝒞>
AsymmetryAI sends no attributes; responses may have time, mode
SessionAuto-created on first use, persists across turns