stream0-channel 0.4.1 → 0.5.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.
@@ -79,8 +79,9 @@ const mcp = new Server(
79
79
 
80
80
  When the user asks you to collaborate with, delegate to, or consult other agents:
81
81
  1. Use the **discover** tool to see which agents are available and what they do
82
- 2. Use the **delegate** tool to send a task and wait for the result
83
- 3. Present the result to the user
82
+ 2. For a single agent: use **delegate** to send a task and wait for the result
83
+ 3. For multiple agents in parallel: use **send_task** for each, then **wait_results** to collect all responses
84
+ 4. Present the results to the user
84
85
 
85
86
  Examples of user requests that should trigger collaboration:
86
87
  - "find someone to review my code"
@@ -163,6 +164,44 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
163
164
  required: ["to", "thread_id", "type", "content"],
164
165
  },
165
166
  },
167
+ {
168
+ name: "send_task",
169
+ description:
170
+ "Send a task to an agent and return immediately without waiting for a response. Returns a thread_id you can pass to wait_results later. Use this when sending tasks to multiple agents in parallel.",
171
+ inputSchema: {
172
+ type: "object",
173
+ properties: {
174
+ to: { type: "string", description: "The agent ID to send the task to" },
175
+ task: { type: "string", description: "Description of what you need the agent to do" },
176
+ context: { type: "string", description: "Additional context like code diffs or file contents" },
177
+ },
178
+ required: ["to", "task"],
179
+ },
180
+ },
181
+ {
182
+ name: "wait_results",
183
+ description:
184
+ "Wait for results from one or more agents that were given tasks via send_task. Pass the thread_ids returned by send_task. Returns all results once every agent has responded (done or failed), or when timeout is reached.",
185
+ inputSchema: {
186
+ type: "object",
187
+ properties: {
188
+ threads: {
189
+ type: "array",
190
+ items: {
191
+ type: "object",
192
+ properties: {
193
+ thread_id: { type: "string" },
194
+ from: { type: "string", description: "The agent ID that should respond" },
195
+ },
196
+ required: ["thread_id", "from"],
197
+ },
198
+ description: "List of {thread_id, from} pairs to wait for",
199
+ },
200
+ timeout: { type: "number", description: "Max seconds to wait (default: 120, max: 300)" },
201
+ },
202
+ required: ["threads"],
203
+ },
204
+ },
166
205
  {
167
206
  name: "ack",
168
207
  description: "Acknowledge a message after processing it so it won't appear again.",
@@ -317,6 +356,88 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
317
356
  };
318
357
  }
319
358
 
359
+ // --- send_task (agent-auth, fire-and-forget) ---
360
+ if (name === "send_task") {
361
+ const { to, task, context } = args;
362
+ const threadId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
363
+
364
+ const content = { task };
365
+ if (context) content.context = context;
366
+
367
+ await stream0Post(`/agents/${to}/inbox`, {
368
+ thread_id: threadId,
369
+ type: "request",
370
+ content,
371
+ }, true);
372
+
373
+ console.error(`[stream0-channel] Sent task to ${to} (thread: ${threadId})`);
374
+
375
+ return {
376
+ content: [
377
+ { type: "text", text: `Task sent to **${to}** (thread: ${threadId})` },
378
+ ],
379
+ };
380
+ }
381
+
382
+ // --- wait_results (agent-auth, poll multiple threads) ---
383
+ if (name === "wait_results") {
384
+ const { threads, timeout: userTimeout } = args;
385
+ const timeoutSec = Math.min(Math.max(userTimeout || 120, 10), 300);
386
+ const deadline = Date.now() + timeoutSec * 1000;
387
+
388
+ // Track which threads we're still waiting for
389
+ const pending = new Map(); // thread_id -> from
390
+ for (const t of threads) pending.set(t.thread_id, t.from);
391
+
392
+ const results = []; // { from, thread_id, type, content }
393
+
394
+ console.error(`[stream0-channel] Waiting for ${pending.size} results (timeout: ${timeoutSec}s)...`);
395
+
396
+ while (pending.size > 0 && Date.now() < deadline) {
397
+ const pollTimeout = Math.min(25, Math.ceil((deadline - Date.now()) / 1000));
398
+ if (pollTimeout <= 0) break;
399
+
400
+ const result = await stream0Get(`/agents/${AGENT_ID}/inbox`, {
401
+ status: "unread",
402
+ timeout: String(pollTimeout),
403
+ }, true);
404
+
405
+ for (const msg of result?.messages || []) {
406
+ if (!pending.has(msg.thread_id)) continue;
407
+
408
+ await stream0Post(`/inbox/messages/${msg.id}/ack`, undefined, true);
409
+
410
+ if (msg.type === "done" || msg.type === "failed") {
411
+ results.push({
412
+ from: msg.from,
413
+ thread_id: msg.thread_id,
414
+ type: msg.type,
415
+ content: msg.content,
416
+ });
417
+ pending.delete(msg.thread_id);
418
+ console.error(`[stream0-channel] Got [${msg.type}] from ${msg.from} (${pending.size} remaining)`);
419
+ }
420
+ }
421
+ }
422
+
423
+ // Format output
424
+ const lines = results.map((r) => {
425
+ const contentText = typeof r.content === "string" ? r.content : JSON.stringify(r.content, null, 2);
426
+ return `### ${r.from} (${r.type})\n${contentText}`;
427
+ });
428
+
429
+ if (pending.size > 0) {
430
+ const timedOut = [...pending.values()];
431
+ lines.push(`\n**Timed out** waiting for: ${timedOut.join(", ")}`);
432
+ }
433
+
434
+ return {
435
+ content: [
436
+ { type: "text", text: `## Results (${results.length}/${threads.length})\n\n${lines.join("\n\n")}` },
437
+ ],
438
+ };
439
+ }
440
+
320
441
  // --- reply (agent-auth) ---
321
442
  if (name === "reply") {
322
443
  const { to, thread_id, type, content } = args;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stream0-channel",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Stream0 MCP channel for Claude Code",
5
5
  "bin": {
6
6
  "stream0-channel": "bin/stream0-channel.mjs"