@spoons-and-mirrors/iam 0.1.6 → 0.1.8
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 +85 -44
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +135 -67
- package/dist/logger.d.ts.map +1 -1
- package/dist/prompt.d.ts +3 -4
- package/dist/prompt.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# IAM (Inter-Agent Messaging)
|
|
2
2
|
|
|
3
|
-
Enable parallel agents communication for opencode
|
|
3
|
+
### Enable parallel agents communication for opencode
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
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.
|
|
10
|
-
|
|
11
9
|
```mermaid
|
|
12
10
|
sequenceDiagram
|
|
13
11
|
participant Parent as Parent Session
|
|
@@ -17,62 +15,105 @@ sequenceDiagram
|
|
|
17
15
|
Parent->>A: spawn task
|
|
18
16
|
Parent->>B: spawn task
|
|
19
17
|
|
|
20
|
-
Note over A,B:
|
|
18
|
+
Note over A,B: Attention mechanism activation
|
|
19
|
+
|
|
20
|
+
A->>B: broadcast(message="Doing X") notifies all parallel agents
|
|
21
|
+
Note over A: Tool result shows agentB is available
|
|
22
|
+
|
|
23
|
+
B->>A: broadcast(message="Doing Y") notifies all parallel agents
|
|
24
|
+
Note over B: Tool result shows agentA is available
|
|
25
|
+
|
|
26
|
+
Note over A,B: Attention mechanism activated
|
|
21
27
|
|
|
22
28
|
A->>B: broadcast(recipient="agentB", message="Question?")
|
|
29
|
+
A->>B: broadcast(recipient="agentB", message="Other question?")
|
|
23
30
|
|
|
24
|
-
Note over B:
|
|
25
|
-
Note over B: Responds
|
|
31
|
+
Note over B: Receive messages in context
|
|
26
32
|
|
|
27
|
-
B->>A: broadcast(
|
|
33
|
+
B->>A: broadcast(reply_to=1, message="Answer!")
|
|
34
|
+
Note over B: Tool result shows source message
|
|
35
|
+
Note over B: Message 1 removed from context
|
|
36
|
+
Note over B: Audit trace persists in tool result
|
|
28
37
|
|
|
29
38
|
Note over A: Receives reply
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
Add to your OpenCode config:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
"plugin": ["@spoons-and-mirrors/iam@latest"]
|
|
32
47
|
```
|
|
33
48
|
|
|
34
49
|
## The `broadcast` Tool
|
|
35
50
|
|
|
36
51
|
```
|
|
37
|
-
broadcast(message="...")
|
|
38
|
-
broadcast(recipient="agentB", message="...")
|
|
39
|
-
broadcast(reply_to=
|
|
40
|
-
broadcast(recipient="agentA", reply_to=[1], message="...") # Reply and mark handled
|
|
52
|
+
broadcast(message="...") # Send to all agents
|
|
53
|
+
broadcast(recipient="agentB", message="...") # Send to specific agent
|
|
54
|
+
broadcast(reply_to=1, message="...") # Reply to message #1 (auto-wires recipient)
|
|
41
55
|
```
|
|
42
56
|
|
|
43
57
|
### Parameters
|
|
44
58
|
|
|
45
|
-
| Parameter | Required | Description
|
|
46
|
-
| ----------- | -------- |
|
|
47
|
-
| `message` | Yes | Your message content
|
|
48
|
-
| `recipient` | No | Target agent(
|
|
49
|
-
| `reply_to` | No |
|
|
59
|
+
| Parameter | Required | Description |
|
|
60
|
+
| ----------- | -------- | --------------------------------------------------------------- |
|
|
61
|
+
| `message` | Yes | Your message content |
|
|
62
|
+
| `recipient` | No | Target agent (single agent only) |
|
|
63
|
+
| `reply_to` | No | Message ID to reply to - auto-wires recipient to message sender |
|
|
50
64
|
|
|
51
65
|
## Receiving Messages
|
|
52
66
|
|
|
53
|
-
Messages
|
|
67
|
+
Messages are injected as a synthetic `broadcast` tool result. Here's the complete structure:
|
|
54
68
|
|
|
55
69
|
```json
|
|
56
70
|
{
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
"tool": "broadcast",
|
|
72
|
+
"state": {
|
|
73
|
+
"status": "completed",
|
|
74
|
+
"input": { "synthetic": true },
|
|
75
|
+
"output": {
|
|
76
|
+
"agents": [
|
|
77
|
+
{ "name": "agentA", "status": "Working on frontend components" }
|
|
78
|
+
],
|
|
79
|
+
"messages": [
|
|
80
|
+
{
|
|
81
|
+
"id": 1,
|
|
82
|
+
"from": "agentA",
|
|
83
|
+
"content": "What's the status on the API?"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": 2,
|
|
87
|
+
"from": "agentA",
|
|
88
|
+
"content": "Also, can you check the tests?"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"title": "1 agent(s), 2 message(s)"
|
|
93
|
+
}
|
|
62
94
|
}
|
|
63
95
|
```
|
|
64
96
|
|
|
65
|
-
|
|
97
|
+
- **`input.synthetic`**: Indicates this was injected by IAM, not a real agent call
|
|
98
|
+
- **`output.agents`**: Status announcements from other agents (not replyable)
|
|
99
|
+
- **`output.messages`**: Actual messages you can reply to using `reply_to`
|
|
66
100
|
|
|
67
101
|
Messages persist in the inbox until the agent marks them as handled using `reply_to`.
|
|
68
102
|
|
|
69
|
-
|
|
103
|
+
**Discovery:** Agents discover each other by calling `broadcast` which activates the attention mechanism.
|
|
70
104
|
|
|
71
|
-
|
|
105
|
+
## Attention Layer
|
|
72
106
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
107
|
+
On every LLM fetch, pending inbox messages are injected as a synthetic `broadcast` tool result at the end of the message chain. The synthetic call has `input: { synthetic: true }` to indicate it was injected by IAM, not a real agent call.
|
|
108
|
+
|
|
109
|
+
After injection, the message chain looks like:
|
|
110
|
+
|
|
111
|
+
1. system prompt
|
|
112
|
+
2. user message
|
|
113
|
+
3. assistant response
|
|
114
|
+
4. tool calls...
|
|
115
|
+
5. user message
|
|
116
|
+
6. **`[broadcast]` 1 agent(s), 2 message(s)** ← injected at end
|
|
76
117
|
|
|
77
118
|
## Example Workflow
|
|
78
119
|
|
|
@@ -80,21 +121,21 @@ Add to your OpenCode config:
|
|
|
80
121
|
# Parent spawns two agents to work on different parts of a feature
|
|
81
122
|
|
|
82
123
|
AgentA (working on frontend):
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
124
|
+
-> broadcast(message="Starting frontend work")
|
|
125
|
+
# Tool result shows: "Available agents: agentB"
|
|
126
|
+
-> ... does work ...
|
|
127
|
+
-> broadcast(recipient="agentB", message="Need the API schema")
|
|
86
128
|
|
|
87
129
|
AgentB (working on backend):
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
130
|
+
-> broadcast(message="Starting backend work")
|
|
131
|
+
# Tool result shows: "Available agents: agentA"
|
|
132
|
+
-> ... sees AgentA's question in inbox ...
|
|
133
|
+
-> broadcast(reply_to=1, message="Here's the schema: {...}")
|
|
134
|
+
# Tool result shows: Marked as handled: #1 from agentA
|
|
135
|
+
# Recipient auto-wired to agentA
|
|
91
136
|
|
|
92
137
|
AgentA:
|
|
93
|
-
|
|
94
|
-
|
|
138
|
+
-> ... sees AgentB's response in inbox ...
|
|
139
|
+
-> broadcast(reply_to=1, message="Got it, thanks!")
|
|
140
|
+
# Recipient auto-wired to agentB
|
|
95
141
|
```
|
|
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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAkmBlD,QAAA,MAAM,MAAM,EAAE,MAyXb,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { tool } from "@opencode-ai/plugin";
|
|
3
3
|
|
|
4
4
|
// prompt.ts
|
|
5
|
-
var BROADCAST_DESCRIPTION = `Communicate with other parallel agents. Use 'recipient' for specific agent
|
|
6
|
-
function broadcastResult(alias, recipients, parallelAgents,
|
|
5
|
+
var BROADCAST_DESCRIPTION = `Communicate with other parallel agents. Use 'recipient' for a specific agent, or omit to message all. Use 'reply_to' to reply (auto-wires recipient to sender).`;
|
|
6
|
+
function broadcastResult(alias, recipients, parallelAgents, handledMessage) {
|
|
7
7
|
const lines = [];
|
|
8
8
|
lines.push(`You are: ${alias}`);
|
|
9
9
|
lines.push(``);
|
|
10
10
|
if (parallelAgents.length > 0) {
|
|
11
|
-
lines.push(`Available agents
|
|
11
|
+
lines.push(`Available agents:`);
|
|
12
12
|
for (const agent of parallelAgents) {
|
|
13
13
|
if (agent.description) {
|
|
14
14
|
lines.push(` - ${agent.alias}: ${agent.description}`);
|
|
@@ -19,27 +19,21 @@ function broadcastResult(alias, recipients, parallelAgents, handledMessages) {
|
|
|
19
19
|
} else {
|
|
20
20
|
lines.push(`No other agents available yet.`);
|
|
21
21
|
}
|
|
22
|
-
if (
|
|
22
|
+
if (handledMessage) {
|
|
23
|
+
lines.push(``);
|
|
24
|
+
const preview = handledMessage.body.length > 80 ? handledMessage.body.substring(0, 80) + "..." : handledMessage.body;
|
|
25
|
+
lines.push(`Replied to #${handledMessage.id} from ${handledMessage.from}:`);
|
|
26
|
+
lines.push(` "${preview}"`);
|
|
27
|
+
} else if (recipients.length > 0) {
|
|
23
28
|
lines.push(``);
|
|
24
29
|
const recipientStr = recipients.length === 1 ? recipients[0] : recipients.join(", ");
|
|
25
30
|
lines.push(`Message sent to: ${recipientStr}`);
|
|
26
31
|
}
|
|
27
|
-
if (handledMessages.length > 0) {
|
|
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
|
-
}
|
|
34
|
-
}
|
|
35
32
|
return lines.join(`
|
|
36
33
|
`);
|
|
37
34
|
}
|
|
38
35
|
var BROADCAST_MISSING_MESSAGE = `Error: 'message' parameter is required.`;
|
|
39
36
|
var BROADCAST_SELF_MESSAGE = `Warning: You cannot send a message to yourself. The target alias is your own alias. Choose a different recipient.`;
|
|
40
|
-
function broadcastMessageTooLong(length, maxLength) {
|
|
41
|
-
return `Error: Message too long (${length} chars). Maximum allowed: ${maxLength} chars.`;
|
|
42
|
-
}
|
|
43
37
|
function broadcastUnknownRecipient(recipient, known) {
|
|
44
38
|
const list = known.length > 0 ? `Known agents: ${known.join(", ")}` : "No agents available yet.";
|
|
45
39
|
return `Error: Unknown recipient "${recipient}". ${list}`;
|
|
@@ -58,31 +52,39 @@ Call \`broadcast(message="...")\` as your FIRST action to announce yourself and
|
|
|
58
52
|
- \`broadcast(recipient="agentB", message="...")\` → send to specific agent
|
|
59
53
|
|
|
60
54
|
## Receiving Messages
|
|
61
|
-
Incoming messages appear as \`broadcast\` tool results
|
|
55
|
+
Incoming messages appear as synthetic \`broadcast\` tool results:
|
|
62
56
|
\`\`\`
|
|
63
|
-
{
|
|
57
|
+
{
|
|
58
|
+
agents: [{ name: "agentA", status: "Working on X" }],
|
|
59
|
+
messages: [{ id: 1, from: "agentA", content: "..." }]
|
|
60
|
+
}
|
|
64
61
|
\`\`\`
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
|
|
63
|
+
- **agents**: Status announcements (not replyable)
|
|
64
|
+
- **messages**: Messages you can reply to using \`reply_to\`
|
|
65
|
+
|
|
66
|
+
Use \`reply_to\` to reply to a message (auto-wires recipient):
|
|
67
|
+
- \`broadcast(reply_to=1, message="...")\` → replies to message #1
|
|
69
68
|
</instructions>
|
|
70
69
|
`;
|
|
71
70
|
|
|
72
71
|
// logger.ts
|
|
73
72
|
import * as fs from "fs";
|
|
74
73
|
import * as path from "path";
|
|
74
|
+
var DEBUG_ENABLED = process.env.OPENCODE_IAM_DEBUG_LOGS === "1";
|
|
75
75
|
var LOG_DIR = path.join(process.cwd(), ".logs");
|
|
76
76
|
var LOG_FILE = path.join(LOG_DIR, "iam.log");
|
|
77
77
|
var WRITE_INTERVAL_MS = 100;
|
|
78
78
|
var logBuffer = [];
|
|
79
79
|
var writeScheduled = false;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
fs.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
if (DEBUG_ENABLED) {
|
|
81
|
+
try {
|
|
82
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
83
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
fs.writeFileSync(LOG_FILE, "");
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
86
88
|
function formatTimestamp() {
|
|
87
89
|
return new Date().toISOString();
|
|
88
90
|
}
|
|
@@ -105,6 +107,9 @@ function scheduleFlush() {
|
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
109
|
function writeLog(level, category, message, data) {
|
|
110
|
+
if (!DEBUG_ENABLED) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
108
113
|
const timestamp = formatTimestamp();
|
|
109
114
|
const dataStr = data !== undefined ? ` | ${JSON.stringify(data)}` : "";
|
|
110
115
|
const logLine = `[${timestamp}] [${level}] [${category}] ${message}${dataStr}
|
|
@@ -130,7 +135,7 @@ var LOG = {
|
|
|
130
135
|
// index.ts
|
|
131
136
|
var CHAR_CODE_A = 65;
|
|
132
137
|
var ALPHABET_SIZE = 26;
|
|
133
|
-
var MAX_DESCRIPTION_LENGTH =
|
|
138
|
+
var MAX_DESCRIPTION_LENGTH = 300;
|
|
134
139
|
var MESSAGE_TTL_MS = 30 * 60 * 1000;
|
|
135
140
|
var UNHANDLED_TTL_MS = 2 * 60 * 60 * 1000;
|
|
136
141
|
var MAX_INBOX_SIZE = 100;
|
|
@@ -142,6 +147,7 @@ var MAX_MESSAGE_LENGTH = 1e4;
|
|
|
142
147
|
var inboxes = new Map;
|
|
143
148
|
var sessionMsgCounter = new Map;
|
|
144
149
|
var activeSessions = new Set;
|
|
150
|
+
var announcedSessions = new Set;
|
|
145
151
|
var sessionToAlias = new Map;
|
|
146
152
|
var aliasToSession = new Map;
|
|
147
153
|
var agentDescriptions = new Map;
|
|
@@ -233,15 +239,17 @@ function getInbox(sessionId) {
|
|
|
233
239
|
}
|
|
234
240
|
return inboxes.get(sessionId);
|
|
235
241
|
}
|
|
236
|
-
function sendMessage(from, to, body) {
|
|
242
|
+
function sendMessage(from, to, body, isStatusAnnouncement = false) {
|
|
243
|
+
const msgIndex = isStatusAnnouncement ? 0 : getNextMsgIndex(to);
|
|
237
244
|
const message = {
|
|
238
245
|
id: generateId(),
|
|
239
|
-
msgIndex
|
|
246
|
+
msgIndex,
|
|
240
247
|
from,
|
|
241
248
|
to,
|
|
242
249
|
body,
|
|
243
250
|
timestamp: Date.now(),
|
|
244
|
-
handled: false
|
|
251
|
+
handled: false,
|
|
252
|
+
isStatusAnnouncement
|
|
245
253
|
};
|
|
246
254
|
const queue = getInbox(to);
|
|
247
255
|
if (queue.length >= MAX_INBOX_SIZE) {
|
|
@@ -259,7 +267,8 @@ function sendMessage(from, to, body) {
|
|
|
259
267
|
msgIndex: message.msgIndex,
|
|
260
268
|
from,
|
|
261
269
|
to,
|
|
262
|
-
bodyLength: body.length
|
|
270
|
+
bodyLength: body.length,
|
|
271
|
+
isStatusAnnouncement
|
|
263
272
|
});
|
|
264
273
|
return message;
|
|
265
274
|
}
|
|
@@ -360,18 +369,39 @@ async function getParentId(client, sessionId) {
|
|
|
360
369
|
function createInboxMessage(sessionId, messages, baseUserMessage) {
|
|
361
370
|
const now = Date.now();
|
|
362
371
|
const userInfo = baseUserMessage.info;
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
372
|
+
const statusAnnouncements = messages.filter((m) => m.isStatusAnnouncement);
|
|
373
|
+
const regularMessages = messages.filter((m) => !m.isStatusAnnouncement);
|
|
374
|
+
const outputData = {};
|
|
375
|
+
if (statusAnnouncements.length > 0) {
|
|
376
|
+
outputData.agents = statusAnnouncements.map((m) => ({
|
|
377
|
+
name: m.from,
|
|
378
|
+
status: m.body
|
|
379
|
+
}));
|
|
380
|
+
}
|
|
381
|
+
if (regularMessages.length > 0) {
|
|
382
|
+
outputData.messages = regularMessages.map((m) => ({
|
|
383
|
+
id: m.msgIndex,
|
|
384
|
+
from: m.from,
|
|
385
|
+
content: m.body
|
|
386
|
+
}));
|
|
387
|
+
}
|
|
368
388
|
const assistantMessageId = `msg_broadcast_${now}`;
|
|
369
389
|
const partId = `prt_broadcast_${now}`;
|
|
370
390
|
const callId = `call_broadcast_${now}`;
|
|
391
|
+
const titleParts = [];
|
|
392
|
+
if (statusAnnouncements.length > 0) {
|
|
393
|
+
titleParts.push(`${statusAnnouncements.length} agent(s)`);
|
|
394
|
+
}
|
|
395
|
+
if (regularMessages.length > 0) {
|
|
396
|
+
titleParts.push(`${regularMessages.length} message(s)`);
|
|
397
|
+
}
|
|
398
|
+
const title = titleParts.length > 0 ? titleParts.join(", ") : "Inbox";
|
|
399
|
+
const output = JSON.stringify(outputData);
|
|
371
400
|
log.debug(LOG.MESSAGE, `Creating bundled inbox message`, {
|
|
372
401
|
sessionId,
|
|
373
|
-
|
|
374
|
-
|
|
402
|
+
totalCount: messages.length,
|
|
403
|
+
statusCount: statusAnnouncements.length,
|
|
404
|
+
messageCount: regularMessages.length
|
|
375
405
|
});
|
|
376
406
|
const result = {
|
|
377
407
|
info: {
|
|
@@ -403,12 +433,13 @@ function createInboxMessage(sessionId, messages, baseUserMessage) {
|
|
|
403
433
|
tool: "broadcast",
|
|
404
434
|
state: {
|
|
405
435
|
status: "completed",
|
|
406
|
-
input: {
|
|
407
|
-
output
|
|
408
|
-
title
|
|
436
|
+
input: { synthetic: true },
|
|
437
|
+
output,
|
|
438
|
+
title,
|
|
409
439
|
metadata: {
|
|
410
|
-
incoming_message:
|
|
411
|
-
message_count:
|
|
440
|
+
incoming_message: regularMessages.length > 0,
|
|
441
|
+
message_count: regularMessages.length,
|
|
442
|
+
status_count: statusAnnouncements.length
|
|
412
443
|
},
|
|
413
444
|
time: { start: now, end: now }
|
|
414
445
|
}
|
|
@@ -428,59 +459,96 @@ var plugin = async (ctx) => {
|
|
|
428
459
|
broadcast: tool({
|
|
429
460
|
description: BROADCAST_DESCRIPTION,
|
|
430
461
|
args: {
|
|
431
|
-
recipient: tool.schema.string().optional().describe("Target agent(
|
|
462
|
+
recipient: tool.schema.string().optional().describe("Target agent (single agent only). Omit to send to all."),
|
|
432
463
|
message: tool.schema.string().describe("Your message"),
|
|
433
|
-
reply_to: tool.schema.
|
|
464
|
+
reply_to: tool.schema.number().optional().describe("Message ID to mark as handled")
|
|
434
465
|
},
|
|
435
466
|
async execute(args, context) {
|
|
436
467
|
const sessionId = context.sessionID;
|
|
437
|
-
const isFirstCall = !
|
|
468
|
+
const isFirstCall = !announcedSessions.has(sessionId);
|
|
438
469
|
registerSession(sessionId);
|
|
439
470
|
const alias = getAlias(sessionId);
|
|
440
471
|
if (!args.message) {
|
|
441
472
|
log.warn(LOG.TOOL, `broadcast missing 'message'`, { alias });
|
|
442
473
|
return BROADCAST_MISSING_MESSAGE;
|
|
443
474
|
}
|
|
444
|
-
|
|
445
|
-
|
|
475
|
+
let messageContent = args.message;
|
|
476
|
+
if (messageContent.length > MAX_MESSAGE_LENGTH) {
|
|
477
|
+
log.warn(LOG.TOOL, `broadcast message truncated`, {
|
|
446
478
|
alias,
|
|
447
|
-
|
|
479
|
+
originalLength: messageContent.length,
|
|
480
|
+
truncatedTo: MAX_MESSAGE_LENGTH
|
|
448
481
|
});
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
if (isFirstCall) {
|
|
452
|
-
setDescription(sessionId, args.message);
|
|
482
|
+
messageContent = messageContent.substring(0, MAX_MESSAGE_LENGTH) + "... [truncated]";
|
|
453
483
|
}
|
|
484
|
+
const parallelAgents = getParallelAgents(sessionId);
|
|
454
485
|
log.debug(LOG.TOOL, `broadcast called`, {
|
|
455
486
|
sessionId,
|
|
456
487
|
alias,
|
|
457
488
|
recipient: args.recipient,
|
|
458
489
|
reply_to: args.reply_to,
|
|
459
|
-
messageLength:
|
|
490
|
+
messageLength: messageContent.length,
|
|
460
491
|
isFirstCall
|
|
461
492
|
});
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
493
|
+
if (isFirstCall && args.reply_to === undefined) {
|
|
494
|
+
announcedSessions.add(sessionId);
|
|
495
|
+
setDescription(sessionId, messageContent);
|
|
496
|
+
const knownAgents2 = getKnownAliases(sessionId);
|
|
497
|
+
for (const targetAlias of knownAgents2) {
|
|
498
|
+
const recipientSessionId = aliasToSession.get(targetAlias);
|
|
499
|
+
if (recipientSessionId) {
|
|
500
|
+
sendMessage(alias, recipientSessionId, messageContent, true);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
log.info(LOG.TOOL, `First broadcast - sent status announcement`, {
|
|
466
504
|
alias,
|
|
467
|
-
|
|
468
|
-
|
|
505
|
+
status: messageContent,
|
|
506
|
+
sentTo: knownAgents2
|
|
469
507
|
});
|
|
508
|
+
return broadcastResult(alias, knownAgents2, parallelAgents, undefined);
|
|
509
|
+
}
|
|
510
|
+
if (isFirstCall) {
|
|
511
|
+
announcedSessions.add(sessionId);
|
|
512
|
+
log.info(LOG.TOOL, `First broadcast with reply_to - treating as normal reply`, {
|
|
513
|
+
alias,
|
|
514
|
+
reply_to: args.reply_to
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
let handledMessage;
|
|
518
|
+
let autoRecipient;
|
|
519
|
+
if (args.reply_to !== undefined) {
|
|
520
|
+
const handled = markMessagesAsHandled(sessionId, [args.reply_to]);
|
|
521
|
+
if (handled.length > 0) {
|
|
522
|
+
handledMessage = handled[0];
|
|
523
|
+
autoRecipient = handledMessage.from;
|
|
524
|
+
if (args.recipient && args.recipient !== autoRecipient) {
|
|
525
|
+
log.warn(LOG.TOOL, `reply_to provided - ignoring recipient param`, {
|
|
526
|
+
alias,
|
|
527
|
+
providedRecipient: args.recipient,
|
|
528
|
+
autoWiredRecipient: autoRecipient
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
log.info(LOG.TOOL, `Handled message via reply_to`, {
|
|
532
|
+
alias,
|
|
533
|
+
msgId: args.reply_to,
|
|
534
|
+
autoRecipient
|
|
535
|
+
});
|
|
536
|
+
}
|
|
470
537
|
}
|
|
471
538
|
const knownAgents = getKnownAliases(sessionId);
|
|
472
|
-
const parallelAgents = getParallelAgents(sessionId);
|
|
473
539
|
let targetAliases;
|
|
474
|
-
if (
|
|
540
|
+
if (autoRecipient) {
|
|
541
|
+
targetAliases = [autoRecipient];
|
|
542
|
+
} else if (!args.recipient) {
|
|
475
543
|
targetAliases = knownAgents;
|
|
476
544
|
} else {
|
|
477
|
-
targetAliases = args.recipient.
|
|
545
|
+
targetAliases = [args.recipient.trim()];
|
|
478
546
|
}
|
|
479
547
|
if (targetAliases.length === 0) {
|
|
480
548
|
log.info(LOG.TOOL, `No recipients, returning agent info`, {
|
|
481
549
|
alias
|
|
482
550
|
});
|
|
483
|
-
return broadcastResult(alias, [], parallelAgents,
|
|
551
|
+
return broadcastResult(alias, [], parallelAgents, handledMessage);
|
|
484
552
|
}
|
|
485
553
|
const parentId = await getParentId(client, sessionId);
|
|
486
554
|
const recipientSessions = [];
|
|
@@ -511,11 +579,11 @@ var plugin = async (ctx) => {
|
|
|
511
579
|
log.info(LOG.TOOL, `No valid recipients after filtering`, {
|
|
512
580
|
alias
|
|
513
581
|
});
|
|
514
|
-
return broadcastResult(alias, [], parallelAgents,
|
|
582
|
+
return broadcastResult(alias, [], parallelAgents, handledMessage);
|
|
515
583
|
}
|
|
516
584
|
const isTargetingParent = parentId && recipientSessions.includes(parentId);
|
|
517
585
|
for (const recipientSessionId of recipientSessions) {
|
|
518
|
-
sendMessage(alias, recipientSessionId,
|
|
586
|
+
sendMessage(alias, recipientSessionId, messageContent, false);
|
|
519
587
|
}
|
|
520
588
|
if (isTargetingParent) {
|
|
521
589
|
log.info(LOG.MESSAGE, `Broadcasting to parent session, calling notify_once`, { sessionId, parentId });
|
|
@@ -539,7 +607,7 @@ var plugin = async (ctx) => {
|
|
|
539
607
|
});
|
|
540
608
|
}
|
|
541
609
|
}
|
|
542
|
-
return broadcastResult(alias, validTargets, parallelAgents,
|
|
610
|
+
return broadcastResult(alias, validTargets, parallelAgents, handledMessage);
|
|
543
611
|
}
|
|
544
612
|
})
|
|
545
613
|
},
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../logger.ts"],"names":[],"mappings":"AAuCA;;GAEG;AACH,iBAAe,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAexC;AA+BD,eAAO,MAAM,GAAG;sBACI,MAAM,WAAW,MAAM,SAAS,OAAO;qBAGxC,MAAM,WAAW,MAAM,SAAS,OAAO;qBAGvC,MAAM,WAAW,MAAM,SAAS,OAAO;sBAGtC,MAAM,WAAW,MAAM,SAAS,OAAO;IAGzD,yDAAyD;;CAE1D,CAAC;AAGF,eAAO,MAAM,GAAG;;;;;;CAMN,CAAC"}
|
package/dist/prompt.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const BROADCAST_DESCRIPTION = "Communicate with other parallel agents. Use 'recipient' for specific agent
|
|
1
|
+
export declare const BROADCAST_DESCRIPTION = "Communicate with other parallel agents. Use 'recipient' for a specific agent, or omit to message all. Use 'reply_to' to reply (auto-wires recipient to sender).";
|
|
2
2
|
export interface ParallelAgent {
|
|
3
3
|
alias: string;
|
|
4
4
|
description?: string;
|
|
@@ -8,10 +8,9 @@ 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
|
-
export declare function broadcastMessageTooLong(length: number, maxLength: number): string;
|
|
15
14
|
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
|
|
15
|
+
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 synthetic `broadcast` tool results:\n```\n{\n agents: [{ name: \"agentA\", status: \"Working on X\" }],\n messages: [{ id: 1, from: \"agentA\", content: \"...\" }]\n}\n```\n\n- **agents**: Status announcements (not replyable)\n- **messages**: Messages you can reply to using `reply_to`\n\nUse `reply_to` to reply to a message (auto-wires recipient):\n- `broadcast(reply_to=1, message=\"...\")` \u2192 replies to message #1\n</instructions>\n";
|
|
17
16
|
//# 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,
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../prompt.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,oKAAoK,CAAC;AAMvM,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,CAuCR;AAED,eAAO,MAAM,yBAAyB,4CAA4C,CAAC;AAEnF,eAAO,MAAM,sBAAsB,sHAAsH,CAAC;AAE1J,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EAAE,GACd,MAAM,CAMR;AAMD,eAAO,MAAM,aAAa,w+BA4BzB,CAAC"}
|