claws-code 0.8.0

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.
Files changed (180) hide show
  1. package/.claude/commands/claws-auto.md +90 -0
  2. package/.claude/commands/claws-bin.md +28 -0
  3. package/.claude/commands/claws-cleanup.md +28 -0
  4. package/.claude/commands/claws-do.md +82 -0
  5. package/.claude/commands/claws-fix.md +40 -0
  6. package/.claude/commands/claws-goal.md +111 -0
  7. package/.claude/commands/claws-help.md +54 -0
  8. package/.claude/commands/claws-plan.md +103 -0
  9. package/.claude/commands/claws-report.md +29 -0
  10. package/.claude/commands/claws-status.md +37 -0
  11. package/.claude/commands/claws-update.md +32 -0
  12. package/.claude/commands/claws.md +64 -0
  13. package/.claude/rules/claws-default-behavior.md +76 -0
  14. package/.claude/settings.json +112 -0
  15. package/.claude/settings.local.json +19 -0
  16. package/.claude/skills/claws-auto-engine/SKILL.md +97 -0
  17. package/.claude/skills/claws-goal-tracker/SKILL.md +106 -0
  18. package/.claude/skills/claws-prompt-templates/SKILL.md +203 -0
  19. package/.claude/skills/claws-wave-lead/SKILL.md +126 -0
  20. package/.claude/skills/claws-wave-subworker/SKILL.md +60 -0
  21. package/CHANGELOG.md +1949 -0
  22. package/LICENSE +21 -0
  23. package/README.md +420 -0
  24. package/bin/cli.js +84 -0
  25. package/cli.js +223 -0
  26. package/docs/ARCHITECTURE.md +511 -0
  27. package/docs/event-protocol.md +588 -0
  28. package/docs/features.md +562 -0
  29. package/docs/guide.md +891 -0
  30. package/docs/index.html +716 -0
  31. package/docs/protocol.md +323 -0
  32. package/extension/.vscodeignore +15 -0
  33. package/extension/CHANGELOG.md +1906 -0
  34. package/extension/LICENSE +21 -0
  35. package/extension/README.md +137 -0
  36. package/extension/docs/features.md +424 -0
  37. package/extension/docs/protocol.md +197 -0
  38. package/extension/esbuild.mjs +25 -0
  39. package/extension/icon.png +0 -0
  40. package/extension/native/.metadata.json +10 -0
  41. package/extension/native/node-pty/LICENSE +69 -0
  42. package/extension/native/node-pty/README.md +165 -0
  43. package/extension/native/node-pty/lib/conpty_console_list_agent.js +16 -0
  44. package/extension/native/node-pty/lib/conpty_console_list_agent.js.map +1 -0
  45. package/extension/native/node-pty/lib/eventEmitter2.js +47 -0
  46. package/extension/native/node-pty/lib/eventEmitter2.js.map +1 -0
  47. package/extension/native/node-pty/lib/index.js +52 -0
  48. package/extension/native/node-pty/lib/index.js.map +1 -0
  49. package/extension/native/node-pty/lib/interfaces.js +7 -0
  50. package/extension/native/node-pty/lib/interfaces.js.map +1 -0
  51. package/extension/native/node-pty/lib/shared/conout.js +11 -0
  52. package/extension/native/node-pty/lib/shared/conout.js.map +1 -0
  53. package/extension/native/node-pty/lib/terminal.js +190 -0
  54. package/extension/native/node-pty/lib/terminal.js.map +1 -0
  55. package/extension/native/node-pty/lib/types.js +7 -0
  56. package/extension/native/node-pty/lib/types.js.map +1 -0
  57. package/extension/native/node-pty/lib/unixTerminal.js +346 -0
  58. package/extension/native/node-pty/lib/unixTerminal.js.map +1 -0
  59. package/extension/native/node-pty/lib/utils.js +39 -0
  60. package/extension/native/node-pty/lib/utils.js.map +1 -0
  61. package/extension/native/node-pty/lib/windowsConoutConnection.js +125 -0
  62. package/extension/native/node-pty/lib/windowsConoutConnection.js.map +1 -0
  63. package/extension/native/node-pty/lib/windowsPtyAgent.js +320 -0
  64. package/extension/native/node-pty/lib/windowsPtyAgent.js.map +1 -0
  65. package/extension/native/node-pty/lib/windowsTerminal.js +199 -0
  66. package/extension/native/node-pty/lib/windowsTerminal.js.map +1 -0
  67. package/extension/native/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  68. package/extension/native/node-pty/lib/worker/conoutSocketWorker.js.map +1 -0
  69. package/extension/native/node-pty/package.json +64 -0
  70. package/extension/native/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  71. package/extension/native/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  72. package/extension/native/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  73. package/extension/native/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  74. package/extension/native/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  75. package/extension/native/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  76. package/extension/native/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  77. package/extension/native/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  78. package/extension/native/node-pty/prebuilds/win32-arm64/pty.node +0 -0
  79. package/extension/native/node-pty/prebuilds/win32-arm64/winpty-agent.exe +0 -0
  80. package/extension/native/node-pty/prebuilds/win32-arm64/winpty.dll +0 -0
  81. package/extension/native/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  82. package/extension/native/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  83. package/extension/native/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  84. package/extension/native/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  85. package/extension/native/node-pty/prebuilds/win32-x64/pty.node +0 -0
  86. package/extension/native/node-pty/prebuilds/win32-x64/winpty-agent.exe +0 -0
  87. package/extension/native/node-pty/prebuilds/win32-x64/winpty.dll +0 -0
  88. package/extension/package-lock.json +605 -0
  89. package/extension/package.json +343 -0
  90. package/extension/scripts/bundle-native.mjs +104 -0
  91. package/extension/scripts/deploy-dev.mjs +60 -0
  92. package/extension/src/ansi-strip.ts +52 -0
  93. package/extension/src/backends/vscode/claws-pty.ts +483 -0
  94. package/extension/src/backends/vscode/status-bar.ts +99 -0
  95. package/extension/src/backends/vscode/vscode-backend.ts +282 -0
  96. package/extension/src/capture-store.ts +125 -0
  97. package/extension/src/event-log.ts +629 -0
  98. package/extension/src/event-schemas.ts +478 -0
  99. package/extension/src/extension.js +492 -0
  100. package/extension/src/extension.ts +873 -0
  101. package/extension/src/lifecycle-engine.ts +60 -0
  102. package/extension/src/lifecycle-rules.ts +171 -0
  103. package/extension/src/lifecycle-store.ts +506 -0
  104. package/extension/src/peer-registry.ts +176 -0
  105. package/extension/src/pipeline-registry.ts +82 -0
  106. package/extension/src/platform.ts +64 -0
  107. package/extension/src/protocol.ts +532 -0
  108. package/extension/src/server-config.ts +98 -0
  109. package/extension/src/server.ts +2210 -0
  110. package/extension/src/task-registry.ts +51 -0
  111. package/extension/src/terminal-backend.ts +211 -0
  112. package/extension/src/terminal-manager.ts +395 -0
  113. package/extension/src/topic-registry.ts +70 -0
  114. package/extension/src/topic-utils.ts +46 -0
  115. package/extension/src/transport.ts +45 -0
  116. package/extension/src/uninstall-cleanup.ts +232 -0
  117. package/extension/src/wave-registry.ts +314 -0
  118. package/extension/src/websocket-transport.ts +153 -0
  119. package/extension/tsconfig.json +23 -0
  120. package/lib/capabilities.js +145 -0
  121. package/lib/dry-run.js +43 -0
  122. package/lib/install.js +1018 -0
  123. package/lib/mcp-setup.js +92 -0
  124. package/lib/platform.js +240 -0
  125. package/lib/preflight.js +152 -0
  126. package/lib/shell-hook.js +343 -0
  127. package/lib/uninstall.js +162 -0
  128. package/lib/verify.js +166 -0
  129. package/mcp_server.js +3529 -0
  130. package/package.json +48 -0
  131. package/rules/claws-default-behavior.md +72 -0
  132. package/scripts/_helpers/atomic-file.mjs +137 -0
  133. package/scripts/_helpers/fix-repair.js +64 -0
  134. package/scripts/_helpers/json-safe.mjs +218 -0
  135. package/scripts/bump-version.sh +84 -0
  136. package/scripts/codegen/gen-docs.mjs +61 -0
  137. package/scripts/codegen/gen-json-schema.mjs +62 -0
  138. package/scripts/codegen/gen-mcp-tools.mjs +358 -0
  139. package/scripts/codegen/gen-types.mjs +172 -0
  140. package/scripts/codegen/index.mjs +42 -0
  141. package/scripts/dev-hooks/check-extension-dirs.js +77 -0
  142. package/scripts/dev-hooks/check-open-claws-terminals.js +70 -0
  143. package/scripts/dev-hooks/check-stale-main.js +55 -0
  144. package/scripts/dev-hooks/check-tag-pushed.js +51 -0
  145. package/scripts/dev-hooks/check-tag-vs-main.js +56 -0
  146. package/scripts/dev-vsix-install.sh +60 -0
  147. package/scripts/fix.sh +702 -0
  148. package/scripts/gen-client-types.mjs +81 -0
  149. package/scripts/git-hooks/pre-commit +31 -0
  150. package/scripts/hooks/lifecycle-state.js +61 -0
  151. package/scripts/hooks/package.json +4 -0
  152. package/scripts/hooks/post-tool-use-claws.js +292 -0
  153. package/scripts/hooks/pre-bash-no-verify-block.js +72 -0
  154. package/scripts/hooks/pre-tool-use-claws.js +206 -0
  155. package/scripts/hooks/session-start-claws.js +97 -0
  156. package/scripts/hooks/stop-claws.js +88 -0
  157. package/scripts/inject-claude-md.js +205 -0
  158. package/scripts/inject-dev-hooks.js +96 -0
  159. package/scripts/inject-global-claude-md.js +140 -0
  160. package/scripts/inject-settings-hooks.js +370 -0
  161. package/scripts/install.ps1 +146 -0
  162. package/scripts/install.sh +1729 -0
  163. package/scripts/monitor-arm-watch.js +155 -0
  164. package/scripts/rebuild-node-pty.sh +245 -0
  165. package/scripts/report.sh +232 -0
  166. package/scripts/shell-hook.fish +164 -0
  167. package/scripts/shell-hook.ps1 +33 -0
  168. package/scripts/shell-hook.sh +232 -0
  169. package/scripts/stream-events.js +399 -0
  170. package/scripts/terminal-wrapper.sh +36 -0
  171. package/scripts/test-enforcement.sh +132 -0
  172. package/scripts/test-install.sh +174 -0
  173. package/scripts/test-installer-parity.sh +135 -0
  174. package/scripts/test-template-enforcement.sh +76 -0
  175. package/scripts/uninstall.sh +143 -0
  176. package/scripts/update.sh +337 -0
  177. package/scripts/verify-release.sh +323 -0
  178. package/scripts/verify-wrapped.sh +194 -0
  179. package/templates/CLAUDE.global.md +135 -0
  180. package/templates/CLAUDE.project.md +37 -0
@@ -0,0 +1,588 @@
1
+ # Claws Event Protocol — Convention Layer
2
+
3
+ This document defines the **observable behavior** every Claws-driven worker must
4
+ exhibit and every orchestrator can rely on. It is a convention, not a wire
5
+ protocol — the wire protocol is `claws/2` pub/sub (see `protocol.md` and
6
+ `extension/src/protocol.ts`). What lives here is the **shape and timing of
7
+ events** workers emit, the **command channel** orchestrators use to drive them,
8
+ and the **state machine** that bounds the dialogue.
9
+
10
+ The goal is real-time, no-polling orchestration. Events arrive at the
11
+ orchestrator within milliseconds of being published. The orchestrator decides
12
+ based on events, not on log scrapes. Workers signal their state explicitly
13
+ instead of being inferred.
14
+
15
+ ---
16
+
17
+ ## 1. Universal Event Envelope
18
+
19
+ Every payload published on any worker- or task-scoped topic SHOULD carry this
20
+ envelope. Consumers can rely on these fields existing.
21
+
22
+ ```jsonc
23
+ {
24
+ "v": 1, // envelope schema version (integer)
25
+ "id": "<uuid-v4>", // unique message id — for dedup, reference
26
+ "correlation_id": "<uuid-v4>?", // groups related events (request/response, decision/outcome)
27
+ "parent_id": "<peer-id>?", // lineage — null for root orchestrator, peer-id of parent worker otherwise
28
+ "from_peer": "<peer-id>", // who published (server adds if absent)
29
+ "from_name": "<peer-name>", // human label (worker-X, orchestrator-main)
30
+ "terminal_id": "<id>?", // associated terminal id, if any
31
+ "ts_published": "2026-04-27T12:00:00.123Z", // worker clock at publish time
32
+ "ts_server": "2026-04-27T12:00:00.124Z", // server clock at fan-out time (server adds)
33
+ "schema": "<topic-schema-name>", // e.g. "worker-phase-v1"
34
+ "data": { /* schema-specific */ }
35
+ }
36
+ ```
37
+
38
+ **Why the envelope:** dedup (`id`), threading (`correlation_id`), tree walks
39
+ (`parent_id`), clock skew detection (`ts_published` vs `ts_server`), and forward
40
+ compatibility (`v`, `schema`).
41
+
42
+ **Server adds** `ts_server`, `from_peer` (if missing), and refuses to fan out
43
+ events whose `from_peer` doesn't match the publishing connection's peer id.
44
+
45
+ ---
46
+
47
+ ## 2. Topic Namespace
48
+
49
+ Topics are dot-separated, lowercase, hierarchical. Patterns support `*` (one
50
+ segment) and `**` (recursive, zero or more).
51
+
52
+ | Namespace | Owner | Purpose |
53
+ |---|---|---|
54
+ | `worker.<peerId>.*` | worker `<peerId>` | facts the worker publishes about itself |
55
+ | `cmd.<peerId>.*` | orchestrator | commands targeted at one specific worker |
56
+ | `cmd.role.<role>` | orchestrator | broadcast to all workers of a role |
57
+ | `task.<taskId>.*` | orchestrator + assigned worker | task lifecycle (orthogonal to worker boot) |
58
+ | `system.*` | server | server-generated (peer.joined, peer.left, gate.fired) |
59
+
60
+ **Authorization (server-enforced — see §10):** workers can only publish on
61
+ `worker.<theirOwnPeerId>.*` and `task.<assignedTaskId>.*`. Only orchestrators
62
+ can publish on `cmd.*`. `system.*` is server-write-only.
63
+
64
+ ---
65
+
66
+ ## 3. Worker-emitted Topics
67
+
68
+ Every worker MUST emit on these topics in the order shown. A worker that skips
69
+ `boot` is invisible to the orchestrator. A worker that skips `complete` looks
70
+ crashed.
71
+
72
+ ### 3.1 `worker.<peerId>.boot`
73
+
74
+ **When:** Claude Code (or whatever process) is up and ready to accept the mission. Emit ONCE per worker lifetime, immediately after `claws_hello`.
75
+
76
+ ```jsonc
77
+ {
78
+ "schema": "worker-boot-v1",
79
+ "data": {
80
+ "model": "claude-sonnet-4-6",
81
+ "role": "worker",
82
+ "parent_peer_id": "p_000001", // orchestrator's peer id
83
+ "mission_summary": "Audit src/server.ts for security issues",
84
+ "capabilities": ["mcp_claws", "sub_workers", "long_thinking"],
85
+ "cwd": "/Users/.../Desktop/Claws",
86
+ "terminal_id": "5"
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### 3.2 `worker.<peerId>.phase`
92
+
93
+ **When:** every transition between the 8 lifecycle phases (or to a terminal
94
+ state). The state machine in §4 bounds legal transitions.
95
+
96
+ ```jsonc
97
+ {
98
+ "schema": "worker-phase-v1",
99
+ "data": {
100
+ "phase": "DEPLOY", // current phase entered
101
+ "prev": "SPAWN", // phase exited
102
+ "transition_reason": "all-workers-spawned",
103
+ "phases_completed": ["PLAN","SPAWN","DEPLOY"],
104
+ "metadata": { /* phase-specific notes */ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ ### 3.3 `worker.<peerId>.event`
110
+
111
+ **When:** any sentinel checkpoint within a phase. The `kind` field switches the
112
+ sub-schema.
113
+
114
+ ```jsonc
115
+ {
116
+ "schema": "worker-event-v1",
117
+ "data": {
118
+ "kind": "BLOCKED|REQUEST|HARVEST|ERROR|DECISION|PROGRESS|LOG",
119
+ "severity": "info|warn|error|fatal",
120
+ "message": "human-readable headline",
121
+ "request_id": "<uuid>?", // present if this expects a response
122
+ /* + kind-specific fields, see §3.3.x */
123
+ }
124
+ }
125
+ ```
126
+
127
+ #### 3.3.1 `kind: "BLOCKED"`
128
+ Worker has paused waiting for something external. Orchestrator should respond
129
+ on `cmd.<peerId>.unblock` with `correlation_id = data.request_id`.
130
+ ```jsonc
131
+ { "kind": "BLOCKED",
132
+ "severity": "warn",
133
+ "message": "needs approval to spawn 3 sub-workers",
134
+ "request_id": "req-abc",
135
+ "data": { "blocking_resource": "approval", "retryable": true } }
136
+ ```
137
+
138
+ #### 3.3.2 `kind: "REQUEST"`
139
+ Worker is asking the orchestrator to make a decision. Orchestrator responds on
140
+ `cmd.<peerId>.<action>` with `correlation_id = data.request_id`.
141
+ ```jsonc
142
+ { "kind": "REQUEST",
143
+ "severity": "info",
144
+ "message": "choose: full rewrite vs. patch?",
145
+ "request_id": "req-xyz",
146
+ "data": { "request_type": "decision",
147
+ "options": [{"id":"A","label":"full rewrite"},{"id":"B","label":"patch"}] } }
148
+ ```
149
+
150
+ #### 3.3.3 `kind: "HARVEST"`
151
+ Worker is delivering intermediate or final artifacts. Orchestrator may collect.
152
+ ```jsonc
153
+ { "kind": "HARVEST",
154
+ "severity": "info",
155
+ "message": "audit findings ready",
156
+ "data": { "artifacts": [{"path":"/tmp/audit.md","type":"markdown","size_bytes":4200}] } }
157
+ ```
158
+
159
+ #### 3.3.4 `kind: "ERROR"`
160
+ Something failed. Severity drives recovery: `warn` is informational, `error`
161
+ needs orchestrator attention, `fatal` means the worker is going down.
162
+ ```jsonc
163
+ { "kind": "ERROR",
164
+ "severity": "fatal",
165
+ "message": "ran out of token budget",
166
+ "data": { "error_class": "UsageLimitExceeded", "retryable": false,
167
+ "suggested_action": "respawn with smaller model" } }
168
+ ```
169
+
170
+ #### 3.3.5 `kind: "DECISION"`
171
+ Worker made a decision autonomously and is logging it (NOT requesting input).
172
+ For audit trails.
173
+ ```jsonc
174
+ { "kind": "DECISION",
175
+ "severity": "info",
176
+ "message": "chose option B (patch) — full rewrite would touch shared files",
177
+ "data": { "decision_id": "dec-1", "option_chosen": "B", "reasoning": "..." } }
178
+ ```
179
+
180
+ #### 3.3.6 `kind: "PROGRESS"`
181
+ Worker reports incremental progress within a long phase. Optional but useful.
182
+ ```jsonc
183
+ { "kind": "PROGRESS",
184
+ "severity": "info",
185
+ "message": "3 of 7 files reviewed",
186
+ "data": { "percent": 0.43, "current_step": 3, "total_steps": 7, "eta_ms": 120000 } }
187
+ ```
188
+
189
+ #### 3.3.7 `kind: "LOG"`
190
+ Catch-all for diagnostic narration. Orchestrator usually ignores; observers may
191
+ collect.
192
+
193
+ ### 3.4 `worker.<peerId>.heartbeat`
194
+
195
+ **When:** every 10s while the worker is alive. Frequency may be tuned but MUST
196
+ be at least every 30s — workers silent for 30s+ are considered stale.
197
+
198
+ ```jsonc
199
+ {
200
+ "schema": "worker-heartbeat-v1",
201
+ "data": {
202
+ "current_phase": "OBSERVE",
203
+ "time_in_phase_ms": 47000,
204
+ "tokens_used": 5421,
205
+ "cost_usd": 0.78,
206
+ "last_event_id": "<uuid of most recent event>",
207
+ "active_sub_workers": ["p_000004"]
208
+ }
209
+ }
210
+ ```
211
+
212
+ If the worker knows it's about to crash (e.g. SIGINT received), it SHOULD
213
+ publish a final heartbeat with `severity: "fatal"` and a parting message
214
+ before disconnecting.
215
+
216
+ ### 3.5 `worker.<peerId>.complete`
217
+
218
+ **When:** worker has finished its mission successfully. Emit ONCE, immediately
219
+ before calling `claws_close` or disconnecting. Mutually exclusive with a
220
+ `fatal` ERROR.
221
+
222
+ ```jsonc
223
+ {
224
+ "schema": "worker-complete-v1",
225
+ "data": {
226
+ "result": "ok",
227
+ "summary": "audited 7 files, found 2 issues — see /tmp/audit.md",
228
+ "artifacts": [{"path":"/tmp/audit.md","type":"markdown"}],
229
+ "phases_completed": ["PLAN","SPAWN","DEPLOY","OBSERVE","HARVEST","CLEANUP","REFLECT"],
230
+ "total_tokens": 18420,
231
+ "total_cost_usd": 2.41,
232
+ "duration_ms": 412000
233
+ }
234
+ }
235
+ ```
236
+
237
+ ---
238
+
239
+ ## 4. Phase State Machine
240
+
241
+ Workers SHOULD transition through phases in this order. Illegal transitions
242
+ trigger a `system.gate.fired` event from the server (see §6) and may be
243
+ rejected outright.
244
+
245
+ ```
246
+ ┌──────────┐
247
+ │ PLAN │ ← only entry point; required
248
+ └────┬─────┘
249
+
250
+ ┌──────────┐
251
+ ┌─►│ SPAWN │
252
+ │ └────┬─────┘
253
+ │ ▼
254
+ │ ┌──────────┐ ┌──────────┐
255
+ │ │ DEPLOY │◄──►│ RECOVER │
256
+ │ └────┬─────┘ └────┬─────┘
257
+ │ ▼ │
258
+ │ ┌──────────┐ │
259
+ └──┤ OBSERVE │◄────────┘
260
+ └────┬─────┘
261
+
262
+ ┌──────────┐
263
+ │ HARVEST │
264
+ └────┬─────┘
265
+
266
+ ┌──────────┐
267
+ │ CLEANUP │
268
+ └────┬─────┘
269
+
270
+ ┌──────────┐
271
+ │ REFLECT │ ← terminal
272
+ └──────────┘
273
+
274
+ +- FAILED (terminal) — reachable from any non-terminal state on fatal error
275
+ ```
276
+
277
+ Allowed transitions (other → other):
278
+ - `PLAN → SPAWN`
279
+ - `SPAWN → DEPLOY | RECOVER | FAILED`
280
+ - `DEPLOY → OBSERVE | RECOVER | FAILED`
281
+ - `OBSERVE → HARVEST | RECOVER | FAILED`
282
+ - `RECOVER → DEPLOY | OBSERVE | FAILED`
283
+ - `HARVEST → CLEANUP | FAILED`
284
+ - `CLEANUP → REFLECT | FAILED`
285
+ - `REFLECT → (terminal, no further transitions)`
286
+ - `FAILED → (terminal)`
287
+
288
+ `RECOVER` may be entered from DEPLOY, OBSERVE, or SPAWN and may exit back to
289
+ the same phase (re-deploy after a recovery). Workers should publish RECOVER
290
+ entry/exit pairs so observers can compute MTTR.
291
+
292
+ ---
293
+
294
+ ## 5. Command Channel — Orchestrator → Worker
295
+
296
+ Workers do NOT subscribe to anything by default (they would need a long-poll
297
+ or a sidecar to receive pushes via MCP). Instead, the orchestrator delivers
298
+ commands via two routes:
299
+
300
+ ### 5.1 Bracketed-paste injection (preferred default)
301
+
302
+ `mcp__claws__claws_broadcast(text="...", targetRole="worker", inject=true)`
303
+ or its single-target equivalent (broadcast filtered server-side by `peerId`).
304
+
305
+ The server writes the text directly into the worker's pty via bracketed paste.
306
+ The worker's Claude Code receives it as if the human typed a follow-up. This
307
+ needs no worker-side machinery.
308
+
309
+ When the orchestrator wants to drive a specific worker, the injected text
310
+ SHOULD start with a recognizable header so the worker knows it's a command
311
+ and not noise:
312
+
313
+ ```
314
+ [CLAWS_CMD r=req-abc] approve_request: { "approved": true, "payload": {...} }
315
+ ```
316
+
317
+ The worker's mission instructs it to look for `[CLAWS_CMD r=<id>]` lines and
318
+ correlate by `r`.
319
+
320
+ ### 5.2 Worker-side sidecar (advanced)
321
+
322
+ For workers that need many commands or want to subscribe to broad patterns,
323
+ spawn a side process inside the worker terminal that runs
324
+ `scripts/stream-events.js` with `CLAWS_TOPIC=cmd.<myPeerId>.**` and a known
325
+ output file. The worker's Claude Code (which has access to Bash + Read) can
326
+ tail that file. Higher fidelity, more setup. Use when 5.1 isn't enough.
327
+
328
+ ### 5.3 Standard command schemas
329
+
330
+ Topics under `cmd.<peerId>.*`:
331
+
332
+ | Topic | Purpose | Schema |
333
+ |---|---|---|
334
+ | `cmd.<peerId>.approve` | approve a `BLOCKED` or `REQUEST` event | `{correlation_id, payload}` |
335
+ | `cmd.<peerId>.reject` | reject with reason | `{correlation_id, reason}` |
336
+ | `cmd.<peerId>.abort` | terminate the worker now | `{reason}` |
337
+ | `cmd.<peerId>.pause` | pause until resumed | `{}` |
338
+ | `cmd.<peerId>.resume` | resume after pause | `{}` |
339
+ | `cmd.<peerId>.set_phase` | force phase transition (override) | `{phase, reason}` |
340
+ | `cmd.<peerId>.spawn` | tell worker to spawn a sub-worker | `{name, mission, model?}` |
341
+ | `cmd.<peerId>.inject_text` | raw text inject (no schema) | `{text, paste?}` |
342
+
343
+ Every command MUST carry `correlation_id` matching the `request_id` of the
344
+ event it answers (if any).
345
+
346
+ ---
347
+
348
+ ## 6. Server-emitted Topics
349
+
350
+ The server publishes on `system.*` for events not tied to any one peer.
351
+
352
+ | Topic | When | Payload |
353
+ |---|---|---|
354
+ | `system.peer.joined` | a peer completes `hello` | `{peerId, role, peerName, ts}` |
355
+ | `system.peer.left` | a peer disconnects | `{peerId, role, reason: "clean"\|"crash"\|"timeout"}` |
356
+ | `system.peer.stale` | no heartbeat from a worker for >30s | `{peerId, last_seen, missed_heartbeats}` |
357
+ | `system.gate.fired` | a hook or server-side gate blocked an action | `{tool, reason, peerId}` |
358
+ | `system.budget.warning` | total cost across active workers exceeds threshold | `{current_usd, threshold_usd}` |
359
+ | `system.malformed.received` | server received an event that didn't validate | `{from, topic, error}` |
360
+
361
+ ---
362
+
363
+ <!-- BEGIN GENERATED SCHEMAS -->
364
+ _This section is auto-generated by `npm run schemas` in `extension/`. Run to update._
365
+
366
+ | Topic Pattern | Schema Name | Key Required Fields |
367
+ |---|---|---|
368
+ | `worker.*.boot` | `worker-boot-v1` | model, role, mission_summary, cwd, terminal_id |
369
+ | `worker.*.phase` | `worker-phase-v1` | phase, prev, transition_reason, phases_completed |
370
+ | `worker.*.event` | `worker-event-v1` | kind, severity, message |
371
+ | `worker.*.heartbeat` | `worker-heartbeat-v1` | current_phase, time_in_phase_ms, tokens_used, cost_usd |
372
+ | `worker.*.complete` | `worker-complete-v1` | result, summary, artifacts, phases_completed |
373
+ | `cmd.*.approve` | `cmd-approve-v1` | correlation_id |
374
+ | `cmd.*.reject` | `cmd-reject-v1` | correlation_id, reason |
375
+ | `cmd.*.abort` | `cmd-abort-v1` | reason |
376
+ | `cmd.*.pause` | `cmd-pause-v1` | _(none)_ |
377
+ | `cmd.*.resume` | `cmd-resume-v1` | _(none)_ |
378
+ | `cmd.*.set_phase` | `cmd-set-phase-v1` | phase, reason |
379
+ | `cmd.*.spawn` | `cmd-spawn-v1` | name, mission |
380
+ | `cmd.*.inject_text` | `cmd-inject-text-v1` | text |
381
+ | `system.peer.joined` | `system-peer-joined-v1` | peerId, role, peerName, ts |
382
+ | `system.peer.left` | `system-peer-left-v1` | peerId, role, reason |
383
+ | `system.peer.stale` | `system-peer-stale-v1` | peerId, last_seen, missed_heartbeats |
384
+ | `system.gate.fired` | `system-gate-fired-v1` | tool, reason, peerId |
385
+ | `system.budget.warning` | `system-budget-warning-v1` | current_usd, threshold_usd |
386
+ | `system.malformed.received` | `system-malformed-received-v1` | from, topic, error |
387
+ <!-- END GENERATED SCHEMAS -->
388
+
389
+ ---
390
+
391
+ ## 7. Heartbeat Discipline
392
+
393
+ - Workers publish `worker.<peerId>.heartbeat` every 10s while in non-terminal
394
+ phases. Pause heartbeats during REFLECT (terminal) and after `complete`.
395
+ - The server tracks `last_seen` per peer (any frame counts, including
396
+ publishes). If `last_seen` is older than 30s, server emits
397
+ `system.peer.stale`.
398
+ - Orchestrator subscribed to `system.peer.stale` decides: nudge via
399
+ `cmd.<peerId>.inject_text`, abort via `cmd.<peerId>.abort`, or respawn.
400
+ - A worker that knows it's about to die SHOULD emit a `worker.<peerId>.event`
401
+ with `kind: "ERROR", severity: "fatal"` BEFORE disconnecting (the "last
402
+ will"). The server emits `system.peer.left` immediately on socket close.
403
+
404
+ ---
405
+
406
+ ## 8. Lineage & Trees
407
+
408
+ - `parent_id` in the envelope encodes the parent peer.
409
+ - For workers spawned by the orchestrator: `parent_id` = orchestrator's peerId.
410
+ - For sub-workers spawned by another worker (worker-A spawns worker-B):
411
+ `parent_id` = worker-A's peerId.
412
+ - The orchestrator can walk the tree by querying `claws_peers` and joining on
413
+ `parent_id`.
414
+
415
+ A `correlation_id` MAY span the tree: when worker-A publishes a REQUEST and
416
+ worker-B is spawned to answer, both A's REQUEST and B's COMPLETE share the
417
+ same `correlation_id`. This lets observers thread cause and effect.
418
+
419
+ ---
420
+
421
+ ## 9. Schema Versioning
422
+
423
+ - `v` in the envelope is the envelope-schema version (currently `1`).
424
+ - `schema` is the data-schema name with version suffix (currently `-v1`).
425
+ - Adding a new field to a `data` payload is non-breaking; consumers MUST
426
+ ignore unknown fields.
427
+ - Removing or renaming a field is breaking; bump to `-v2` and run both in
428
+ parallel for a deprecation window.
429
+ - Adding a new event `kind` is non-breaking if consumers default to ignoring
430
+ unknown kinds.
431
+ - Adding a new top-level topic is non-breaking.
432
+ - Removing a topic or changing its semantics is breaking; coordinate
433
+ carefully.
434
+
435
+ ---
436
+
437
+ ## 10. Authorization Rules (server-enforced)
438
+
439
+ In `server.ts`, the publish handler MUST reject:
440
+
441
+ - A peer publishing on `worker.<X>.*` where `X != ownPeerId` → reject with
442
+ `{ok: false, error: "publish forbidden — not your topic"}`.
443
+ - A non-orchestrator publishing on `cmd.*` → reject.
444
+ - Any peer publishing on `system.*` → reject (server-only).
445
+ - Subscribers may subscribe to anything (`**` is allowed).
446
+
447
+ These rules turn pub/sub into a trust boundary, not just a routing layer.
448
+
449
+ ---
450
+
451
+ ## 11. Persistence & Replay
452
+
453
+ - An optional sidecar with `CLAWS_TOPIC='**'` tees every event to
454
+ `.claws/events.jsonl` (one JSON object per line, newline-delimited).
455
+ - Tools can replay: `cat .claws/events.jsonl | jq 'select(.topic | startswith("worker."))'`.
456
+ - For multi-session replay, the file is appended (not truncated). Rotation
457
+ is the user's responsibility (e.g. `logrotate`).
458
+
459
+ ---
460
+
461
+ ## 12. Worker Checklist (what a worker MUST do)
462
+
463
+ A compliant worker:
464
+ 1. After `claws_hello`, immediately publish `worker.<peerId>.boot` with the
465
+ universal envelope.
466
+ 2. Publish `worker.<peerId>.phase` on every phase transition.
467
+ 3. Publish `worker.<peerId>.heartbeat` every 10s while non-terminal.
468
+ 4. Publish `worker.<peerId>.event` at every sentinel checkpoint (BLOCKED,
469
+ REQUEST, HARVEST, ERROR, DECISION).
470
+ 5. Watch its own pty input (or its sidecar tailed file) for
471
+ `[CLAWS_CMD r=<id>]` lines and correlate by `r`.
472
+ 6. On terminal phase entry (REFLECT or FAILED): publish
473
+ `worker.<peerId>.complete` (or a final `ERROR` with `severity: fatal`)
474
+ THEN disconnect.
475
+
476
+ A worker that doesn't do (1)–(4) is invisible to the orchestrator and
477
+ defaults to the legacy polling model.
478
+
479
+ ---
480
+
481
+ ## 13. Orchestrator Checklist (what an orchestrator MUST do)
482
+
483
+ A compliant orchestrator:
484
+ 1. Launch `scripts/stream-events.js` under Monitor with
485
+ `CLAWS_ROLE=orchestrator CLAWS_TOPIC='**'` (or a tighter pattern) at
486
+ session start. ONE persistent connection.
487
+ 2. React to incoming events as Monitor notifications. NO polling.
488
+ 3. Maintain its own model of the worker tree from `boot`, `phase`, and
489
+ `complete` events.
490
+ 4. Respond to `BLOCKED` and `REQUEST` events within a sensible deadline by
491
+ publishing on `cmd.<peerId>.*` with matching `correlation_id`.
492
+ 5. On `system.peer.stale` for a worker it spawned: nudge or respawn.
493
+ 6. On `system.peer.left` with `reason: "crash"`: optionally respawn or escalate.
494
+ 7. Before ending its own session: publish `cmd.role.worker` with `abort` and
495
+ wait for `complete` or `peer.left` from each worker.
496
+
497
+ ---
498
+
499
+ ## 14. Examples — Full Dialogue
500
+
501
+ **Scenario:** orchestrator spawns worker-A to audit `src/server.ts`. A finds
502
+ a complex case and asks for guidance.
503
+
504
+ ```
505
+ # orchestrator → server (via mcp__claws__claws_create + claws_send)
506
+ spawns wrapped terminal with Claude Code, mission: "audit src/server.ts"
507
+
508
+ # server → orchestrator's sidecar
509
+ {topic: "system.peer.joined", from_peer: "p7", role: "worker", peerName: "audit-A"}
510
+
511
+ # worker-A → all subscribers
512
+ {topic: "worker.p7.boot", data: {model:"sonnet-4-6", mission_summary:"audit src/server.ts", parent_peer_id:"p1"}}
513
+
514
+ # worker-A transitions
515
+ {topic: "worker.p7.phase", data: {phase:"DEPLOY", prev:"SPAWN"}}
516
+ {topic: "worker.p7.heartbeat", data: {current_phase:"DEPLOY", tokens_used:120}}
517
+
518
+ # worker-A finds something tricky
519
+ {topic: "worker.p7.event", data: {kind:"REQUEST", request_id:"r1",
520
+ message:"server.ts:380 — should I rewrite the handler or patch it?",
521
+ options: [{id:"A",label:"rewrite"},{id:"B",label:"patch"}]}}
522
+
523
+ # orchestrator (in chat with human) decides "B"
524
+ mcp__claws__claws_publish(topic:"cmd.p7.approve",
525
+ payload:{correlation_id:"r1", chosen:"B"})
526
+
527
+ # server → worker-A's pty (via inject route, see §5.1)
528
+ [CLAWS_CMD r=r1] approve_request: {"chosen":"B"}
529
+
530
+ # worker-A continues
531
+ {topic: "worker.p7.phase", data: {phase:"OBSERVE"}}
532
+ {topic: "worker.p7.event", data: {kind:"HARVEST",
533
+ message:"audit done", artifacts:[{path:"/tmp/audit.md"}]}}
534
+ {topic: "worker.p7.phase", data: {phase:"HARVEST"}}
535
+ {topic: "worker.p7.phase", data: {phase:"CLEANUP"}}
536
+ {topic: "worker.p7.phase", data: {phase:"REFLECT"}}
537
+ {topic: "worker.p7.complete", data: {result:"ok", summary:"...", artifacts:[...]}}
538
+
539
+ # worker-A disconnects
540
+ # server → orchestrator
541
+ {topic: "system.peer.left", from_peer:"p7", reason:"clean"}
542
+ ```
543
+
544
+ The whole exchange happens in real time. The orchestrator's chat sees each
545
+ event as a Monitor notification. No polling, no log scraping, no MISSION_COMPLETE
546
+ string match.
547
+
548
+ ---
549
+
550
+ ## 15. What this Convention does NOT cover (yet)
551
+
552
+ - **Bandwidth/throttling**: a worker that publishes 1000 events/sec will
553
+ flood the orchestrator. Future work: rate-limit per topic per peer.
554
+ - **At-least-once delivery**: `claws/2` is in-process best-effort fan-out. If
555
+ the orchestrator's sidecar disconnects mid-event, the event is lost.
556
+ Persistence (§11) is the workaround.
557
+ - **Cross-machine**: `claws/2` is unix socket only today. WebSocket transport
558
+ is Phase 3 of the project.
559
+ - **Encryption / auth**: same machine, same user — no auth. Cross-machine
560
+ needs token auth (Phase 3).
561
+ - **Schema validation**: today the convention is enforced by humans reading
562
+ the spec. Future: a thin client wrapper that validates before publish, and
563
+ server-side rejection of malformed events.
564
+
565
+ ---
566
+
567
+ ## 16. Quick Reference
568
+
569
+ ```
570
+ WORKER MUST EMIT:
571
+ worker.<id>.boot once, after hello
572
+ worker.<id>.phase (on transition) every phase change
573
+ worker.<id>.heartbeat (every 10s) while non-terminal
574
+ worker.<id>.event (at checkpoints) BLOCKED | REQUEST | HARVEST | ERROR | DECISION | PROGRESS
575
+ worker.<id>.complete once, before disconnect
576
+
577
+ ORCHESTRATOR MUST SUBSCRIBE:
578
+ worker.** (everything from workers)
579
+ system.** (server-generated)
580
+ task.** (if using tasks)
581
+
582
+ ORCHESTRATOR MAY PUBLISH:
583
+ cmd.<peerId>.* (commands to specific worker)
584
+ cmd.role.* (broadcast to a role)
585
+
586
+ SENTINEL TOKEN IN INJECTED TEXT:
587
+ [CLAWS_CMD r=<id>] <command>: <json-payload>
588
+ ```