clowl 0.2.0__tar.gz

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.
clowl-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: clowl
3
+ Version: 0.2.0
4
+ Summary: CLowl - A structured communication language for AI agent-to-agent messaging
5
+ Author-email: Oscar Sterling Agency <oscar@oscarsterling.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://clowl.dev
8
+ Project-URL: Repository, https://github.com/oscarsterling/clowl
9
+ Project-URL: Documentation, https://clowl.dev
10
+ Keywords: clowl,agent,ai,protocol,communication,multi-agent
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+
25
+ # CLowl
26
+
27
+ **A language for AI agents that humans can read.**
28
+
29
+ CLowl (Claw + Talk) is a structured communication protocol for AI agent-to-agent messaging. It defines a minimal JSON schema with typed performatives, message metadata, and context referencing that runs on top of any transport (MCP, A2A, HTTP, WebSocket, stdio, etc.). Every CLowl message has a deterministic English translation, so enterprises can audit any agent conversation and developers can debug multi-agent pipelines in seconds.
30
+
31
+ ---
32
+
33
+ ## Install
34
+
35
+ **TypeScript / Node.js:**
36
+ ```bash
37
+ npm install clowl-protocol
38
+ ```
39
+
40
+ **Python:**
41
+ ```bash
42
+ pip install clowl
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Quick Start
48
+
49
+ **TypeScript:**
50
+ ```typescript
51
+ import { createReq, generateCid, generateTid } from "clowl";
52
+
53
+ const cid = generateCid();
54
+ const req = createReq("oscar", "radar", cid, "search", { q: "MCP vs A2A" }, { tid: generateTid() });
55
+ console.log(req.toHuman());
56
+ ```
57
+
58
+ **Python:**
59
+ ```python
60
+ from clowl import create_req, generate_cid, generate_tid
61
+
62
+ cid = generate_cid()
63
+ req = create_req("oscar", "radar", cid, "search", {"q": "MCP vs A2A"}, tid=generate_tid())
64
+ print(req.to_human())
65
+ ```
66
+
67
+ ---
68
+
69
+ ## The Problem
70
+
71
+ When AI agents talk to each other, nobody knows what they're saying.
72
+
73
+ Agent A sends a blob of unstructured text to Agent B. Agent B replies with another blob. Somewhere in that chain, something goes wrong, and you have no idea what was requested, what was delegated, or where the task died. Multi-agent systems are powerful and opaque in equal measure.
74
+
75
+ ---
76
+
77
+ ## The Solution
78
+
79
+ CLowl is a language agents speak and humans can read. It defines:
80
+
81
+ - **Structured messages** with typed intent (requests, delegates, errors, progress updates)
82
+ - **Message metadata** for tracing, dedup, and conversation reconstruction
83
+ - **A live translator** that converts CLowl JSON to plain English instantly
84
+
85
+ ---
86
+
87
+ ## Message Format
88
+
89
+ Every CLowl message is a JSON object with 8 required fields:
90
+
91
+ ```json
92
+ {
93
+ "clowl": "0.2",
94
+ "mid": "01914b2c-7abc-...",
95
+ "ts": 1709078400,
96
+ "p": "REQ",
97
+ "from": "oscar",
98
+ "to": "radar",
99
+ "cid": "conv-001",
100
+ "body": {
101
+ "t": "search",
102
+ "d": { "q": "MCP vs A2A", "scope": "web" }
103
+ }
104
+ }
105
+ ```
106
+
107
+ Optional fields: `tid` (trace ID), `pid` (parent message), `ctx` (context reference), `auth` (auth token), `det` (determinism flag).
108
+
109
+ See the [full spec](spec-v0.2.md) for complete field definitions.
110
+
111
+ ---
112
+
113
+ ## Performatives
114
+
115
+ | Code | Name | Meaning |
116
+ |------|------|---------|
117
+ | `REQ` | Request | "Do this thing." Initiates a task. |
118
+ | `INF` | Inform | "Here is information." No action expected. |
119
+ | `ACK` | Acknowledge | "Got it, proceeding." |
120
+ | `ERR` | Error | "Failed. Here's why." Structured error code + message. |
121
+ | `DLGT` | Delegate | "Passing to someone better suited." Requires `delegation_mode`: `transfer`, `fork`, or `assist`. |
122
+ | `DONE` | Complete | "Finished. Here's the result." |
123
+ | `CNCL` | Cancel | "Abort this task." |
124
+ | `QRY` | Query | "What's the status?" or "Give me info without acting." |
125
+ | `PROG` | Progress | "Here's an update on the running task." |
126
+ | `CAPS` | Capabilities | "Here's what I can do." Broadcast on connection. |
127
+
128
+ ---
129
+
130
+ ## The Translator
131
+
132
+ Paste CLowl JSON, get English. Paste English, get CLowl JSON. No API needed.
133
+
134
+ ```bash
135
+ $ python translator.py '{"clowl":"0.2","mid":"m001","ts":1709078400,"tid":"t001","p":"REQ","from":"oscar","to":"radar","cid":"c001","body":{"t":"search","d":{"q":"CLowl competitors","scope":"web"}}}'
136
+
137
+ [2026-02-27 12:00:00 UTC] [t001] [m001...] oscar > radar: REQUEST search
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Integration Options
143
+
144
+ **Option 1: Inject the system prompt**
145
+
146
+ Add [`system-prompt-v0.2.md`](system-prompt-v0.2.md) to your agent's system prompt. Any LLM will start generating CLowl messages immediately.
147
+
148
+ **Option 2: Add the JSON schema to your tool calls**
149
+
150
+ Use [`clowl-schema.json`](clowl-schema.json) as a function-calling tool definition. Works with OpenAI, Anthropic, Google, and any local model that supports structured output.
151
+
152
+ **Option 3: Use the libraries**
153
+
154
+ TypeScript and Python libraries provide message creation, validation, state tracking, and translation with zero external dependencies.
155
+
156
+ ---
157
+
158
+ ## Examples
159
+
160
+ See [`examples-v0.2.md`](examples-v0.2.md) for full examples with CLowl JSON and English translations.
161
+
162
+ ---
163
+
164
+ ## Roadmap
165
+
166
+ **v0.3 (planned)**
167
+ - Three-layer architecture (Semantic / Coordination / Transport)
168
+ - Streaming progress (chunked PROG messages)
169
+ - Cryptographic message signing
170
+ - Go SDK
171
+
172
+ **v1.0 (stable)**
173
+ - Breaking changes locked out
174
+ - Binary encoding option (MessagePack)
175
+ - Production SDK with retry, dedup, and dead-letter queue support
176
+
177
+ ---
178
+
179
+ ## Contributing
180
+
181
+ CLowl is an open spec. Contributions welcome:
182
+
183
+ 1. Fork the repo
184
+ 2. Read [`spec-v0.2.md`](spec-v0.2.md) for the source of truth
185
+ 3. Open an issue for design questions before building
186
+ 4. PRs should include spec changes + updated examples
187
+
188
+ ---
189
+
190
+ ## License
191
+
192
+ MIT
193
+
194
+ ---
195
+
196
+ Built by [Oscar Sterling Agency](https://clowl.dev) | [clowl.dev](https://clowl.dev)
clowl-0.2.0/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # CLowl
2
+
3
+ **A language for AI agents that humans can read.**
4
+
5
+ CLowl (Claw + Talk) is a structured communication protocol for AI agent-to-agent messaging. It defines a minimal JSON schema with typed performatives, message metadata, and context referencing that runs on top of any transport (MCP, A2A, HTTP, WebSocket, stdio, etc.). Every CLowl message has a deterministic English translation, so enterprises can audit any agent conversation and developers can debug multi-agent pipelines in seconds.
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ **TypeScript / Node.js:**
12
+ ```bash
13
+ npm install clowl-protocol
14
+ ```
15
+
16
+ **Python:**
17
+ ```bash
18
+ pip install clowl
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Quick Start
24
+
25
+ **TypeScript:**
26
+ ```typescript
27
+ import { createReq, generateCid, generateTid } from "clowl";
28
+
29
+ const cid = generateCid();
30
+ const req = createReq("oscar", "radar", cid, "search", { q: "MCP vs A2A" }, { tid: generateTid() });
31
+ console.log(req.toHuman());
32
+ ```
33
+
34
+ **Python:**
35
+ ```python
36
+ from clowl import create_req, generate_cid, generate_tid
37
+
38
+ cid = generate_cid()
39
+ req = create_req("oscar", "radar", cid, "search", {"q": "MCP vs A2A"}, tid=generate_tid())
40
+ print(req.to_human())
41
+ ```
42
+
43
+ ---
44
+
45
+ ## The Problem
46
+
47
+ When AI agents talk to each other, nobody knows what they're saying.
48
+
49
+ Agent A sends a blob of unstructured text to Agent B. Agent B replies with another blob. Somewhere in that chain, something goes wrong, and you have no idea what was requested, what was delegated, or where the task died. Multi-agent systems are powerful and opaque in equal measure.
50
+
51
+ ---
52
+
53
+ ## The Solution
54
+
55
+ CLowl is a language agents speak and humans can read. It defines:
56
+
57
+ - **Structured messages** with typed intent (requests, delegates, errors, progress updates)
58
+ - **Message metadata** for tracing, dedup, and conversation reconstruction
59
+ - **A live translator** that converts CLowl JSON to plain English instantly
60
+
61
+ ---
62
+
63
+ ## Message Format
64
+
65
+ Every CLowl message is a JSON object with 8 required fields:
66
+
67
+ ```json
68
+ {
69
+ "clowl": "0.2",
70
+ "mid": "01914b2c-7abc-...",
71
+ "ts": 1709078400,
72
+ "p": "REQ",
73
+ "from": "oscar",
74
+ "to": "radar",
75
+ "cid": "conv-001",
76
+ "body": {
77
+ "t": "search",
78
+ "d": { "q": "MCP vs A2A", "scope": "web" }
79
+ }
80
+ }
81
+ ```
82
+
83
+ Optional fields: `tid` (trace ID), `pid` (parent message), `ctx` (context reference), `auth` (auth token), `det` (determinism flag).
84
+
85
+ See the [full spec](spec-v0.2.md) for complete field definitions.
86
+
87
+ ---
88
+
89
+ ## Performatives
90
+
91
+ | Code | Name | Meaning |
92
+ |------|------|---------|
93
+ | `REQ` | Request | "Do this thing." Initiates a task. |
94
+ | `INF` | Inform | "Here is information." No action expected. |
95
+ | `ACK` | Acknowledge | "Got it, proceeding." |
96
+ | `ERR` | Error | "Failed. Here's why." Structured error code + message. |
97
+ | `DLGT` | Delegate | "Passing to someone better suited." Requires `delegation_mode`: `transfer`, `fork`, or `assist`. |
98
+ | `DONE` | Complete | "Finished. Here's the result." |
99
+ | `CNCL` | Cancel | "Abort this task." |
100
+ | `QRY` | Query | "What's the status?" or "Give me info without acting." |
101
+ | `PROG` | Progress | "Here's an update on the running task." |
102
+ | `CAPS` | Capabilities | "Here's what I can do." Broadcast on connection. |
103
+
104
+ ---
105
+
106
+ ## The Translator
107
+
108
+ Paste CLowl JSON, get English. Paste English, get CLowl JSON. No API needed.
109
+
110
+ ```bash
111
+ $ python translator.py '{"clowl":"0.2","mid":"m001","ts":1709078400,"tid":"t001","p":"REQ","from":"oscar","to":"radar","cid":"c001","body":{"t":"search","d":{"q":"CLowl competitors","scope":"web"}}}'
112
+
113
+ [2026-02-27 12:00:00 UTC] [t001] [m001...] oscar > radar: REQUEST search
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Integration Options
119
+
120
+ **Option 1: Inject the system prompt**
121
+
122
+ Add [`system-prompt-v0.2.md`](system-prompt-v0.2.md) to your agent's system prompt. Any LLM will start generating CLowl messages immediately.
123
+
124
+ **Option 2: Add the JSON schema to your tool calls**
125
+
126
+ Use [`clowl-schema.json`](clowl-schema.json) as a function-calling tool definition. Works with OpenAI, Anthropic, Google, and any local model that supports structured output.
127
+
128
+ **Option 3: Use the libraries**
129
+
130
+ TypeScript and Python libraries provide message creation, validation, state tracking, and translation with zero external dependencies.
131
+
132
+ ---
133
+
134
+ ## Examples
135
+
136
+ See [`examples-v0.2.md`](examples-v0.2.md) for full examples with CLowl JSON and English translations.
137
+
138
+ ---
139
+
140
+ ## Roadmap
141
+
142
+ **v0.3 (planned)**
143
+ - Three-layer architecture (Semantic / Coordination / Transport)
144
+ - Streaming progress (chunked PROG messages)
145
+ - Cryptographic message signing
146
+ - Go SDK
147
+
148
+ **v1.0 (stable)**
149
+ - Breaking changes locked out
150
+ - Binary encoding option (MessagePack)
151
+ - Production SDK with retry, dedup, and dead-letter queue support
152
+
153
+ ---
154
+
155
+ ## Contributing
156
+
157
+ CLowl is an open spec. Contributions welcome:
158
+
159
+ 1. Fork the repo
160
+ 2. Read [`spec-v0.2.md`](spec-v0.2.md) for the source of truth
161
+ 3. Open an issue for design questions before building
162
+ 4. PRs should include spec changes + updated examples
163
+
164
+ ---
165
+
166
+ ## License
167
+
168
+ MIT
169
+
170
+ ---
171
+
172
+ Built by [Oscar Sterling Agency](https://clowl.dev) | [clowl.dev](https://clowl.dev)
@@ -0,0 +1,46 @@
1
+ """CLowl v0.2 - A structured communication language for AI agent-to-agent messaging.
2
+
3
+ Usage:
4
+ from clowl import CLowlMessage, create_req, create_done, generate_cid, generate_tid
5
+ """
6
+
7
+ from .clowl import (
8
+ CLOWL_VERSION,
9
+ VALID_PERFORMATIVES,
10
+ VALID_DELEGATION_MODES,
11
+ PERFORMATIVE_NAMES,
12
+ CLowlMessage,
13
+ generate_mid,
14
+ generate_cid,
15
+ generate_tid,
16
+ sha256_of_file,
17
+ create_req,
18
+ create_ack,
19
+ create_done,
20
+ create_err,
21
+ create_dlgt,
22
+ create_prog,
23
+ create_caps,
24
+ create_cncl,
25
+ )
26
+
27
+ __version__ = CLOWL_VERSION
28
+ __all__ = [
29
+ "CLOWL_VERSION",
30
+ "VALID_PERFORMATIVES",
31
+ "VALID_DELEGATION_MODES",
32
+ "PERFORMATIVE_NAMES",
33
+ "CLowlMessage",
34
+ "generate_mid",
35
+ "generate_cid",
36
+ "generate_tid",
37
+ "sha256_of_file",
38
+ "create_req",
39
+ "create_ack",
40
+ "create_done",
41
+ "create_err",
42
+ "create_dlgt",
43
+ "create_prog",
44
+ "create_caps",
45
+ "create_cncl",
46
+ ]
@@ -0,0 +1,493 @@
1
+ #!/usr/bin/env python3
2
+ """CLowl v0.2 — Reference Python Library
3
+
4
+ A minimal, stdlib-only Python library for creating, validating, and translating
5
+ CLowl messages.
6
+
7
+ No external dependencies. Python 3.8+ only.
8
+ """
9
+
10
+ import json
11
+ import time
12
+ import uuid
13
+ import hashlib
14
+ from datetime import datetime, timezone
15
+ from typing import Optional, Union, List
16
+
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Constants
20
+ # ---------------------------------------------------------------------------
21
+
22
+ CLOWL_VERSION = "0.2"
23
+
24
+ VALID_PERFORMATIVES = frozenset({
25
+ "REQ", "INF", "ACK", "ERR", "DLGT", "DONE", "CNCL", "QRY", "PROG", "CAPS"
26
+ })
27
+
28
+ VALID_DELEGATION_MODES = frozenset({"transfer", "fork", "assist"})
29
+
30
+ PERFORMATIVE_NAMES = {
31
+ "REQ": "REQUEST",
32
+ "INF": "INFORM",
33
+ "ACK": "ACKNOWLEDGE",
34
+ "ERR": "ERROR",
35
+ "DLGT": "DELEGATE",
36
+ "DONE": "COMPLETE",
37
+ "CNCL": "CANCEL",
38
+ "QRY": "QUERY",
39
+ "PROG": "PROGRESS",
40
+ "CAPS": "CAPABILITIES",
41
+ }
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # ID generation
46
+ # ---------------------------------------------------------------------------
47
+
48
+ def generate_mid() -> str:
49
+ """Generate a time-ordered message ID (UUIDv7-style).
50
+
51
+ Format: <timestamp_ms_hex>-<4-hex-groups from uuid4>
52
+ Sorts lexicographically by creation time.
53
+ """
54
+ ts_ms = int(time.time() * 1000)
55
+ rand = uuid.uuid4().hex
56
+ # UUIDv7-style: 48-bit timestamp prefix + version + random
57
+ return f"{ts_ms:012x}-7{rand[1:4]}-{rand[4:8]}-{rand[8:12]}-{rand[12:24]}"
58
+
59
+
60
+ def generate_cid() -> str:
61
+ """Generate a conversation ID."""
62
+ return uuid.uuid4().hex[:16]
63
+
64
+
65
+ def generate_tid() -> str:
66
+ """Generate a trace ID."""
67
+ return "t-" + uuid.uuid4().hex[:12]
68
+
69
+
70
+ def sha256_of_file(path: str) -> Optional[str]:
71
+ """Compute SHA-256 of a file's contents. Returns None if file not found."""
72
+ try:
73
+ with open(path, "rb") as f:
74
+ return hashlib.sha256(f.read()).hexdigest()
75
+ except (FileNotFoundError, PermissionError):
76
+ return None
77
+
78
+
79
+ # ---------------------------------------------------------------------------
80
+ # CLowlMessage
81
+ # ---------------------------------------------------------------------------
82
+
83
+ class CLowlMessage:
84
+ """Represents a single CLowl v0.2 message.
85
+
86
+ Required fields: clowl, mid, ts, p, from_, to, cid, body
87
+ Optional fields: tid, pid, ctx, auth, det
88
+ """
89
+
90
+ def __init__(
91
+ self,
92
+ p: str,
93
+ from_: str,
94
+ to: Union[str, List[str]],
95
+ cid: str,
96
+ body_t: str,
97
+ body_d: Optional[dict] = None,
98
+ *,
99
+ mid: Optional[str] = None,
100
+ ts: Optional[int] = None,
101
+ tid: Optional[str] = None,
102
+ pid: Optional[str] = None,
103
+ ctx: Optional[dict] = None,
104
+ auth: Optional[str] = None,
105
+ det: bool = False,
106
+ ):
107
+ self.clowl = CLOWL_VERSION
108
+ self.mid = mid or generate_mid()
109
+ self.ts = ts or int(time.time())
110
+ self.tid = tid
111
+ self.pid = pid
112
+ self.p = p
113
+ self.from_ = from_
114
+ self.to = to
115
+ self.cid = cid
116
+ self.body = {"t": body_t, "d": body_d or {}}
117
+ self.ctx = ctx
118
+ self.auth = auth
119
+ self.det = det
120
+
121
+ # ------------------------------------------------------------------
122
+ # Serialization
123
+ # ------------------------------------------------------------------
124
+
125
+ def to_dict(self) -> dict:
126
+ """Convert to a Python dict (suitable for JSON serialization)."""
127
+ d: dict = {
128
+ "clowl": self.clowl,
129
+ "mid": self.mid,
130
+ "ts": self.ts,
131
+ "p": self.p,
132
+ "from": self.from_,
133
+ "to": self.to,
134
+ "cid": self.cid,
135
+ "body": self.body,
136
+ }
137
+ # Optional fields — only include if set
138
+ if self.tid is not None: d["tid"] = self.tid
139
+ if self.pid is not None: d["pid"] = self.pid
140
+ if self.ctx is not None: d["ctx"] = self.ctx
141
+ if self.auth is not None: d["auth"] = self.auth
142
+ if self.det: d["det"] = self.det
143
+ return d
144
+
145
+ def to_json(self, indent: int = 2) -> str:
146
+ """Serialize to JSON string."""
147
+ return json.dumps(self.to_dict(), indent=indent)
148
+
149
+ @classmethod
150
+ def from_dict(cls, data: dict) -> "CLowlMessage":
151
+ """Construct a CLowlMessage from a dict (e.g., parsed JSON)."""
152
+ body = data.get("body", {})
153
+ body_t = body.get("t", "")
154
+ body_d = body.get("d", {})
155
+ return cls(
156
+ p = data["p"],
157
+ from_ = data["from"],
158
+ to = data["to"],
159
+ cid = data["cid"],
160
+ body_t = body_t,
161
+ body_d = body_d,
162
+ mid = data.get("mid"),
163
+ ts = data.get("ts"),
164
+ tid = data.get("tid"),
165
+ pid = data.get("pid"),
166
+ ctx = data.get("ctx"),
167
+ auth = data.get("auth"),
168
+ det = data.get("det", False),
169
+ )
170
+
171
+ @classmethod
172
+ def from_json(cls, json_str: str) -> "CLowlMessage":
173
+ """Construct a CLowlMessage from a JSON string."""
174
+ return cls.from_dict(json.loads(json_str))
175
+
176
+ # ------------------------------------------------------------------
177
+ # Validation
178
+ # ------------------------------------------------------------------
179
+
180
+ def validate(self) -> list:
181
+ """Validate the message. Returns a list of error strings (empty = valid)."""
182
+ errors = []
183
+
184
+ if self.clowl != CLOWL_VERSION:
185
+ errors.append(f"Invalid clowl version: '{self.clowl}' (expected '{CLOWL_VERSION}')")
186
+
187
+ if not self.mid:
188
+ errors.append("Missing required field: mid")
189
+
190
+ if not isinstance(self.ts, int) or self.ts < 0:
191
+ errors.append(f"Invalid ts: {self.ts!r} (must be non-negative integer)")
192
+
193
+ if self.p not in VALID_PERFORMATIVES:
194
+ errors.append(f"Invalid performative: '{self.p}' (must be one of {sorted(VALID_PERFORMATIVES)})")
195
+
196
+ if not self.from_:
197
+ errors.append("Missing required field: from")
198
+
199
+ if not self.to:
200
+ errors.append("Missing required field: to")
201
+
202
+ if not self.cid:
203
+ errors.append("Missing required field: cid")
204
+
205
+ if not isinstance(self.body, dict):
206
+ errors.append("body must be an object")
207
+ else:
208
+ if not self.body.get("t"):
209
+ errors.append("body.t is required")
210
+ if not isinstance(self.body.get("d"), dict):
211
+ errors.append("body.d must be an object")
212
+
213
+ # DLGT requires delegation_mode
214
+ if self.p == "DLGT":
215
+ d = self.body.get("d", {})
216
+ mode = d.get("delegation_mode")
217
+ if mode not in VALID_DELEGATION_MODES:
218
+ errors.append(
219
+ f"DLGT messages require body.d.delegation_mode to be one of "
220
+ f"{sorted(VALID_DELEGATION_MODES)}, got {mode!r}"
221
+ )
222
+
223
+ # ctx validation
224
+ if self.ctx is not None:
225
+ if not isinstance(self.ctx, dict):
226
+ errors.append("ctx must be an object with ref/inline/hash fields")
227
+ else:
228
+ inline = self.ctx.get("inline")
229
+ if inline and len(inline) > 2000:
230
+ errors.append(f"ctx.inline exceeds 2000 character limit (got {len(inline)})")
231
+ h = self.ctx.get("hash")
232
+ if h and not (isinstance(h, str) and len(h) == 64):
233
+ errors.append(f"ctx.hash must be a 64-char SHA-256 hex string")
234
+
235
+ return errors
236
+
237
+ def is_valid(self) -> bool:
238
+ """Return True if the message passes validation."""
239
+ return len(self.validate()) == 0
240
+
241
+ # ------------------------------------------------------------------
242
+ # Human translation
243
+ # ------------------------------------------------------------------
244
+
245
+ def to_human(self) -> str:
246
+ """Return a human-readable English description of this message."""
247
+ ts_str = datetime.fromtimestamp(self.ts, tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
248
+ perf = PERFORMATIVE_NAMES.get(self.p, self.p)
249
+ to_str = "[" + ", ".join(self.to) + "]" if isinstance(self.to, list) else self.to
250
+
251
+ body_d = self.body.get("d", {})
252
+ body_t = self.body.get("t", "")
253
+ tid_tag = f"[{self.tid}] " if self.tid else ""
254
+ pid_tag = f" (re: {self.pid[:8]}…)" if self.pid else ""
255
+ det_tag = " [deterministic]" if self.det else ""
256
+ auth_tag = " [authenticated]" if self.auth else ""
257
+
258
+ # Body summary
259
+ if self.p == "ERR":
260
+ code = body_d.get("code", "?")
261
+ msg = body_d.get("msg", "unknown error")
262
+ retry = " — retryable" if body_d.get("retry") else ""
263
+ detail = f"[{code}] {msg}{retry}"
264
+ elif self.p == "CAPS":
265
+ supports = body_d.get("supports", [])
266
+ ver = body_d.get("clowl", "?")
267
+ detail = f"CLowl v{ver} | supports: {', '.join(supports)}"
268
+ elif self.p == "DLGT":
269
+ mode = body_d.get("delegation_mode", "transfer")
270
+ rest = {k: v for k, v in body_d.items() if k != "delegation_mode"}
271
+ detail = f"{body_t} [{mode}]"
272
+ if rest:
273
+ detail += f" — {json.dumps(rest)}"
274
+ elif self.p == "PROG":
275
+ pct = body_d.get("pct")
276
+ note = body_d.get("note", "")
277
+ detail = f"{body_t} — {pct}% {note}".strip(" —") if pct is not None else f"{body_t} — {note}"
278
+ elif self.p == "ACK":
279
+ eta = body_d.get("eta")
280
+ detail = f"Acknowledged {body_t}" + (f" — ETA {eta}" if eta else "")
281
+ elif body_d:
282
+ detail = f"{body_t} — {json.dumps(body_d)}"
283
+ else:
284
+ detail = body_t
285
+
286
+ ctx_str = ""
287
+ if self.ctx:
288
+ ref = self.ctx.get("ref")
289
+ h = self.ctx.get("hash")
290
+ if ref:
291
+ ctx_str = f" | ctx: {ref}"
292
+ if h:
293
+ ctx_str += f" (sha256: {h[:12]}…)"
294
+
295
+ return (
296
+ f"[{ts_str}] {tid_tag}"
297
+ f"{self.from_} → {to_str}: {perf} {detail}"
298
+ f"{ctx_str}{det_tag}{pid_tag}{auth_tag}"
299
+ )
300
+
301
+ def __repr__(self) -> str:
302
+ return f"CLowlMessage(p={self.p!r}, from={self.from_!r}, to={self.to!r}, t={self.body.get('t')!r})"
303
+
304
+
305
+ # ---------------------------------------------------------------------------
306
+ # Factory methods
307
+ # ---------------------------------------------------------------------------
308
+
309
+ def create_req(
310
+ from_: str, to: Union[str, List[str]], cid: str, task: str,
311
+ data: Optional[dict] = None, **kwargs
312
+ ) -> CLowlMessage:
313
+ """Create a REQ (Request) message."""
314
+ return CLowlMessage("REQ", from_, to, cid, task, data or {}, **kwargs)
315
+
316
+
317
+ def create_ack(
318
+ from_: str, to: str, cid: str, task: str,
319
+ data: Optional[dict] = None, **kwargs
320
+ ) -> CLowlMessage:
321
+ """Create an ACK (Acknowledge) message."""
322
+ return CLowlMessage("ACK", from_, to, cid, task, data or {}, **kwargs)
323
+
324
+
325
+ def create_done(
326
+ from_: str, to: str, cid: str, task: str,
327
+ data: Optional[dict] = None, **kwargs
328
+ ) -> CLowlMessage:
329
+ """Create a DONE (Complete) message."""
330
+ return CLowlMessage("DONE", from_, to, cid, task, data or {}, **kwargs)
331
+
332
+
333
+ def create_err(
334
+ from_: str, to: str, cid: str,
335
+ code: str, msg: str, retry: bool = False, **kwargs
336
+ ) -> CLowlMessage:
337
+ """Create an ERR (Error) message."""
338
+ return CLowlMessage("ERR", from_, to, cid, "error", {
339
+ "code": code, "msg": msg, "retry": retry
340
+ }, **kwargs)
341
+
342
+
343
+ def create_dlgt(
344
+ from_: str, to: str, cid: str, task: str,
345
+ delegation_mode: str = "transfer",
346
+ data: Optional[dict] = None, **kwargs
347
+ ) -> CLowlMessage:
348
+ """Create a DLGT (Delegate) message.
349
+
350
+ delegation_mode: 'transfer' | 'fork' | 'assist'
351
+ """
352
+ if delegation_mode not in VALID_DELEGATION_MODES:
353
+ raise ValueError(f"delegation_mode must be one of {sorted(VALID_DELEGATION_MODES)}")
354
+ d = {"delegation_mode": delegation_mode}
355
+ if data:
356
+ d.update(data)
357
+ return CLowlMessage("DLGT", from_, to, cid, task, d, **kwargs)
358
+
359
+
360
+ def create_prog(
361
+ from_: str, to: str, cid: str, task: str,
362
+ pct: Optional[int] = None, note: str = "", **kwargs
363
+ ) -> CLowlMessage:
364
+ """Create a PROG (Progress) message."""
365
+ data: dict = {}
366
+ if pct is not None:
367
+ data["pct"] = pct
368
+ if note:
369
+ data["note"] = note
370
+ return CLowlMessage("PROG", from_, to, cid, task, data, **kwargs)
371
+
372
+
373
+ def create_caps(
374
+ from_: str, supports: List[str], **kwargs
375
+ ) -> CLowlMessage:
376
+ """Create a CAPS (Capabilities) message. Broadcasts to '*'."""
377
+ return CLowlMessage("CAPS", from_, "*", "system", "capabilities", {
378
+ "supports": supports,
379
+ "clowl": CLOWL_VERSION,
380
+ }, **kwargs)
381
+
382
+
383
+ def create_cncl(
384
+ from_: str, to: str, cid: str, task: str,
385
+ reason: str = "", **kwargs
386
+ ) -> CLowlMessage:
387
+ """Create a CNCL (Cancel) message."""
388
+ data = {"reason": reason} if reason else {}
389
+ return CLowlMessage("CNCL", from_, to, cid, task, data, **kwargs)
390
+
391
+
392
+ # ---------------------------------------------------------------------------
393
+ # __main__ — example usage
394
+ # ---------------------------------------------------------------------------
395
+
396
+ if __name__ == "__main__":
397
+ print("=== CLowl v0.2 Reference Library — Example Usage ===\n")
398
+
399
+ # Shared conversation / trace IDs
400
+ cid = generate_cid()
401
+ tid = generate_tid()
402
+ print(f"Conversation: {cid}")
403
+ print(f"Trace: {tid}\n")
404
+
405
+ # 1. Radar advertises capabilities
406
+ caps = create_caps("radar", ["search:web", "search:repo", "analyze:trend"])
407
+ print("1. CAPS broadcast:")
408
+ print(caps.to_human())
409
+ print()
410
+
411
+ # 2. Oscar requests a search
412
+ req = create_req(
413
+ "oscar", "radar", cid, "search",
414
+ {"q": "MCP vs A2A comparison", "scope": "web"},
415
+ tid=tid,
416
+ )
417
+ errors = req.validate()
418
+ assert not errors, f"Validation failed: {errors}"
419
+ print("2. REQ (with validation):")
420
+ print(req.to_human())
421
+ print()
422
+
423
+ # 3. Radar acknowledges
424
+ ack = create_ack("radar", "oscar", cid, "search", {"eta": "60s"},
425
+ tid=tid, pid=req.mid)
426
+ print("3. ACK:")
427
+ print(ack.to_human())
428
+ print()
429
+
430
+ # 4. Radar sends progress
431
+ prog = create_prog("radar", "oscar", cid, "search", pct=50,
432
+ note="Indexed 4 of 8 sources",
433
+ tid=tid, pid=req.mid)
434
+ print("4. PROG:")
435
+ print(prog.to_human())
436
+ print()
437
+
438
+ # 5. Radar completes
439
+ done = create_done("radar", "oscar", cid, "search",
440
+ {"result_path": "research/mcp-vs-a2a.md", "sources": 8},
441
+ tid=tid, pid=req.mid, det=True)
442
+ print("5. DONE (deterministic):")
443
+ print(done.to_human())
444
+ print()
445
+
446
+ # 6. Oscar delegates to Muse (transfer mode)
447
+ dlgt = create_dlgt(
448
+ "oscar", "muse", cid, "analyze",
449
+ delegation_mode="transfer",
450
+ data={"target": "content angle", "type": "competitive"},
451
+ tid=tid, pid=done.mid,
452
+ ctx={"ref": "research/mcp-vs-a2a.md", "inline": None, "hash": None},
453
+ )
454
+ print("6. DLGT (transfer):")
455
+ print(dlgt.to_human())
456
+ print()
457
+
458
+ # 7. Error example
459
+ err = create_err("muse", "oscar", cid, "E003",
460
+ "Context file not found: research/mcp-vs-a2a.md",
461
+ retry=True, tid=tid)
462
+ print("7. ERR (retryable):")
463
+ print(err.to_human())
464
+ print()
465
+
466
+ # 8. Broadcast INF to multiple agents
467
+ broadcast = CLowlMessage(
468
+ "INF", "oscar", ["radar", "muse", "ink"], cid, "notify",
469
+ {"message": "Pipeline starting for MCP vs A2A analysis"},
470
+ tid=tid,
471
+ )
472
+ print("8. INF broadcast to [radar, muse, ink]:")
473
+ print(broadcast.to_human())
474
+ print()
475
+
476
+ # 9. Cancel
477
+ cncl = create_cncl("oscar", "radar", cid, "search",
478
+ reason="Scope changed, no longer needed", tid=tid)
479
+ print("9. CNCL:")
480
+ print(cncl.to_human())
481
+ print()
482
+
483
+ # 10. Round-trip: to_dict → from_dict
484
+ d = req.to_dict()
485
+ req2 = CLowlMessage.from_dict(d)
486
+ assert req2.mid == req.mid
487
+ assert req2.body == req.body
488
+ print("10. Round-trip (to_dict → from_dict): ✓")
489
+ print()
490
+
491
+ # 11. Show full JSON for the REQ
492
+ print("11. Full JSON for REQ message:")
493
+ print(req.to_json())
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.4
2
+ Name: clowl
3
+ Version: 0.2.0
4
+ Summary: CLowl - A structured communication language for AI agent-to-agent messaging
5
+ Author-email: Oscar Sterling Agency <oscar@oscarsterling.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://clowl.dev
8
+ Project-URL: Repository, https://github.com/oscarsterling/clowl
9
+ Project-URL: Documentation, https://clowl.dev
10
+ Keywords: clowl,agent,ai,protocol,communication,multi-agent
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+
25
+ # CLowl
26
+
27
+ **A language for AI agents that humans can read.**
28
+
29
+ CLowl (Claw + Talk) is a structured communication protocol for AI agent-to-agent messaging. It defines a minimal JSON schema with typed performatives, message metadata, and context referencing that runs on top of any transport (MCP, A2A, HTTP, WebSocket, stdio, etc.). Every CLowl message has a deterministic English translation, so enterprises can audit any agent conversation and developers can debug multi-agent pipelines in seconds.
30
+
31
+ ---
32
+
33
+ ## Install
34
+
35
+ **TypeScript / Node.js:**
36
+ ```bash
37
+ npm install clowl-protocol
38
+ ```
39
+
40
+ **Python:**
41
+ ```bash
42
+ pip install clowl
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Quick Start
48
+
49
+ **TypeScript:**
50
+ ```typescript
51
+ import { createReq, generateCid, generateTid } from "clowl";
52
+
53
+ const cid = generateCid();
54
+ const req = createReq("oscar", "radar", cid, "search", { q: "MCP vs A2A" }, { tid: generateTid() });
55
+ console.log(req.toHuman());
56
+ ```
57
+
58
+ **Python:**
59
+ ```python
60
+ from clowl import create_req, generate_cid, generate_tid
61
+
62
+ cid = generate_cid()
63
+ req = create_req("oscar", "radar", cid, "search", {"q": "MCP vs A2A"}, tid=generate_tid())
64
+ print(req.to_human())
65
+ ```
66
+
67
+ ---
68
+
69
+ ## The Problem
70
+
71
+ When AI agents talk to each other, nobody knows what they're saying.
72
+
73
+ Agent A sends a blob of unstructured text to Agent B. Agent B replies with another blob. Somewhere in that chain, something goes wrong, and you have no idea what was requested, what was delegated, or where the task died. Multi-agent systems are powerful and opaque in equal measure.
74
+
75
+ ---
76
+
77
+ ## The Solution
78
+
79
+ CLowl is a language agents speak and humans can read. It defines:
80
+
81
+ - **Structured messages** with typed intent (requests, delegates, errors, progress updates)
82
+ - **Message metadata** for tracing, dedup, and conversation reconstruction
83
+ - **A live translator** that converts CLowl JSON to plain English instantly
84
+
85
+ ---
86
+
87
+ ## Message Format
88
+
89
+ Every CLowl message is a JSON object with 8 required fields:
90
+
91
+ ```json
92
+ {
93
+ "clowl": "0.2",
94
+ "mid": "01914b2c-7abc-...",
95
+ "ts": 1709078400,
96
+ "p": "REQ",
97
+ "from": "oscar",
98
+ "to": "radar",
99
+ "cid": "conv-001",
100
+ "body": {
101
+ "t": "search",
102
+ "d": { "q": "MCP vs A2A", "scope": "web" }
103
+ }
104
+ }
105
+ ```
106
+
107
+ Optional fields: `tid` (trace ID), `pid` (parent message), `ctx` (context reference), `auth` (auth token), `det` (determinism flag).
108
+
109
+ See the [full spec](spec-v0.2.md) for complete field definitions.
110
+
111
+ ---
112
+
113
+ ## Performatives
114
+
115
+ | Code | Name | Meaning |
116
+ |------|------|---------|
117
+ | `REQ` | Request | "Do this thing." Initiates a task. |
118
+ | `INF` | Inform | "Here is information." No action expected. |
119
+ | `ACK` | Acknowledge | "Got it, proceeding." |
120
+ | `ERR` | Error | "Failed. Here's why." Structured error code + message. |
121
+ | `DLGT` | Delegate | "Passing to someone better suited." Requires `delegation_mode`: `transfer`, `fork`, or `assist`. |
122
+ | `DONE` | Complete | "Finished. Here's the result." |
123
+ | `CNCL` | Cancel | "Abort this task." |
124
+ | `QRY` | Query | "What's the status?" or "Give me info without acting." |
125
+ | `PROG` | Progress | "Here's an update on the running task." |
126
+ | `CAPS` | Capabilities | "Here's what I can do." Broadcast on connection. |
127
+
128
+ ---
129
+
130
+ ## The Translator
131
+
132
+ Paste CLowl JSON, get English. Paste English, get CLowl JSON. No API needed.
133
+
134
+ ```bash
135
+ $ python translator.py '{"clowl":"0.2","mid":"m001","ts":1709078400,"tid":"t001","p":"REQ","from":"oscar","to":"radar","cid":"c001","body":{"t":"search","d":{"q":"CLowl competitors","scope":"web"}}}'
136
+
137
+ [2026-02-27 12:00:00 UTC] [t001] [m001...] oscar > radar: REQUEST search
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Integration Options
143
+
144
+ **Option 1: Inject the system prompt**
145
+
146
+ Add [`system-prompt-v0.2.md`](system-prompt-v0.2.md) to your agent's system prompt. Any LLM will start generating CLowl messages immediately.
147
+
148
+ **Option 2: Add the JSON schema to your tool calls**
149
+
150
+ Use [`clowl-schema.json`](clowl-schema.json) as a function-calling tool definition. Works with OpenAI, Anthropic, Google, and any local model that supports structured output.
151
+
152
+ **Option 3: Use the libraries**
153
+
154
+ TypeScript and Python libraries provide message creation, validation, state tracking, and translation with zero external dependencies.
155
+
156
+ ---
157
+
158
+ ## Examples
159
+
160
+ See [`examples-v0.2.md`](examples-v0.2.md) for full examples with CLowl JSON and English translations.
161
+
162
+ ---
163
+
164
+ ## Roadmap
165
+
166
+ **v0.3 (planned)**
167
+ - Three-layer architecture (Semantic / Coordination / Transport)
168
+ - Streaming progress (chunked PROG messages)
169
+ - Cryptographic message signing
170
+ - Go SDK
171
+
172
+ **v1.0 (stable)**
173
+ - Breaking changes locked out
174
+ - Binary encoding option (MessagePack)
175
+ - Production SDK with retry, dedup, and dead-letter queue support
176
+
177
+ ---
178
+
179
+ ## Contributing
180
+
181
+ CLowl is an open spec. Contributions welcome:
182
+
183
+ 1. Fork the repo
184
+ 2. Read [`spec-v0.2.md`](spec-v0.2.md) for the source of truth
185
+ 3. Open an issue for design questions before building
186
+ 4. PRs should include spec changes + updated examples
187
+
188
+ ---
189
+
190
+ ## License
191
+
192
+ MIT
193
+
194
+ ---
195
+
196
+ Built by [Oscar Sterling Agency](https://clowl.dev) | [clowl.dev](https://clowl.dev)
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ clowl/__init__.py
4
+ clowl/clowl.py
5
+ clowl.egg-info/PKG-INFO
6
+ clowl.egg-info/SOURCES.txt
7
+ clowl.egg-info/dependency_links.txt
8
+ clowl.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ clowl
2
+ clowl-ts
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "clowl"
7
+ version = "0.2.0"
8
+ description = "CLowl - A structured communication language for AI agent-to-agent messaging"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.8"
12
+ authors = [
13
+ {name = "Oscar Sterling Agency", email = "oscar@oscarsterling.com"}
14
+ ]
15
+ keywords = ["clowl", "agent", "ai", "protocol", "communication", "multi-agent"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Software Development :: Libraries :: Python Modules",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://clowl.dev"
32
+ Repository = "https://github.com/oscarsterling/clowl"
33
+ Documentation = "https://clowl.dev"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["."]
37
+ include = ["clowl*"]
clowl-0.2.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+