@@ -106,10 +106,110 @@
106 106 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
107 107 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
if len(got.Payload) != 0 {
108 108 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
t.Errorf("expected empty payload, got %s", got.Payload)
109 109 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
110 110 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
}
111 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
112 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestMatchesRecipient(t *testing.T) {
113 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cases := []struct {
114 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ name string
115 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ to []string
116 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ nick string
117 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ agentType string
118 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ want bool
119 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }{
120 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // backwards compat
121 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"empty to matches all", nil, "claude-1", "worker", true},
122 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
123 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // @all
124 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@all matches worker", []string{"@all"}, "claude-1", "worker", true},
125 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@all matches operator", []string{"@all"}, "glengoolie", "operator", true},
126 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
127 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // role tokens
128 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@workers matches worker", []string{"@workers"}, "claude-1", "worker", true},
129 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@workers no match orchestrator", []string{"@workers"}, "claude-1", "orchestrator", false},
130 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@operators matches operator", []string{"@operators"}, "glengoolie", "operator", true},
131 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@orchestrators matches orchestrator", []string{"@orchestrators"}, "claude-1", "orchestrator", true},
132 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@observers matches observer", []string{"@observers"}, "sentinel", "observer", true},
133 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
134 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // prefix glob
135 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@claude-* matches claude-1", []string{"@claude-*"}, "claude-1", "worker", true},
136 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@claude-* matches claude-sonnet", []string{"@claude-*"}, "claude-sonnet", "worker", true},
137 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@claude-* no match codex-1", []string{"@claude-*"}, "codex-1", "worker", false},
138 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"@gemini-* matches gemini-pro", []string{"@gemini-*"}, "gemini-pro", "worker", true},
139 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
140 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // exact nick
141 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"exact nick match", []string{"codex-7"}, "codex-7", "worker", true},
142 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"exact nick no match", []string{"codex-7"}, "codex-8", "worker", false},
143 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
144 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // OR semantics
145 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"OR: second token matches", []string{"@operators", "codex-7"}, "codex-7", "worker", true},
146 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"OR: first token matches", []string{"@workers", "codex-7"}, "claude-1", "worker", true},
147 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ {"OR: none match", []string{"@operators", "codex-7"}, "claude-1", "worker", false},
148 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
149 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
150 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for _, tc := range cases {
151 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Run(tc.name, func(t *testing.T) {
152 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ env := &protocol.Envelope{
153 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ V: protocol.Version,
154 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ Type: protocol.TypeTaskCreate,
155 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ ID: "test",
156 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ From: "orchestrator",
157 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ To: tc.to,
158 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ TS: 1,
159 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
160 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ got := protocol.MatchesRecipient(env, tc.nick, tc.agentType)
161 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if got != tc.want {
162 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("MatchesRecipient(%v, %q, %q) = %v, want %v", tc.to, tc.nick, tc.agentType, got, tc.want)
163 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
164 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ })
165 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
166 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
167 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
168 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestNewTo(t *testing.T) {
169 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ env, err := protocol.NewTo(protocol.TypeTaskCreate, "orchestrator-1", []string{"@workers", "@claude-*"}, nil)
170 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err != nil {
171 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("NewTo: %v", err)
172 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
173 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(env.To) != 2 {
174 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("To length: got %d, want 2", len(env.To))
175 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
176 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if env.To[0] != "@workers" || env.To[1] != "@claude-*" {
177 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("To: got %v", env.To)
178 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
179 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
180 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ // round-trip
181 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ data, err := protocol.Marshal(env)
182 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err != nil {
183 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("Marshal: %v", err)
184 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
185 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ got, err := protocol.Unmarshal(data)
186 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err != nil {
187 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("Unmarshal: %v", err)
188 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
189 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if len(got.To) != 2 || got.To[0] != "@workers" {
190 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Errorf("round-trip To: got %v", got.To)
191 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
192 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
193 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
194 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ func TestToOmittedWhenEmpty(t *testing.T) {
195 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ env, err := protocol.New(protocol.TypeAgentHello, "agent", nil)
196 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err != nil {
197 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("New: %v", err)
198 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
199 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ data, err := protocol.Marshal(env)
200 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err != nil {
201 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("Marshal: %v", err)
202 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
203 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ var raw map[string]any
204 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if err := json.Unmarshal(data, &raw); err != nil {
205 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Fatalf("json.Unmarshal: %v", err)
206 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
207 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if _, ok := raw["to"]; ok {
208 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ t.Error("expected 'to' key to be omitted when empty")
209 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
210 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ }
111 211 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
112 212 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
func TestAllMessageTypes(t *testing.T) {
113 213 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
types := []string{
114 214 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
protocol.TypeTaskCreate,
115 215 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
protocol.TypeTaskUpdate,
116 216 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!