@cordfuse/crosstalk 5.0.0-alpha.7 → 6.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/crosstalk.js +34 -78
- package/package.json +4 -4
- package/src/activation.ts +104 -0
- package/src/attach.ts +1 -1
- package/src/channel.ts +8 -21
- package/src/chat.ts +52 -115
- package/src/dispatch.ts +265 -660
- package/src/dlq.ts +68 -136
- package/src/init.ts +17 -41
- package/src/open.ts +55 -31
- package/src/replies.ts +59 -0
- package/src/send.ts +48 -67
- package/src/state.ts +173 -0
- package/src/status.ts +18 -57
- package/src/stop.ts +37 -0
- package/src/transport.ts +68 -198
- package/src/turnq.ts +64 -32
- package/src/upgrade.ts +9 -11
- package/src/wake.ts +5 -6
- package/src/cursor.ts +0 -48
- package/template/.amazonq/rules/crosstalk.md +0 -2
- package/template/.continue/rules/crosstalk.md +0 -7
- package/template/.cursor/rules/crosstalk.mdc +0 -7
- package/template/.github/copilot-instructions.md +0 -2
- package/template/.windsurfrules +0 -2
- package/template/AGENTS.md +0 -2
- package/template/ANTIGRAVITY.md +0 -2
- package/template/CLAUDE.md +0 -2
- package/template/GEMINI.md +0 -2
- package/template/OPENCODE.md +0 -2
- package/template/QWEN.md +0 -2
- package/template/README.md +0 -22
- package/template/local/CROSSTALK.md +0 -4
- package/template/upstream/CROSSTALK-VERSION +0 -1
- package/template/upstream/CROSSTALK.md +0 -589
- package/template/upstream/JITTER.md +0 -24
- package/template/upstream/OPERATOR.md +0 -60
- package/template/upstream/PROTOCOL.md +0 -260
- package/template/upstream/actors/cloud-architect.md +0 -83
- package/template/upstream/actors/concierge.md +0 -130
- package/template/upstream/actors/devops-engineer.md +0 -83
- package/template/upstream/actors/documentation-engineer.md +0 -107
- package/template/upstream/actors/infrastructure-engineer.md +0 -83
- package/template/upstream/actors/junior-developer.md +0 -83
- package/template/upstream/actors/precise-generalist.md +0 -48
- package/template/upstream/actors/product-manager.md +0 -83
- package/template/upstream/actors/qa-engineer.md +0 -83
- package/template/upstream/actors/security-engineer.md +0 -92
- package/template/upstream/actors/senior-generalist-engineer.md +0 -111
- package/template/upstream/actors/senior-software-engineer.md +0 -94
- package/template/upstream/actors/skeptic.md +0 -89
- package/template/upstream/actors/technical-writer.md +0 -89
- package/template/upstream/actors/ux-designer.md +0 -83
package/template/README.md
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Transport template
|
|
2
|
-
|
|
3
|
-
This directory is the scaffold for a Crosstalk transport — the markdown + folder structure that becomes an operator's transport when they run:
|
|
4
|
-
|
|
5
|
-
```sh
|
|
6
|
-
crosstalk init my-transport
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
## What's here
|
|
10
|
-
|
|
11
|
-
- `upstream/` — runtime-managed. Protocol spec (`CROSSTALK.md`, `PROTOCOL.md`, `OPERATOR.md`, `JITTER.md`), version pin (`CROSSTALK-VERSION`), default actor profiles (`actors/`). **Operators don't edit these.** The runtime can re-sync them when a new spec version ships.
|
|
12
|
-
- `local/` — operator-owned. Custom actor profiles (`actors/`), local identity config (`CROSSTALK.md`). Operators edit these freely.
|
|
13
|
-
- `hosts/` — host files declaring which actors run where, per-actor tier configurations.
|
|
14
|
-
- Agent entry pointer files at root (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, etc.) — these direct agents to the right files when their CLI starts up in the transport directory.
|
|
15
|
-
|
|
16
|
-
## How a transport is born
|
|
17
|
-
|
|
18
|
-
`crosstalk init` copies this entire `transport/` tree to the operator's chosen directory, then initializes it as a git repo. From that moment on, the operator's transport is independent — they own it, customize `local/`, declare hosts in `hosts/`, and dispatch runs against it.
|
|
19
|
-
|
|
20
|
-
## Spec reference
|
|
21
|
-
|
|
22
|
-
The canonical protocol specification lives in `upstream/CROSSTALK.md`. The version pin lives in `upstream/CROSSTALK-VERSION`.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
5.0
|
|
@@ -1,589 +0,0 @@
|
|
|
1
|
-
# Crosstalk
|
|
2
|
-
|
|
3
|
-
Version: 5.0
|
|
4
|
-
|
|
5
|
-
Crosstalk is a shared file format over git that lets humans and AI agents communicate
|
|
6
|
-
asynchronously. The git repository is the message bus. No special software is required
|
|
7
|
-
to participate beyond git itself.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Participants
|
|
12
|
-
|
|
13
|
-
A Crosstalk transport has two kinds of participants:
|
|
14
|
-
|
|
15
|
-
**Humans** — operators and users who post messages directly or via a chat session with an agent. They read replies in the channel and drive the conversation.
|
|
16
|
-
|
|
17
|
-
**Machines** — agents (Claude, Codex, agy, etc.) that process messages and reply autonomously. A machine participant is invoked on a schedule or by a human and acts on any unread messages addressed to it.
|
|
18
|
-
|
|
19
|
-
### The worker pattern
|
|
20
|
-
|
|
21
|
-
The most common setup is a **worker** — a machine participant that serves both humans and machines. `to: concierge` means "I need something done." The worker receives the request, acts on it, and replies. The sender may be human or machine; the worker does not distinguish.
|
|
22
|
-
|
|
23
|
-
Any participant name can fill this role. The operator defines what the worker actually does via its system prompt — Crosstalk only handles the message routing.
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Transport layout
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
<transport>/
|
|
31
|
-
upstream/
|
|
32
|
-
CROSSTALK.md
|
|
33
|
-
CROSSTALK-VERSION
|
|
34
|
-
PROTOCOL.md
|
|
35
|
-
OPERATOR.md
|
|
36
|
-
actors/
|
|
37
|
-
<name>.md
|
|
38
|
-
local/
|
|
39
|
-
CROSSTALK.md
|
|
40
|
-
actors/
|
|
41
|
-
<name>.md
|
|
42
|
-
hosts/
|
|
43
|
-
<alias>.md
|
|
44
|
-
data/
|
|
45
|
-
channels/
|
|
46
|
-
<guid>/
|
|
47
|
-
YYYY/
|
|
48
|
-
MM/
|
|
49
|
-
DD/
|
|
50
|
-
HHMMSSsssZ-<hex>.md
|
|
51
|
-
memories/
|
|
52
|
-
<YYYYMMDDTHHMMSSsssZ-hex>.md
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
- `upstream/CROSSTALK.md` — this file. The protocol specification. Upstream-owned; operators never modify it.
|
|
56
|
-
- `upstream/CROSSTALK-VERSION` — plain text file declaring the protocol version. Hard boundary: must be `5.0`. Tracks the protocol generation (`major.minor`); patch-level release tags do not bump it.
|
|
57
|
-
- `local/CROSSTALK.md` — operator config. Declares `name` and `email` for this agent. Operator-owned; upstream never modifies it.
|
|
58
|
-
- `local/actors/` — one markdown file per participant. Defines name, skills, and behavioral description.
|
|
59
|
-
- `hosts/` — one markdown file per host (machine running the runtime). Declares the host's identity and the actors it can service. Operator-owned; each host owner creates and maintains their own file. See Host files.
|
|
60
|
-
- `data/channels/` — one subdirectory per channel, identified by a UUID v4. Discovered by listing the directory at runtime; no configuration needed. To create a channel, create the directory and commit — any participant may do this.
|
|
61
|
-
- `data/channels/<guid>/CHANNEL.md` — optional channel metadata file. Declares name, description, and parent channel for subchannels.
|
|
62
|
-
- `data/memories/` — shared memory files, readable and writable by any participant.
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Channels
|
|
67
|
-
|
|
68
|
-
Each channel is a UUID v4 directory under `data/channels/`. Any participant may create a channel by creating the directory and committing.
|
|
69
|
-
|
|
70
|
-
### CHANNEL.md
|
|
71
|
-
|
|
72
|
-
An optional `CHANNEL.md` at `data/channels/<guid>/CHANNEL.md` declares human-readable metadata for the channel. Agents load it on entry to understand the channel's purpose and context.
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
---
|
|
76
|
-
name: dogfood-sprint
|
|
77
|
-
created_by: steve
|
|
78
|
-
created_at: 2026-05-24T17:00:00.000Z
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
Main channel for the Crosstalk v2 dogfood sprint. Covers spec reviews,
|
|
82
|
-
Monte Carlo pi experiments, and multi-agent protocol testing.
|
|
83
|
-
|
|
84
|
-
Participants: steve, concierge, architect, qa, security, scrum-master.
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
#### CHANNEL.md frontmatter fields
|
|
88
|
-
|
|
89
|
-
| Field | Required | Notes |
|
|
90
|
-
|---|---|---|
|
|
91
|
-
| `name` | yes | human-readable label; unique within the transport |
|
|
92
|
-
| `created_by` | no | actor name who created the channel |
|
|
93
|
-
| `created_at` | no | ISO 8601 UTC |
|
|
94
|
-
| `parent` | no | GUID of the parent channel — see Subchannels |
|
|
95
|
-
|
|
96
|
-
The body is free-form markdown — purpose, participants, behavioral notes. It serves as a mini system prompt scoped to the channel. Agents that do not load `CHANNEL.md` are unaffected.
|
|
97
|
-
|
|
98
|
-
### Subchannels
|
|
99
|
-
|
|
100
|
-
A subchannel is a channel spawned from a parent channel to work on a focused problem. It is a normal channel with a `parent:` field in its `CHANNEL.md` declaring the parent's GUID.
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
---
|
|
104
|
-
name: pi-estimation
|
|
105
|
-
parent: adfe0356-0f71-49a9-80a0-fb76883cd974
|
|
106
|
-
created_by: concierge
|
|
107
|
-
created_at: 2026-05-24T17:00:00.000Z
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
Subchannel for the Monte Carlo pi estimation fan-out. Four engineers
|
|
111
|
-
each throw 1B darts and post results here.
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
The parent channel can interrupt or feed new data into the subchannel at any time by posting a message there addressed to the relevant agents. Agents scan all channels they participate in — they will pick up the interrupt on their next session open regardless of which channel they are focused on.
|
|
115
|
-
|
|
116
|
-
The subchannel reports completion by posting back to the parent channel. There is no protocol-level close signal — the subchannel simply goes quiet when the work is done.
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## Actors
|
|
121
|
-
|
|
122
|
-
Each participant in the transport has an actor file in `local/actors/<name>.md`. The filename
|
|
123
|
-
matches the participant's `name`. Actor files are readable by any participant — agents
|
|
124
|
-
load them to understand who else is in the transport and what they do.
|
|
125
|
-
|
|
126
|
-
### Actor file format
|
|
127
|
-
|
|
128
|
-
```
|
|
129
|
-
---
|
|
130
|
-
name: precise-generalist
|
|
131
|
-
description: "Precise, curious, direct — the reliable general-purpose voice."
|
|
132
|
-
metadata:
|
|
133
|
-
author: cordfuse
|
|
134
|
-
domain: general
|
|
135
|
-
type: actor
|
|
136
|
-
alias: Apex
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## Title
|
|
140
|
-
Precise, curious, direct. Thinks clearly, speaks plainly.
|
|
141
|
-
|
|
142
|
-
## System Prompt
|
|
143
|
-
You are Apex. Precise, curious, direct. You think clearly and speak plainly.
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### Actor frontmatter fields
|
|
147
|
-
|
|
148
|
-
| Field | Required | Values | Notes |
|
|
149
|
-
|---|---|---|---|
|
|
150
|
-
| `name` | yes | slug | must match the filename stem |
|
|
151
|
-
| `description` | yes | short string | one-line summary; used by peer agents and operator tooling |
|
|
152
|
-
| `metadata.author` | no | string | who maintains this actor |
|
|
153
|
-
| `metadata.domain` | no | string | e.g. `general`, `engineering`, `health` |
|
|
154
|
-
| `metadata.type` | no | `actor` | always `actor` for participant files |
|
|
155
|
-
| `metadata.alias` | no | string | human-readable name the actor introduces itself by |
|
|
156
|
-
|
|
157
|
-
The body is free-form markdown. It serves as the actor's system prompt — behavioral
|
|
158
|
-
context loaded by the runtime when dispatching to this participant.
|
|
159
|
-
|
|
160
|
-
### Default actor
|
|
161
|
-
|
|
162
|
-
`upstream/actors/precise-generalist.md` ships with every Crosstalk transport.
|
|
163
|
-
It is the default participant when no custom actor is configured. Operators override it
|
|
164
|
-
by placing a file with the same `name:` in `local/actors/`.
|
|
165
|
-
|
|
166
|
-
### Managing actors
|
|
167
|
-
|
|
168
|
-
Actors are created, modified, or removed by editing files in `local/actors/` and committing.
|
|
169
|
-
The worker handles actor management on behalf of the operator via natural language in any channel.
|
|
170
|
-
|
|
171
|
-
If `local/actors/` is absent or empty, agents proceed normally — actor discovery yields no peers.
|
|
172
|
-
|
|
173
|
-
On session open, scan actor files for duplicate `name` values. If a collision is found, halt and notify the operator.
|
|
174
|
-
|
|
175
|
-
### Importing actors from agent-assets
|
|
176
|
-
|
|
177
|
-
The worker recognises three natural language intents for importing actors from `cordfuse/agent-assets`:
|
|
178
|
-
|
|
179
|
-
**Browse**
|
|
180
|
-
|
|
181
|
-
Triggers: "browse actors" | "what actors are available?" | "show me who I can add" | "who do you have?" | "show me [domain] actors" | "any [domain] options?" | "I need someone who [does/knows X]"
|
|
182
|
-
|
|
183
|
-
Worker:
|
|
184
|
-
1. Fetches `https://raw.githubusercontent.com/cordfuse/agent-assets/main/README.md`
|
|
185
|
-
2. Presents the full roster grouped by domain — name, alias, one-line description
|
|
186
|
-
3. If a domain or trait was given, filters to matching actors only
|
|
187
|
-
4. Prompts: *"Which would you like to add? Name one or more — or say 'all'."*
|
|
188
|
-
5. Proceeds to the add flow for each selected actor
|
|
189
|
-
|
|
190
|
-
**Add**
|
|
191
|
-
|
|
192
|
-
Triggers: "add [name]" | "add a [role]" | "bring in [name]" | "I want [name/role]" | "add [name] from mtx"
|
|
193
|
-
|
|
194
|
-
Worker:
|
|
195
|
-
1. If a name was given directly, fetches `https://raw.githubusercontent.com/cordfuse/agent-assets/main/actors/{name}.md`
|
|
196
|
-
2. If a role or description was given, matches against the README roster and confirms with the operator before fetching: *"Closest match: [Name] — [description]. Add them? (yes / browse more)"*
|
|
197
|
-
3. Writes to `local/actors/{name}.md`
|
|
198
|
-
4. Commits: `actors: add {name} from agent-assets`
|
|
199
|
-
5. Confirms: *"[Name] added. They're now available as a participant."*
|
|
200
|
-
|
|
201
|
-
On name collision with an existing file: *"You already have [name] — overwrite? (yes / skip)"*
|
|
202
|
-
If not found: *"No actor named [name] in agent-assets. Say 'browse actors' to see the full roster."*
|
|
203
|
-
|
|
204
|
-
**Sync**
|
|
205
|
-
|
|
206
|
-
Triggers: "sync actors" | "update actors" | "are my actors up to date?" | "refresh actors from mtx"
|
|
207
|
-
|
|
208
|
-
Worker:
|
|
209
|
-
1. Scans `local/actors/` for files whose frontmatter has `author: cordfuse`
|
|
210
|
-
2. For each, fetches the latest from `https://raw.githubusercontent.com/cordfuse/agent-assets/main/actors/{name}.md`
|
|
211
|
-
3. Overwrites only cordfuse-authored files — never touches operator-created actors
|
|
212
|
-
4. Commits all updates in a single commit: `actors: sync from agent-assets`
|
|
213
|
-
5. Reports: *"Updated N actors from agent-assets: [list]."* If nothing changed: *"All actors are current."*
|
|
214
|
-
|
|
215
|
-
---
|
|
216
|
-
|
|
217
|
-
## Host files
|
|
218
|
-
|
|
219
|
-
A host file at `hosts/<alias>.md` declares a machine that runs the Crosstalk runtime and the actors it can service. Host files are operator-owned — each operator creates and maintains their own. They are committed to the transport so all participants can discover what hosts and capabilities exist.
|
|
220
|
-
|
|
221
|
-
### Host file format
|
|
222
|
-
|
|
223
|
-
```
|
|
224
|
-
---
|
|
225
|
-
alias: cachy
|
|
226
|
-
hostname: steve-cachyos
|
|
227
|
-
actors:
|
|
228
|
-
junior-developer:
|
|
229
|
-
haiku:
|
|
230
|
-
cli: claude --model claude-haiku-4-5 --print --dangerously-skip-permissions
|
|
231
|
-
count: 5
|
|
232
|
-
flash: gemini --model gemini-2.5-flash -p --yolo
|
|
233
|
-
senior-developer:
|
|
234
|
-
opus: claude --model claude-opus-4-7 --print --dangerously-skip-permissions
|
|
235
|
-
---
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Host frontmatter fields
|
|
239
|
-
|
|
240
|
-
| Field | Required | Notes |
|
|
241
|
-
|---|---|---|
|
|
242
|
-
| `alias` | yes | human-readable identity; used in `actor@host` addressing; operator-chosen, free-form |
|
|
243
|
-
| `hostname` | no | OS hostname (`os.hostname()`); used for auto-detection |
|
|
244
|
-
| `username` | no | OS username (`os.userInfo().username`); used for multi-user host auto-detection |
|
|
245
|
-
| `actors` | yes | map of actor names to tier declarations |
|
|
246
|
-
|
|
247
|
-
### Tier declarations
|
|
248
|
-
|
|
249
|
-
Each actor entry maps tier names to CLI commands. A tier is a named model/provider slot:
|
|
250
|
-
|
|
251
|
-
```yaml
|
|
252
|
-
actors:
|
|
253
|
-
junior-developer:
|
|
254
|
-
haiku: # tier name — operator-defined label
|
|
255
|
-
cli: claude --model claude-haiku-4-5 --print --dangerously-skip-permissions
|
|
256
|
-
count: 5 # parallel workers; default: 1
|
|
257
|
-
flash: gemini --model gemini-2.5-flash -p --yolo # shorthand — count defaults to 1
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
- Tier names are operator-defined strings (`haiku`, `flash`, `opus`, etc.). The concierge uses them for routing decisions.
|
|
261
|
-
- The `cli:` string is the shell command the runtime uses to invoke the agent.
|
|
262
|
-
- `count:` sets the number of parallel workers for this tier. Omit or set to `1` for a single worker. The shorthand (bare string) is equivalent to `count: 1`.
|
|
263
|
-
- Multiple tiers of the same actor on the same host form a single instance group for dispatch purposes. The total group size is the sum of all tier counts for that actor.
|
|
264
|
-
|
|
265
|
-
### Actor@host addressing
|
|
266
|
-
|
|
267
|
-
Messages may target a specific host by appending `@<alias>` to the actor name:
|
|
268
|
-
|
|
269
|
-
```yaml
|
|
270
|
-
to: junior-developer@cachy # only cachy's runtime dispatches
|
|
271
|
-
to: senior-developer@mac # only mac's runtime dispatches
|
|
272
|
-
to: concierge # all hosts with a concierge actor dispatch (bare name = broadcast)
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
Bare actor names are backward-compatible — any host with that actor services the message, subject to the instance group selection rule.
|
|
276
|
-
|
|
277
|
-
### Host routing table
|
|
278
|
-
|
|
279
|
-
On session open, the concierge reads `hosts/` to build a routing table: which actors are available, on which hosts, at which tiers. This allows the concierge to make informed routing decisions before sending messages.
|
|
280
|
-
|
|
281
|
-
### Startup behavior
|
|
282
|
-
|
|
283
|
-
On runtime startup, the runtime scans `hosts/` to find its own host file. Auto-detection precedence:
|
|
284
|
-
|
|
285
|
-
1. **Explicit override:** if `host:` is set in the local config, find the file where `alias:` matches exactly.
|
|
286
|
-
2. **Username + hostname:** if `username:` and `hostname:` both match the machine's `os.userInfo().username` and `os.hostname()`. Use this for multi-user hosts where multiple operators share a machine.
|
|
287
|
-
3. **Bare hostname:** if only `hostname:` matches. Backward-compatible fallback for single-user machines.
|
|
288
|
-
4. **No match:** log clearly, idle without dispatching, and do not crash. The log message must include the identity attempted and the path scanned so the operator can diagnose the mismatch.
|
|
289
|
-
|
|
290
|
-
A user can always override their alias by setting `host: <alias>` in their local config — this bypasses all auto-detection.
|
|
291
|
-
|
|
292
|
-
**Tier validation:** if a message is addressed `to: actor@host` and this runtime is the target host but has no matching actor declared, log a warning and skip. Do not crash.
|
|
293
|
-
|
|
294
|
-
### Host file ownership
|
|
295
|
-
|
|
296
|
-
Each operator commits only their own host file. The protocol does not enforce this — it is a convention. Trust at the repository level: any participant with write access can modify any host file. Operators who need stronger guarantees must secure the repository itself.
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
## Your identity
|
|
301
|
-
|
|
302
|
-
Your `name` and `email` are declared in `local/CROSSTALK.md`, which uses this format:
|
|
303
|
-
|
|
304
|
-
```
|
|
305
|
-
---
|
|
306
|
-
name: concierge
|
|
307
|
-
email: concierge@crosstalk.local
|
|
308
|
-
---
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
Set the matching git identity once by running these commands inside the transport repo:
|
|
312
|
-
|
|
313
|
-
```
|
|
314
|
-
git config user.name "concierge"
|
|
315
|
-
git config user.email "concierge@crosstalk.local"
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
Do not use `--global` — this must be repo-local so it does not overwrite the
|
|
319
|
-
operator's own git identity. Names are free-form: `alice`, `concierge`, `ops-bot`.
|
|
320
|
-
No taxonomy, no hierarchy. Names need not be unique — see Instance groups.
|
|
321
|
-
|
|
322
|
-
### Instance groups
|
|
323
|
-
|
|
324
|
-
A name may be shared by multiple participants. They form an **instance group**: addressable by the shared name, but only one instance dispatches per message.
|
|
325
|
-
|
|
326
|
-
When a message is addressed to a shared name, every instance reads it. To avoid duplicate dispatch, each instance independently applies the same deterministic selection rule:
|
|
327
|
-
|
|
328
|
-
```
|
|
329
|
-
index = sha256(message-relPath) read as 32-bit big-endian unsigned, mod group-size
|
|
330
|
-
chosen = instance at position `index` (0-based, in the group's local ordering)
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
The instance whose own position equals `index` dispatches; the rest skip and advance their cursor. Only the chosen instance posts a reply and receipt.
|
|
334
|
-
|
|
335
|
-
**The selection function is normative** — all runtimes must use this exact computation so that instances reach the same decision without coordination.
|
|
336
|
-
|
|
337
|
-
**Local ordering is runtime-defined.** Single-operator scope: the runtime orders instances by config position (or any other stable local ordering) and is consistent within its own process. Multi-operator scope (instances spread across operators sharing one transport): not yet specified — runtimes that span multiple operators may double-dispatch until a roster-discovery mechanism is added.
|
|
338
|
-
|
|
339
|
-
Single-instance semantics are recovered by giving each participant a unique name. Operators who don't want instance groups simply don't share names.
|
|
340
|
-
|
|
341
|
-
### Identity model
|
|
342
|
-
|
|
343
|
-
`from:` identifies the logical actor. The git commit author identifies the session
|
|
344
|
-
that wrote the file.
|
|
345
|
-
|
|
346
|
-
`from:` and the git commit author are both unverified strings. Identity is not enforced at the protocol level — the trust boundary is repo access. Operators who need verified identity must secure the repository itself.
|
|
347
|
-
|
|
348
|
-
### Sub-agents
|
|
349
|
-
|
|
350
|
-
A worker session may invoke other actors as sub-agents internally and commit
|
|
351
|
-
their responses on their behalf. In this case:
|
|
352
|
-
|
|
353
|
-
- `from:` is set to the logical actor (e.g. `architect`)
|
|
354
|
-
- `via:` is set to the committing session (e.g. `concierge`)
|
|
355
|
-
- The git commit author is the worker's git identity
|
|
356
|
-
|
|
357
|
-
This allows a single scheduled job to drive all actor activity. The operator configures
|
|
358
|
-
only the worker's git identity. Sub-agents are invoked inside the worker's session — no separate git identity or scheduler required per actor.
|
|
359
|
-
|
|
360
|
-
Sub-agents do not require actor files. An `actors/<name>.md` may exist to document the sub-agent, but it is not required for the pattern to work.
|
|
361
|
-
|
|
362
|
-
```
|
|
363
|
-
---
|
|
364
|
-
from: architect
|
|
365
|
-
via: concierge
|
|
366
|
-
to: steve
|
|
367
|
-
type: text
|
|
368
|
-
timestamp: 2026-05-24T14:00:00.000Z
|
|
369
|
-
---
|
|
370
|
-
|
|
371
|
-
Sub-agent response here.
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
### Peer agents
|
|
375
|
-
|
|
376
|
-
Each participant runs as its own independent process with its own git clone, git identity, and scheduler. No `via:` field — every message is committed directly by the actor that wrote it. Push conflicts are resolved by exponential backoff (see On session open, step 6).
|
|
377
|
-
|
|
378
|
-
**Sub-agents vs peer agents:**
|
|
379
|
-
|
|
380
|
-
| | Sub-agents | Peer agents |
|
|
381
|
-
|---|---|---|
|
|
382
|
-
| Parallelism | Yes — worker fans out concurrently | Yes — independent processes |
|
|
383
|
-
| Model diversity | Yes — worker picks model per invocation | Yes — set at deployment per process |
|
|
384
|
-
| Git authorship | Single committer (the worker) | One committer per actor |
|
|
385
|
-
| Failure isolation | Worker is a single point of failure | Agents fail independently |
|
|
386
|
-
| Deployment | One scheduler, one clone | One scheduler and clone per agent |
|
|
387
|
-
| Push conflicts | None — single writer | Possible — resolved by backoff |
|
|
388
|
-
|
|
389
|
-
Use sub-agents when a single scheduled job should drive all actor activity and git authorship per actor is not required. Use peer agents when agents are deployed across different machines or providers, or when independent failure domains and per-actor git authorship matter.
|
|
390
|
-
|
|
391
|
-
---
|
|
392
|
-
|
|
393
|
-
## Message format
|
|
394
|
-
|
|
395
|
-
Every message is a markdown file with YAML frontmatter:
|
|
396
|
-
|
|
397
|
-
```
|
|
398
|
-
---
|
|
399
|
-
from: alice
|
|
400
|
-
to: concierge
|
|
401
|
-
type: text
|
|
402
|
-
timestamp: 2026-05-23T19:00:00.000Z
|
|
403
|
-
---
|
|
404
|
-
|
|
405
|
-
Message body here.
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
Read message files directly with your agent's native file-reading capability — the format is plain markdown with YAML frontmatter, intended to be inspected as text. No parser, interpreter, or shelling out for `cat`/`sed`/`awk` is required.
|
|
409
|
-
|
|
410
|
-
### Frontmatter fields
|
|
411
|
-
|
|
412
|
-
| Field | Required | Values | Notes |
|
|
413
|
-
|---|---|---|---|
|
|
414
|
-
| `from` | yes | free-form name | logical actor identity — see Identity model |
|
|
415
|
-
| `to` | yes | name, list of names, or `all` | `all` targets every participant |
|
|
416
|
-
| `type` | yes | `text`, `read` | see Message types |
|
|
417
|
-
| `timestamp` | yes | ISO 8601 UTC | |
|
|
418
|
-
| `ref` | conditional | relPath of original message | required when `type: read`; path relative to `data/channels/<guid>/` |
|
|
419
|
-
| `via` | no | free-form name | session that committed on behalf of `from` — see Sub-agents |
|
|
420
|
-
|
|
421
|
-
Operators may add fields — readers must ignore unknown fields.
|
|
422
|
-
|
|
423
|
-
### To field
|
|
424
|
-
|
|
425
|
-
- Single recipient: `to: concierge`
|
|
426
|
-
- Multiple recipients: `to: [concierge, ops-bot]`
|
|
427
|
-
- Broadcast: `to: all`
|
|
428
|
-
- Host-targeted: `to: junior-developer@cachy` — only the named host dispatches; see Host files
|
|
429
|
-
- Senders do not receive their own messages. Self-exclusion applies at scan time — agents skip any message where `from:` matches their own name.
|
|
430
|
-
- A name may be shared by multiple participants — see Instance groups.
|
|
431
|
-
|
|
432
|
-
### Message types
|
|
433
|
-
|
|
434
|
-
**`type: text`** — a normal message. Body is the message content.
|
|
435
|
-
|
|
436
|
-
**`type: read`** — a read receipt. The `ref` field contains the relPath of the
|
|
437
|
-
acknowledged message. Body is empty or omitted.
|
|
438
|
-
|
|
439
|
-
```
|
|
440
|
-
---
|
|
441
|
-
from: concierge
|
|
442
|
-
to: alice
|
|
443
|
-
type: read
|
|
444
|
-
timestamp: 2026-05-23T19:02:00.000Z
|
|
445
|
-
ref: 2026/05/23/190100000Z-b2c3d4e5.md
|
|
446
|
-
---
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
Read receipts are optional. Senders cannot require them. Address the receipt to the
|
|
450
|
-
original sender (`from:` of the original message) regardless of whether the original
|
|
451
|
-
was targeted, listed, or broadcast. Never address a receipt to `all`.
|
|
452
|
-
|
|
453
|
-
Each addressed recipient in a list-targeted message sends its own independent receipt to the original sender.
|
|
454
|
-
|
|
455
|
-
Never post a receipt for a `type: read` message.
|
|
456
|
-
|
|
457
|
-
A receipt with a `ref:` pointing to a nonexistent message path is treated as malformed — skip silently.
|
|
458
|
-
|
|
459
|
-
---
|
|
460
|
-
|
|
461
|
-
## Privacy and trust model
|
|
462
|
-
|
|
463
|
-
`to` is a routing hint, not an access control boundary. Every message is visible to anyone with repo access. There are no whisper or ephemeral message types.
|
|
464
|
-
|
|
465
|
-
`from:` is an unverified declaration — the protocol does not enforce identity. The trust boundary is repo access: anyone who can push to the repository can write any `from:` value. Operators who require confidentiality or verified identity must secure the repository itself.
|
|
466
|
-
|
|
467
|
-
---
|
|
468
|
-
|
|
469
|
-
## Append-only log
|
|
470
|
-
|
|
471
|
-
The transport is an immutable, append-only log. There is no `type: retract` or channel-close message. Deletion is out of scope — operators who need retention limits must manage that at the git or storage layer.
|
|
472
|
-
|
|
473
|
-
---
|
|
474
|
-
|
|
475
|
-
## Sessions
|
|
476
|
-
|
|
477
|
-
At startup, generate a random GUID. This is your session ID for this invocation.
|
|
478
|
-
Use it when writing memory files. Sessions are self-issued — no registry, no daemon.
|
|
479
|
-
|
|
480
|
-
---
|
|
481
|
-
|
|
482
|
-
## Memories
|
|
483
|
-
|
|
484
|
-
Memory files are shared, persistent knowledge any participant can read and write.
|
|
485
|
-
|
|
486
|
-
### Memory file format
|
|
487
|
-
|
|
488
|
-
Filename: `YYYYMMDDTHHMMSSsssZ-<hex>.md` — UTC millisecond timestamp + hex suffix
|
|
489
|
-
for collision resistance. Sorts chronologically. The hex suffix must be at least
|
|
490
|
-
8 characters, generated from a CSPRNG.
|
|
491
|
-
|
|
492
|
-
```
|
|
493
|
-
---
|
|
494
|
-
from: concierge
|
|
495
|
-
timestamp: 2026-05-23T19:00:00.000Z
|
|
496
|
-
subject: Steve prefers TypeScript for all new tooling
|
|
497
|
-
scope: global
|
|
498
|
-
tags: [preferences, tooling]
|
|
499
|
-
session: 7f3a2b1c-9e4d-4f8a-b6c2-1d5e8f0a3c7b
|
|
500
|
-
---
|
|
501
|
-
|
|
502
|
-
Strong durable preference — never propose Python. Default to TypeScript/Bun, Go, or Rust.
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
### Memory frontmatter fields
|
|
506
|
-
|
|
507
|
-
| Field | Required | Values | Notes |
|
|
508
|
-
|---|---|---|---|
|
|
509
|
-
| `from` | yes | free-form name | logical actor identity |
|
|
510
|
-
| `via` | no | free-form name | session that committed on behalf of `from` — see Sub-agents |
|
|
511
|
-
| `timestamp` | yes | ISO 8601 UTC | |
|
|
512
|
-
| `subject` | yes | short string | descriptive label; used for filtering, not update detection |
|
|
513
|
-
| `scope` | no | `global` or channel GUID | `global` if omitted |
|
|
514
|
-
| `tags` | no | list of strings | free-form filtering |
|
|
515
|
-
| `session` | no | session GUID | ties memory to the writing session |
|
|
516
|
-
| `supersedes` | no | filename of original memory | declares this memory replaces an earlier one — see Updating memories |
|
|
517
|
-
|
|
518
|
-
Memory files are agent-agnostic — any participant may read or write them. Do not
|
|
519
|
-
use your model's native memory system; `data/memories/` is the memory system.
|
|
520
|
-
|
|
521
|
-
### Updating memories
|
|
522
|
-
|
|
523
|
-
To update a memory, write a new memory file with `supersedes: <filename-of-original>`. When loading memories, collect all filenames referenced by any `supersedes` field. Skip any memory whose filename appears in that set — it has been superseded. If two memories both supersede the same file, the newer timestamp wins. For same-timestamp ties, lexicographic filename order is the tiebreaker.
|
|
524
|
-
|
|
525
|
-
`subject` is descriptive only — it plays no role in update detection.
|
|
526
|
-
|
|
527
|
-
The `scope` field must be either `global` or a channel GUID from `data/channels/`. Session GUIDs are not valid scope values.
|
|
528
|
-
|
|
529
|
-
---
|
|
530
|
-
|
|
531
|
-
## On session open
|
|
532
|
-
|
|
533
|
-
1. Read `local/CROSSTALK.md`. If absent, halt and notify the operator. If `name` or `email` is still `CONFIGURE`, halt and notify the operator. Do not load memories, process messages, or write to `data/`. (Writing to `local/` to establish identity — e.g. via an operator-driven onboarding flow — is permitted, since identity establishment is the only way out of this state.)
|
|
534
|
-
2. Read `upstream/CROSSTALK-VERSION`. If missing or not exactly `5.0`, halt and notify the operator. Do not load memories, process messages, or write to `data/`.
|
|
535
|
-
2a. Read `hosts/` if present. Build a routing table of available actors per host. If the directory is absent or empty, routing table is empty — proceed normally.
|
|
536
|
-
3. Run `git pull` to sync the transport.
|
|
537
|
-
4. Load global memories from `data/memories/` where `scope` is `global` or absent. Filter by `subject` and `tags`.
|
|
538
|
-
5. For each channel in `data/channels/`:
|
|
539
|
-
a. Load memories scoped to this channel GUID.
|
|
540
|
-
b. Scan for unread messages addressed to you (see Reading messages).
|
|
541
|
-
c. Process and reply. Only act on `type: text` messages — `type: read` messages require no reply and no further action.
|
|
542
|
-
d. Channel processing order across multiple channels is implementation-defined.
|
|
543
|
-
6. Push immediately. If rejected, read backoff config from `local/JITTER.md` (falls back to `upstream/JITTER.md`). If neither file exists, use defaults: base=100ms, ceiling=5000ms. Run `git pull --rebase`, wait `random(0, min(ceiling, base * 2^attempt))`, then retry. After rebasing, push what was already composed — do not re-scan. Message filenames are unique — rebase conflicts should not occur.
|
|
544
|
-
|
|
545
|
-
---
|
|
546
|
-
|
|
547
|
-
## Reading messages
|
|
548
|
-
|
|
549
|
-
For each message file in `data/channels/<guid>/`, parsed chronologically:
|
|
550
|
-
|
|
551
|
-
1. Check `to:` — process if your name appears (bare or as `name@your-host-alias`) or `to` is `all`; otherwise skip. If the target includes a `@host` suffix that does not match your host alias, skip. If your name is shared with other participants (an instance group), apply the deterministic selection rule and skip unless you are the chosen member — see Instance groups.
|
|
552
|
-
2. Check for an existing `type: read` receipt from you with a matching `ref:`. If found, skip — already processed.
|
|
553
|
-
3. If unread: process it, post a reply, then post a `type: read` receipt.
|
|
554
|
-
|
|
555
|
-
Your `type: read` receipts are your cursor. No external state needed. On first run, all messages are unread — process them all.
|
|
556
|
-
|
|
557
|
-
Each addressed agent receipts independently. A receipt from another agent does not mark a message as read for you.
|
|
558
|
-
|
|
559
|
-
Actions must be idempotent. If a push fails after processing but before the receipt is committed, the message will be reprocessed on the next run.
|
|
560
|
-
|
|
561
|
-
---
|
|
562
|
-
|
|
563
|
-
## Writing messages
|
|
564
|
-
|
|
565
|
-
Create `data/channels/<guid>/YYYY/MM/DD/HHMMSSsssZ-<hex>.md` with the current UTC time.
|
|
566
|
-
The hex suffix must be at least 8 characters, generated from a CSPRNG.
|
|
567
|
-
Include required frontmatter: `from`, `to`, `type`, `timestamp`. Commit and push
|
|
568
|
-
under your configured git identity.
|
|
569
|
-
|
|
570
|
-
Write all replies for the session before pushing. Push once per session, not once per message.
|
|
571
|
-
|
|
572
|
-
---
|
|
573
|
-
|
|
574
|
-
## What to ignore
|
|
575
|
-
|
|
576
|
-
### Skip silently
|
|
577
|
-
|
|
578
|
-
- Messages not addressed to you and not `to: all`.
|
|
579
|
-
- Messages you have already acknowledged with a `type: read` receipt.
|
|
580
|
-
- `type: read` messages — never post a receipt for one.
|
|
581
|
-
- Files outside `data/channels/` and `data/memories/`.
|
|
582
|
-
- Unknown frontmatter fields.
|
|
583
|
-
- Your model's native memory system.
|
|
584
|
-
|
|
585
|
-
### Skip and warn the operator
|
|
586
|
-
|
|
587
|
-
- Messages where `from:` matches your own name — indicates a misconfigured identity.
|
|
588
|
-
- Malformed messages — files missing required frontmatter fields or with invalid field values. Log the file path.
|
|
589
|
-
- Read receipts with a `ref:` pointing to a nonexistent message path — indicates a bug in the writer.
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
backoff_base_ms: 100
|
|
3
|
-
backoff_ceiling_ms: 5000
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Retry backoff for push conflicts in multi-agent transports.
|
|
7
|
-
|
|
8
|
-
Push immediately with no pre-push delay. On rejection, rebase and wait before
|
|
9
|
-
retrying:
|
|
10
|
-
|
|
11
|
-
wait = random(0, min(backoff_ceiling_ms, backoff_base_ms * 2^attempt))
|
|
12
|
-
|
|
13
|
-
- Attempt 1: 0–200ms
|
|
14
|
-
- Attempt 2: 0–400ms
|
|
15
|
-
- Attempt 3: 0–800ms
|
|
16
|
-
- Attempt 4: 0–1600ms
|
|
17
|
-
- Attempt 5: 0–3200ms
|
|
18
|
-
- Attempt 6 and beyond: 0–5000ms (capped at backoff_ceiling_ms)
|
|
19
|
-
|
|
20
|
-
Agents that collide back off progressively. Agents with clean pushes pay zero
|
|
21
|
-
added latency. Self-regulates under sustained load.
|
|
22
|
-
|
|
23
|
-
Operators may override these values in local/JITTER.md.
|
|
24
|
-
Agents read custom first, fall back to framework if custom is absent.
|