@spoons-and-mirrors/iam 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@ Enable parallel agents communication for opencode
6
6
 
7
7
  ## How It Works
8
8
 
9
- Parallel agents they can send messages to each other using the `broadcast` tool. Messages are relayed to the proper agent's context.
9
+ Parallel agents can send messages to each other using the `broadcast` tool. Messages are relayed to the proper agent's context.
10
10
 
11
11
  ```mermaid
12
12
  sequenceDiagram
@@ -17,36 +17,42 @@ sequenceDiagram
17
17
  Parent->>A: spawn task
18
18
  Parent->>B: spawn task
19
19
 
20
- Note over A,B: Both agents auto-register on first LLM call
20
+ Note over A,B: Registration
21
+
22
+ A->>A: broadcast(message="Doing X")
23
+ Note over A: Tool result shows agentB is available
24
+
25
+ B->>B: broadcast(message="Doing Y")
26
+ Note over B: Tool result shows agentA is available
21
27
 
22
28
  A->>B: broadcast(recipient="agentB", message="Question?")
23
29
 
24
- Note over B: Receives message
25
- Note over B: Responds
30
+ Note over B: Receives message in inbox
26
31
 
27
- B->>A: broadcast(recipient="agentA", reply_to=[1], message="Answer!")
32
+ B->>A: broadcast(recipient="agentA", reply_to=1, message="Answer!")
33
+ Note over B: Tool result shows source message
28
34
 
29
35
  Note over A: Receives reply
30
- Note over B: Tool output shows both reply and handled message
31
- Note over B: Remove message from inbox
36
+ Note over B: Message removed from inbox
37
+ Note over B: Audit trace persists in tool result
32
38
  ```
33
39
 
34
40
  ## The `broadcast` Tool
35
41
 
36
42
  ```
37
- broadcast(message="...") # Send to all agents
38
- broadcast(recipient="agentB", message="...") # Send to specific agent
39
- broadcast(reply_to=[1, 2], message="...") # Mark messages as handled
40
- broadcast(recipient="agentA", reply_to=[1], message="...") # Reply and mark handled
43
+ broadcast(message="...") # Send to all agents
44
+ broadcast(recipient="agentB", message="...") # Send to specific agent
45
+ broadcast(reply_to=1, message="...") # Mark message as handled
46
+ broadcast(recipient="agentA", reply_to=1, message="...") # Reply and mark handled
41
47
  ```
42
48
 
43
49
  ### Parameters
44
50
 
45
- | Parameter | Required | Description |
46
- | ----------- | -------- | -------------------------------------------------------------------- |
47
- | `message` | Yes | Your message content |
48
- | `recipient` | No | Target agent(s), comma-separated. Omit to send to all |
49
- | `reply_to` | No | Array of message IDs to mark as handled (e.g., `[1]` or `[1, 2, 3]`) |
51
+ | Parameter | Required | Description |
52
+ | ----------- | -------- | ----------------------------------------- |
53
+ | `message` | Yes | Your message content |
54
+ | `recipient` | No | Target agent(s), comma-separated |
55
+ | `reply_to` | No | Message ID to mark as handled (e.g., `1`) |
50
56
 
51
57
  ## Receiving Messages
52
58
 
@@ -55,17 +61,16 @@ Messages appear as a `broadcast` tool result with structured data:
55
61
  ```json
56
62
  {
57
63
  "messages": [
58
- { "id": 1, "from": "agentA", "body": "What's the status on the API?" },
59
- { "id": 2, "from": "agentA", "body": "Also, can you check the tests?" }
60
- ],
61
- "agents": ["agentA", "agentC: Working on backend"]
64
+ {"id": 1, "from": "agentA", "body": "What's the status on the API?"},
65
+ {"id": 2, "from": "agentA", "body": "Also, can you check the tests?"}
66
+ ]
62
67
  }
63
68
  ```
64
69
 
65
- The `agents` array always shows available agents to message. This is injected even when there are no incoming messages.
66
-
67
70
  Messages persist in the inbox until the agent marks them as handled using `reply_to`.
68
71
 
72
+ **Discovery:** Agents discover each other by calling `broadcast` - the tool result shows all available agents.
73
+
69
74
  ## Installation
70
75
 
71
76
  Add to your OpenCode config:
@@ -80,21 +85,19 @@ Add to your OpenCode config:
80
85
  # Parent spawns two agents to work on different parts of a feature
81
86
 
82
87
  AgentA (working on frontend):
83
- broadcast(message="Starting frontend work")
84
- ... does work ...
85
- broadcast(recipient="agentB", message="Need the API schema")
88
+ -> broadcast(message="Starting frontend work")
89
+ # Tool result shows: "Available agents: agentB"
90
+ -> ... does work ...
91
+ -> broadcast(recipient="agentB", message="Need the API schema")
86
92
 
87
93
  AgentB (working on backend):
88
- broadcast(message="Starting backend work")
89
- ... sees AgentA's question in inbox ...
90
- broadcast(recipient="agentA", reply_to=[1], message="Here's the schema: {...}")
94
+ -> broadcast(message="Starting backend work")
95
+ # Tool result shows: "Available agents: agentA"
96
+ -> ... sees AgentA's question in inbox ...
97
+ -> broadcast(recipient="agentA", reply_to=1, message="Here's the schema: {...}")
98
+ # Tool result shows: Marked as handled: #1 from agentA
91
99
 
92
100
  AgentA:
93
- ... sees AgentB's response in inbox ...
94
- broadcast(reply_to=[1], message="Got it, thanks!")
101
+ -> ... sees AgentB's response in inbox ...
102
+ -> broadcast(reply_to=1, message="Got it, thanks!")
95
103
  ```
96
-
97
- ## Notes
98
-
99
- - Agents are assigned aliases automatically: `agentA`, `agentB`, `agentC`, etc.
100
- - Logs are written to `.logs/iam.log` for debugging
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAijBlD,QAAA,MAAM,MAAM,EAAE,MAwTb,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAijBlD,QAAA,MAAM,MAAM,EAAE,MA0Tb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { tool } from "@opencode-ai/plugin";
3
3
 
4
4
  // prompt.ts
5
5
  var BROADCAST_DESCRIPTION = `Communicate with other parallel agents. Use 'recipient' for specific agent(s), or omit to message all. Use 'reply_to' to mark messages as handled.`;
6
- function broadcastResult(alias, recipients, parallelAgents, handledMessages) {
6
+ function broadcastResult(alias, recipients, parallelAgents, handledMessage) {
7
7
  const lines = [];
8
8
  lines.push(`You are: ${alias}`);
9
9
  lines.push(``);
@@ -24,13 +24,11 @@ function broadcastResult(alias, recipients, parallelAgents, handledMessages) {
24
24
  const recipientStr = recipients.length === 1 ? recipients[0] : recipients.join(", ");
25
25
  lines.push(`Message sent to: ${recipientStr}`);
26
26
  }
27
- if (handledMessages.length > 0) {
27
+ if (handledMessage) {
28
28
  lines.push(``);
29
- lines.push(`Marked as handled: ${handledMessages.length} message(s)`);
30
- for (const msg of handledMessages) {
31
- const preview = msg.body.length > 80 ? msg.body.substring(0, 80) + "..." : msg.body;
32
- lines.push(` #${msg.id} from ${msg.from}: "${preview}"`);
33
- }
29
+ const preview = handledMessage.body.length > 80 ? handledMessage.body.substring(0, 80) + "..." : handledMessage.body;
30
+ lines.push(`Marked as handled: #${handledMessage.id} from ${handledMessage.from}`);
31
+ lines.push(` "${preview}"`);
34
32
  }
35
33
  return lines.join(`
36
34
  `);
@@ -63,9 +61,8 @@ Incoming messages appear as \`broadcast\` tool results with a \`messages\` array
63
61
  { messages: [{ id: 1, from: "agentA", body: "..." }, ...] }
64
62
  \`\`\`
65
63
 
66
- Use \`reply_to\` to mark messages as handled (they persist until you do):
67
- - \`broadcast(recipient="agentA", reply_to=[1], message="...")\`
68
- - \`broadcast(recipient="agentA", reply_to=[1, 2, 3], message="...")\`
64
+ Use \`reply_to\` to mark a message as handled (they persist until you do):
65
+ - \`broadcast(recipient="agentA", reply_to=1, message="...")\`
69
66
  </instructions>
70
67
  `;
71
68
 
@@ -430,7 +427,7 @@ var plugin = async (ctx) => {
430
427
  args: {
431
428
  recipient: tool.schema.string().optional().describe("Target agent(s), comma-separated. Omit to send to all."),
432
429
  message: tool.schema.string().describe("Your message"),
433
- reply_to: tool.schema.array(tool.schema.number()).optional().describe("Message IDs to mark as handled (e.g., [1, 2, 3])")
430
+ reply_to: tool.schema.number().optional().describe("Message ID to mark as handled")
434
431
  },
435
432
  async execute(args, context) {
436
433
  const sessionId = context.sessionID;
@@ -459,14 +456,16 @@ var plugin = async (ctx) => {
459
456
  messageLength: args.message.length,
460
457
  isFirstCall
461
458
  });
462
- let handledMessages = [];
463
- if (args.reply_to && args.reply_to.length > 0) {
464
- handledMessages = markMessagesAsHandled(sessionId, args.reply_to);
465
- log.info(LOG.TOOL, `Handled messages via reply_to`, {
466
- alias,
467
- requested: args.reply_to,
468
- actuallyHandled: handledMessages.length
469
- });
459
+ let handledMessage;
460
+ if (args.reply_to !== undefined) {
461
+ const handled = markMessagesAsHandled(sessionId, [args.reply_to]);
462
+ if (handled.length > 0) {
463
+ handledMessage = handled[0];
464
+ log.info(LOG.TOOL, `Handled message via reply_to`, {
465
+ alias,
466
+ msgId: args.reply_to
467
+ });
468
+ }
470
469
  }
471
470
  const knownAgents = getKnownAliases(sessionId);
472
471
  const parallelAgents = getParallelAgents(sessionId);
@@ -480,7 +479,7 @@ var plugin = async (ctx) => {
480
479
  log.info(LOG.TOOL, `No recipients, returning agent info`, {
481
480
  alias
482
481
  });
483
- return broadcastResult(alias, [], parallelAgents, handledMessages);
482
+ return broadcastResult(alias, [], parallelAgents, handledMessage);
484
483
  }
485
484
  const parentId = await getParentId(client, sessionId);
486
485
  const recipientSessions = [];
@@ -511,7 +510,7 @@ var plugin = async (ctx) => {
511
510
  log.info(LOG.TOOL, `No valid recipients after filtering`, {
512
511
  alias
513
512
  });
514
- return broadcastResult(alias, [], parallelAgents, handledMessages);
513
+ return broadcastResult(alias, [], parallelAgents, handledMessage);
515
514
  }
516
515
  const isTargetingParent = parentId && recipientSessions.includes(parentId);
517
516
  for (const recipientSessionId of recipientSessions) {
@@ -539,7 +538,7 @@ var plugin = async (ctx) => {
539
538
  });
540
539
  }
541
540
  }
542
- return broadcastResult(alias, validTargets, parallelAgents, handledMessages);
541
+ return broadcastResult(alias, validTargets, parallelAgents, handledMessage);
543
542
  }
544
543
  })
545
544
  },
package/dist/prompt.d.ts CHANGED
@@ -8,10 +8,10 @@ export interface HandledMessage {
8
8
  from: string;
9
9
  body: string;
10
10
  }
11
- export declare function broadcastResult(alias: string, recipients: string[], parallelAgents: ParallelAgent[], handledMessages: HandledMessage[]): string;
11
+ export declare function broadcastResult(alias: string, recipients: string[], parallelAgents: ParallelAgent[], handledMessage?: HandledMessage): string;
12
12
  export declare const BROADCAST_MISSING_MESSAGE = "Error: 'message' parameter is required.";
13
13
  export declare const BROADCAST_SELF_MESSAGE = "Warning: You cannot send a message to yourself. The target alias is your own alias. Choose a different recipient.";
14
14
  export declare function broadcastMessageTooLong(length: number, maxLength: number): string;
15
15
  export declare function broadcastUnknownRecipient(recipient: string, known: string[]): string;
16
- export declare const SYSTEM_PROMPT = "\n<instructions tool=\"iam\">\n# Inter-Agent Messaging\n\nUse `broadcast` to communicate with other parallel agents.\n\n## IMPORTANT: Broadcast Immediately on Start\nCall `broadcast(message=\"...\")` as your FIRST action to announce yourself and discover other agents. The tool result will show all available agents you can message.\n\n## Sending Messages\n- `broadcast(message=\"...\")` \u2192 send to all agents\n- `broadcast(recipient=\"agentB\", message=\"...\")` \u2192 send to specific agent\n\n## Receiving Messages\nIncoming messages appear as `broadcast` tool results with a `messages` array:\n```\n{ messages: [{ id: 1, from: \"agentA\", body: \"...\" }, ...] }\n```\n\nUse `reply_to` to mark messages as handled (they persist until you do):\n- `broadcast(recipient=\"agentA\", reply_to=[1], message=\"...\")`\n- `broadcast(recipient=\"agentA\", reply_to=[1, 2, 3], message=\"...\")`\n</instructions>\n";
16
+ export declare const SYSTEM_PROMPT = "\n<instructions tool=\"iam\">\n# Inter-Agent Messaging\n\nUse `broadcast` to communicate with other parallel agents.\n\n## IMPORTANT: Broadcast Immediately on Start\nCall `broadcast(message=\"...\")` as your FIRST action to announce yourself and discover other agents. The tool result will show all available agents you can message.\n\n## Sending Messages\n- `broadcast(message=\"...\")` \u2192 send to all agents\n- `broadcast(recipient=\"agentB\", message=\"...\")` \u2192 send to specific agent\n\n## Receiving Messages\nIncoming messages appear as `broadcast` tool results with a `messages` array:\n```\n{ messages: [{ id: 1, from: \"agentA\", body: \"...\" }, ...] }\n```\n\nUse `reply_to` to mark a message as handled (they persist until you do):\n- `broadcast(recipient=\"agentA\", reply_to=1, message=\"...\")`\n</instructions>\n";
17
17
  //# sourceMappingURL=prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../prompt.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,uJAAuJ,CAAC;AAM1L,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAMD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,EAAE,aAAa,EAAE,EAC/B,eAAe,EAAE,cAAc,EAAE,GAChC,MAAM,CAyCR;AAED,eAAO,MAAM,yBAAyB,4CAA4C,CAAC;AAEnF,eAAO,MAAM,sBAAsB,sHAAsH,CAAC;AAE1J,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,MAAM,CAER;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EAAE,GACd,MAAM,CAMR;AAMD,eAAO,MAAM,aAAa,q5BAuBzB,CAAC"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../prompt.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,uJAAuJ,CAAC;AAM1L,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAMD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAAE,EACpB,cAAc,EAAE,aAAa,EAAE,EAC/B,cAAc,CAAC,EAAE,cAAc,GAC9B,MAAM,CA2CR;AAED,eAAO,MAAM,yBAAyB,4CAA4C,CAAC;AAEnF,eAAO,MAAM,sBAAsB,sHAAsH,CAAC;AAE1J,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,MAAM,CAER;AAED,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EAAE,GACd,MAAM,CAMR;AAMD,eAAO,MAAM,aAAa,00BAsBzB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spoons-and-mirrors/iam",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Inter-agent messaging for OpenCode parallel subagents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",