@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 +38 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -22
- package/dist/prompt.d.ts +2 -2
- package/dist/prompt.d.ts.map +1 -1
- package/package.json +1 -1
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
|
|
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:
|
|
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=
|
|
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:
|
|
31
|
-
Note over B:
|
|
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="...")
|
|
38
|
-
broadcast(recipient="agentB", message="...")
|
|
39
|
-
broadcast(reply_to=
|
|
40
|
-
broadcast(recipient="agentA", reply_to=
|
|
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
|
|
49
|
-
| `reply_to` | No |
|
|
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
|
-
{
|
|
59
|
-
{
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
94
|
-
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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,
|
|
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 (
|
|
27
|
+
if (handledMessage) {
|
|
28
28
|
lines.push(``);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
67
|
-
- \`broadcast(recipient="agentA", reply_to=
|
|
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.
|
|
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
|
|
463
|
-
if (args.reply_to
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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[],
|
|
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
|
|
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
|
package/dist/prompt.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|