@spoons-and-mirrors/iam 0.1.7 → 0.1.9
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 +69 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +155 -79
- package/dist/logger.d.ts.map +1 -1
- package/dist/prompt.d.ts +2 -3
- 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 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,67 +15,102 @@ 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
|
|
21
19
|
|
|
22
|
-
A->>
|
|
23
|
-
Note over
|
|
20
|
+
A->>B: broadcast(message="Doing X")
|
|
21
|
+
Note over B: Get announcement in synthetic tool result
|
|
24
22
|
|
|
25
|
-
B->>B: broadcast(message="Doing Y")
|
|
26
|
-
Note over B: Tool result shows agentA is available
|
|
27
23
|
|
|
28
24
|
A->>B: broadcast(recipient="agentB", message="Question?")
|
|
25
|
+
A->>B: broadcast(recipient="agentB", message="Other question?")
|
|
29
26
|
|
|
30
|
-
Note over B:
|
|
27
|
+
Note over B: Get messages in synthetic tool result
|
|
31
28
|
|
|
32
|
-
B->>A: broadcast(
|
|
29
|
+
B->>A: broadcast(reply_to=1, message="Answer!")
|
|
33
30
|
Note over B: Tool result shows source message
|
|
31
|
+
Note over B: Clear message 1 from synthetic
|
|
34
32
|
|
|
35
33
|
Note over A: Receives reply
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Add to your OpenCode config:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
"plugin": ["@spoons-and-mirrors/iam@latest"]
|
|
38
42
|
```
|
|
39
43
|
|
|
40
44
|
## The `broadcast` Tool
|
|
41
45
|
|
|
42
46
|
```
|
|
43
|
-
broadcast(message="...")
|
|
44
|
-
broadcast(recipient="agentB", message="...")
|
|
45
|
-
broadcast(reply_to=1, message="...")
|
|
46
|
-
broadcast(recipient="agentA", reply_to=1, message="...") # Reply and mark handled
|
|
47
|
+
broadcast(message="...") # Send to all agents
|
|
48
|
+
broadcast(recipient="agentB", message="...") # Send to specific agent
|
|
49
|
+
broadcast(reply_to=1, message="...") # Reply to message #1 (auto-wires recipient)
|
|
47
50
|
```
|
|
48
51
|
|
|
49
52
|
### Parameters
|
|
50
53
|
|
|
51
|
-
| Parameter | Required | Description
|
|
52
|
-
| ----------- | -------- |
|
|
53
|
-
| `message` | Yes | Your message content
|
|
54
|
-
| `recipient` | No | Target agent(
|
|
55
|
-
| `reply_to` | No | Message ID to
|
|
54
|
+
| Parameter | Required | Description |
|
|
55
|
+
| ----------- | -------- | --------------------------------------------------------------- |
|
|
56
|
+
| `message` | Yes | Your message content |
|
|
57
|
+
| `recipient` | No | Target agent (single agent only) |
|
|
58
|
+
| `reply_to` | No | Message ID to reply to - auto-wires recipient to message sender |
|
|
56
59
|
|
|
57
60
|
## Receiving Messages
|
|
58
61
|
|
|
59
|
-
Messages
|
|
62
|
+
Messages are injected as a synthetic `broadcast` tool result. Here's the complete structure:
|
|
60
63
|
|
|
61
64
|
```json
|
|
62
65
|
{
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
"tool": "broadcast",
|
|
67
|
+
"state": {
|
|
68
|
+
"status": "completed",
|
|
69
|
+
"input": { "synthetic": true },
|
|
70
|
+
"output": {
|
|
71
|
+
"hint": "ACTION REQUIRED: Announce yourself...",
|
|
72
|
+
"agents": [
|
|
73
|
+
{ "name": "agentA", "status": "Working on frontend components" }
|
|
74
|
+
],
|
|
75
|
+
"messages": [
|
|
76
|
+
{
|
|
77
|
+
"id": 1,
|
|
78
|
+
"from": "agentA",
|
|
79
|
+
"content": "What's the status on the API?"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": 2,
|
|
83
|
+
"from": "agentA",
|
|
84
|
+
"content": "Also, can you check the tests?"
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"title": "1 agent(s), 2 message(s)"
|
|
89
|
+
}
|
|
67
90
|
}
|
|
68
91
|
```
|
|
69
92
|
|
|
93
|
+
- **`input.synthetic`**: Indicates this was injected by IAM, not a real agent call
|
|
94
|
+
- **`output.hint`**: Shown only if agent hasn't announced yet (disappears after first broadcast)
|
|
95
|
+
- **`output.agents`**: Other agents and their status (not replyable)
|
|
96
|
+
- **`output.messages`**: Messages you can reply to using `reply_to`
|
|
97
|
+
|
|
70
98
|
Messages persist in the inbox until the agent marks them as handled using `reply_to`.
|
|
71
99
|
|
|
72
|
-
**Discovery:** Agents discover each other
|
|
100
|
+
**Discovery:** Agents discover each other through synthetic injection. The first `broadcast` call sets the agent's status, which other agents see in the `agents` array.
|
|
73
101
|
|
|
74
|
-
##
|
|
102
|
+
## Attention Layer
|
|
75
103
|
|
|
76
|
-
|
|
104
|
+
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.
|
|
77
105
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
106
|
+
After injection, the message chain looks like:
|
|
107
|
+
|
|
108
|
+
1. system prompt
|
|
109
|
+
2. user message
|
|
110
|
+
3. assistant response
|
|
111
|
+
4. tool calls...
|
|
112
|
+
5. user message
|
|
113
|
+
6. **`[broadcast]` 1 agent(s), 2 message(s)** ← injected at end
|
|
81
114
|
|
|
82
115
|
## Example Workflow
|
|
83
116
|
|
|
@@ -94,10 +127,12 @@ AgentB (working on backend):
|
|
|
94
127
|
-> broadcast(message="Starting backend work")
|
|
95
128
|
# Tool result shows: "Available agents: agentA"
|
|
96
129
|
-> ... sees AgentA's question in inbox ...
|
|
97
|
-
-> broadcast(
|
|
130
|
+
-> broadcast(reply_to=1, message="Here's the schema: {...}")
|
|
98
131
|
# Tool result shows: Marked as handled: #1 from agentA
|
|
132
|
+
# Recipient auto-wired to agentA
|
|
99
133
|
|
|
100
134
|
AgentA:
|
|
101
135
|
-> ... sees AgentB's response in inbox ...
|
|
102
136
|
-> broadcast(reply_to=1, message="Got it, thanks!")
|
|
137
|
+
# Recipient auto-wired to agentB
|
|
103
138
|
```
|
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;AAsnBlD,QAAA,MAAM,MAAM,EAAE,MA6Xb,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
|
|
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
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,25 +19,21 @@ function broadcastResult(alias, recipients, parallelAgents, handledMessage) {
|
|
|
19
19
|
} else {
|
|
20
20
|
lines.push(`No other agents available yet.`);
|
|
21
21
|
}
|
|
22
|
-
if (recipients.length > 0) {
|
|
23
|
-
lines.push(``);
|
|
24
|
-
const recipientStr = recipients.length === 1 ? recipients[0] : recipients.join(", ");
|
|
25
|
-
lines.push(`Message sent to: ${recipientStr}`);
|
|
26
|
-
}
|
|
27
22
|
if (handledMessage) {
|
|
28
23
|
lines.push(``);
|
|
29
24
|
const preview = handledMessage.body.length > 80 ? handledMessage.body.substring(0, 80) + "..." : handledMessage.body;
|
|
30
|
-
lines.push(`
|
|
25
|
+
lines.push(`Replied to #${handledMessage.id} from ${handledMessage.from}:`);
|
|
31
26
|
lines.push(` "${preview}"`);
|
|
27
|
+
} else if (recipients.length > 0) {
|
|
28
|
+
lines.push(``);
|
|
29
|
+
const recipientStr = recipients.length === 1 ? recipients[0] : recipients.join(", ");
|
|
30
|
+
lines.push(`Message sent to: ${recipientStr}`);
|
|
32
31
|
}
|
|
33
32
|
return lines.join(`
|
|
34
33
|
`);
|
|
35
34
|
}
|
|
36
35
|
var BROADCAST_MISSING_MESSAGE = `Error: 'message' parameter is required.`;
|
|
37
36
|
var BROADCAST_SELF_MESSAGE = `Warning: You cannot send a message to yourself. The target alias is your own alias. Choose a different recipient.`;
|
|
38
|
-
function broadcastMessageTooLong(length, maxLength) {
|
|
39
|
-
return `Error: Message too long (${length} chars). Maximum allowed: ${maxLength} chars.`;
|
|
40
|
-
}
|
|
41
37
|
function broadcastUnknownRecipient(recipient, known) {
|
|
42
38
|
const list = known.length > 0 ? `Known agents: ${known.join(", ")}` : "No agents available yet.";
|
|
43
39
|
return `Error: Unknown recipient "${recipient}". ${list}`;
|
|
@@ -48,38 +44,49 @@ var SYSTEM_PROMPT = `
|
|
|
48
44
|
|
|
49
45
|
Use \`broadcast\` to communicate with other parallel agents.
|
|
50
46
|
|
|
51
|
-
## IMPORTANT:
|
|
52
|
-
|
|
47
|
+
## IMPORTANT: Announce Yourself First
|
|
48
|
+
Your first action should be calling \`broadcast(message="what you're working on")\` to announce yourself. Until you do, other agents won't know your purpose. The synthetic tool result will show a hint reminding you to announce.
|
|
53
49
|
|
|
54
50
|
## Sending Messages
|
|
55
|
-
- \`broadcast(message="...")\` → send to all agents
|
|
51
|
+
- \`broadcast(message="...")\` → announce yourself or send to all agents
|
|
56
52
|
- \`broadcast(recipient="agentB", message="...")\` → send to specific agent
|
|
57
53
|
|
|
58
54
|
## Receiving Messages
|
|
59
|
-
Incoming messages appear as \`broadcast\` tool results
|
|
55
|
+
Incoming messages appear as synthetic \`broadcast\` tool results:
|
|
60
56
|
\`\`\`
|
|
61
|
-
{
|
|
57
|
+
{
|
|
58
|
+
hint: "ACTION REQUIRED: Announce yourself...", // only if you haven't announced
|
|
59
|
+
agents: [{ name: "agentA", status: "Working on X" }],
|
|
60
|
+
messages: [{ id: 1, from: "agentA", content: "..." }]
|
|
61
|
+
}
|
|
62
62
|
\`\`\`
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
-
|
|
64
|
+
- **hint**: Disappears after you announce yourself
|
|
65
|
+
- **agents**: Other agents and their status (not replyable)
|
|
66
|
+
- **messages**: Messages you can reply to using \`reply_to\`
|
|
67
|
+
|
|
68
|
+
Use \`reply_to\` to reply to a message (auto-wires recipient):
|
|
69
|
+
- \`broadcast(reply_to=1, message="...")\` → replies to message #1
|
|
66
70
|
</instructions>
|
|
67
71
|
`;
|
|
68
72
|
|
|
69
73
|
// logger.ts
|
|
70
74
|
import * as fs from "fs";
|
|
71
75
|
import * as path from "path";
|
|
76
|
+
var DEBUG_ENABLED = process.env.OPENCODE_IAM_DEBUG_LOGS === "1";
|
|
72
77
|
var LOG_DIR = path.join(process.cwd(), ".logs");
|
|
73
78
|
var LOG_FILE = path.join(LOG_DIR, "iam.log");
|
|
74
79
|
var WRITE_INTERVAL_MS = 100;
|
|
75
80
|
var logBuffer = [];
|
|
76
81
|
var writeScheduled = false;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
fs.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
if (DEBUG_ENABLED) {
|
|
83
|
+
try {
|
|
84
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
85
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
fs.writeFileSync(LOG_FILE, "");
|
|
88
|
+
} catch {}
|
|
89
|
+
}
|
|
83
90
|
function formatTimestamp() {
|
|
84
91
|
return new Date().toISOString();
|
|
85
92
|
}
|
|
@@ -102,6 +109,9 @@ function scheduleFlush() {
|
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
function writeLog(level, category, message, data) {
|
|
112
|
+
if (!DEBUG_ENABLED) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
105
115
|
const timestamp = formatTimestamp();
|
|
106
116
|
const dataStr = data !== undefined ? ` | ${JSON.stringify(data)}` : "";
|
|
107
117
|
const logLine = `[${timestamp}] [${level}] [${category}] ${message}${dataStr}
|
|
@@ -127,7 +137,7 @@ var LOG = {
|
|
|
127
137
|
// index.ts
|
|
128
138
|
var CHAR_CODE_A = 65;
|
|
129
139
|
var ALPHABET_SIZE = 26;
|
|
130
|
-
var MAX_DESCRIPTION_LENGTH =
|
|
140
|
+
var MAX_DESCRIPTION_LENGTH = 300;
|
|
131
141
|
var MESSAGE_TTL_MS = 30 * 60 * 1000;
|
|
132
142
|
var UNHANDLED_TTL_MS = 2 * 60 * 60 * 1000;
|
|
133
143
|
var MAX_INBOX_SIZE = 100;
|
|
@@ -139,12 +149,14 @@ var MAX_MESSAGE_LENGTH = 1e4;
|
|
|
139
149
|
var inboxes = new Map;
|
|
140
150
|
var sessionMsgCounter = new Map;
|
|
141
151
|
var activeSessions = new Set;
|
|
152
|
+
var announcedSessions = new Set;
|
|
142
153
|
var sessionToAlias = new Map;
|
|
143
154
|
var aliasToSession = new Map;
|
|
144
155
|
var agentDescriptions = new Map;
|
|
145
156
|
var nextAgentIndex = 0;
|
|
146
157
|
var registeringSessionsLock = new Set;
|
|
147
158
|
var sessionParentCache = new Map;
|
|
159
|
+
var childSessionCache = new Set;
|
|
148
160
|
function cleanupExpiredMessages() {
|
|
149
161
|
const now = Date.now();
|
|
150
162
|
let totalRemoved = 0;
|
|
@@ -354,21 +366,55 @@ async function getParentId(client, sessionId) {
|
|
|
354
366
|
return null;
|
|
355
367
|
}
|
|
356
368
|
}
|
|
357
|
-
function
|
|
369
|
+
async function isChildSession(client, sessionId) {
|
|
370
|
+
if (childSessionCache.has(sessionId)) {
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
const parentId = await getParentId(client, sessionId);
|
|
374
|
+
if (parentId) {
|
|
375
|
+
childSessionCache.add(sessionId);
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
function createInboxMessage(sessionId, messages, baseUserMessage, parallelAgents, hasAnnounced) {
|
|
358
381
|
const now = Date.now();
|
|
359
382
|
const userInfo = baseUserMessage.info;
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
383
|
+
const outputData = {};
|
|
384
|
+
if (!hasAnnounced) {
|
|
385
|
+
outputData.hint = `ACTION REQUIRED: Announce yourself to other agents by calling broadcast(message="what you're working on")`;
|
|
386
|
+
}
|
|
387
|
+
if (parallelAgents.length > 0) {
|
|
388
|
+
outputData.agents = parallelAgents.map((agent) => ({
|
|
389
|
+
name: agent.alias,
|
|
390
|
+
status: agent.description
|
|
391
|
+
}));
|
|
392
|
+
}
|
|
393
|
+
if (messages.length > 0) {
|
|
394
|
+
outputData.messages = messages.map((m) => ({
|
|
395
|
+
id: m.msgIndex,
|
|
396
|
+
from: m.from,
|
|
397
|
+
content: m.body
|
|
398
|
+
}));
|
|
399
|
+
}
|
|
365
400
|
const assistantMessageId = `msg_broadcast_${now}`;
|
|
366
401
|
const partId = `prt_broadcast_${now}`;
|
|
367
402
|
const callId = `call_broadcast_${now}`;
|
|
368
|
-
|
|
403
|
+
const titleParts = [];
|
|
404
|
+
if (parallelAgents.length > 0) {
|
|
405
|
+
titleParts.push(`${parallelAgents.length} agent(s)`);
|
|
406
|
+
}
|
|
407
|
+
if (messages.length > 0) {
|
|
408
|
+
titleParts.push(`${messages.length} message(s)`);
|
|
409
|
+
}
|
|
410
|
+
const title = titleParts.length > 0 ? titleParts.join(", ") : "Inbox";
|
|
411
|
+
const output = JSON.stringify(outputData);
|
|
412
|
+
log.info(LOG.MESSAGE, `Creating inbox injection`, {
|
|
369
413
|
sessionId,
|
|
370
|
-
|
|
371
|
-
|
|
414
|
+
agents: parallelAgents.map((a) => a.alias),
|
|
415
|
+
agentStatuses: parallelAgents.map((a) => a.description?.substring(0, 50)),
|
|
416
|
+
messageIds: messages.map((m) => m.msgIndex),
|
|
417
|
+
messageFroms: messages.map((m) => m.from)
|
|
372
418
|
});
|
|
373
419
|
const result = {
|
|
374
420
|
info: {
|
|
@@ -400,12 +446,13 @@ function createInboxMessage(sessionId, messages, baseUserMessage) {
|
|
|
400
446
|
tool: "broadcast",
|
|
401
447
|
state: {
|
|
402
448
|
status: "completed",
|
|
403
|
-
input: {
|
|
404
|
-
output
|
|
405
|
-
title
|
|
449
|
+
input: { synthetic: true },
|
|
450
|
+
output,
|
|
451
|
+
title,
|
|
406
452
|
metadata: {
|
|
407
|
-
incoming_message:
|
|
408
|
-
message_count: messages.length
|
|
453
|
+
incoming_message: messages.length > 0,
|
|
454
|
+
message_count: messages.length,
|
|
455
|
+
agent_count: parallelAgents.length
|
|
409
456
|
},
|
|
410
457
|
time: { start: now, end: now }
|
|
411
458
|
}
|
|
@@ -425,55 +472,83 @@ var plugin = async (ctx) => {
|
|
|
425
472
|
broadcast: tool({
|
|
426
473
|
description: BROADCAST_DESCRIPTION,
|
|
427
474
|
args: {
|
|
428
|
-
recipient: tool.schema.string().optional().describe("Target agent(
|
|
475
|
+
recipient: tool.schema.string().optional().describe("Target agent (single agent only). Omit to send to all."),
|
|
429
476
|
message: tool.schema.string().describe("Your message"),
|
|
430
477
|
reply_to: tool.schema.number().optional().describe("Message ID to mark as handled")
|
|
431
478
|
},
|
|
432
479
|
async execute(args, context) {
|
|
433
480
|
const sessionId = context.sessionID;
|
|
434
|
-
const isFirstCall = !
|
|
481
|
+
const isFirstCall = !announcedSessions.has(sessionId);
|
|
435
482
|
registerSession(sessionId);
|
|
436
483
|
const alias = getAlias(sessionId);
|
|
437
484
|
if (!args.message) {
|
|
438
485
|
log.warn(LOG.TOOL, `broadcast missing 'message'`, { alias });
|
|
439
486
|
return BROADCAST_MISSING_MESSAGE;
|
|
440
487
|
}
|
|
441
|
-
|
|
442
|
-
|
|
488
|
+
let messageContent = args.message;
|
|
489
|
+
if (messageContent.length > MAX_MESSAGE_LENGTH) {
|
|
490
|
+
log.warn(LOG.TOOL, `broadcast message truncated`, {
|
|
443
491
|
alias,
|
|
444
|
-
|
|
492
|
+
originalLength: messageContent.length,
|
|
493
|
+
truncatedTo: MAX_MESSAGE_LENGTH
|
|
445
494
|
});
|
|
446
|
-
|
|
495
|
+
messageContent = messageContent.substring(0, MAX_MESSAGE_LENGTH) + "... [truncated]";
|
|
447
496
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
log.debug(LOG.TOOL, `broadcast called`, {
|
|
452
|
-
sessionId,
|
|
497
|
+
const parallelAgents = getParallelAgents(sessionId);
|
|
498
|
+
log.info(LOG.TOOL, `broadcast called`, {
|
|
453
499
|
alias,
|
|
454
500
|
recipient: args.recipient,
|
|
455
501
|
reply_to: args.reply_to,
|
|
456
|
-
messageLength:
|
|
502
|
+
messageLength: messageContent.length,
|
|
457
503
|
isFirstCall
|
|
458
504
|
});
|
|
505
|
+
if (isFirstCall && args.reply_to === undefined) {
|
|
506
|
+
announcedSessions.add(sessionId);
|
|
507
|
+
setDescription(sessionId, messageContent);
|
|
508
|
+
const knownAgents2 = getKnownAliases(sessionId);
|
|
509
|
+
log.info(LOG.TOOL, `First broadcast - status announcement`, {
|
|
510
|
+
alias,
|
|
511
|
+
status: messageContent.substring(0, 80),
|
|
512
|
+
discoveredAgents: knownAgents2
|
|
513
|
+
});
|
|
514
|
+
return broadcastResult(alias, knownAgents2, parallelAgents, undefined);
|
|
515
|
+
}
|
|
516
|
+
if (isFirstCall) {
|
|
517
|
+
announcedSessions.add(sessionId);
|
|
518
|
+
log.info(LOG.TOOL, `First broadcast with reply_to - treating as normal reply`, {
|
|
519
|
+
alias,
|
|
520
|
+
reply_to: args.reply_to
|
|
521
|
+
});
|
|
522
|
+
}
|
|
459
523
|
let handledMessage;
|
|
524
|
+
let autoRecipient;
|
|
460
525
|
if (args.reply_to !== undefined) {
|
|
461
526
|
const handled = markMessagesAsHandled(sessionId, [args.reply_to]);
|
|
462
527
|
if (handled.length > 0) {
|
|
463
528
|
handledMessage = handled[0];
|
|
529
|
+
autoRecipient = handledMessage.from;
|
|
530
|
+
if (args.recipient && args.recipient !== autoRecipient) {
|
|
531
|
+
log.warn(LOG.TOOL, `reply_to provided - ignoring recipient param`, {
|
|
532
|
+
alias,
|
|
533
|
+
providedRecipient: args.recipient,
|
|
534
|
+
autoWiredRecipient: autoRecipient
|
|
535
|
+
});
|
|
536
|
+
}
|
|
464
537
|
log.info(LOG.TOOL, `Handled message via reply_to`, {
|
|
465
538
|
alias,
|
|
466
|
-
msgId: args.reply_to
|
|
539
|
+
msgId: args.reply_to,
|
|
540
|
+
autoRecipient
|
|
467
541
|
});
|
|
468
542
|
}
|
|
469
543
|
}
|
|
470
544
|
const knownAgents = getKnownAliases(sessionId);
|
|
471
|
-
const parallelAgents = getParallelAgents(sessionId);
|
|
472
545
|
let targetAliases;
|
|
473
|
-
if (
|
|
546
|
+
if (autoRecipient) {
|
|
547
|
+
targetAliases = [autoRecipient];
|
|
548
|
+
} else if (!args.recipient) {
|
|
474
549
|
targetAliases = knownAgents;
|
|
475
550
|
} else {
|
|
476
|
-
targetAliases = args.recipient.
|
|
551
|
+
targetAliases = [args.recipient.trim()];
|
|
477
552
|
}
|
|
478
553
|
if (targetAliases.length === 0) {
|
|
479
554
|
log.info(LOG.TOOL, `No recipients, returning agent info`, {
|
|
@@ -514,7 +589,7 @@ var plugin = async (ctx) => {
|
|
|
514
589
|
}
|
|
515
590
|
const isTargetingParent = parentId && recipientSessions.includes(parentId);
|
|
516
591
|
for (const recipientSessionId of recipientSessions) {
|
|
517
|
-
sendMessage(alias, recipientSessionId,
|
|
592
|
+
sendMessage(alias, recipientSessionId, messageContent);
|
|
518
593
|
}
|
|
519
594
|
if (isTargetingParent) {
|
|
520
595
|
log.info(LOG.MESSAGE, `Broadcasting to parent session, calling notify_once`, { sessionId, parentId });
|
|
@@ -564,19 +639,8 @@ var plugin = async (ctx) => {
|
|
|
564
639
|
log.debug(LOG.INJECT, `No sessionID in system.transform input, skipping`);
|
|
565
640
|
return;
|
|
566
641
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
if (!result.data?.parentID) {
|
|
570
|
-
log.debug(LOG.INJECT, `Session has no parentID (main session), skipping IAM`, {
|
|
571
|
-
sessionId
|
|
572
|
-
});
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
} catch (e) {
|
|
576
|
-
log.debug(LOG.INJECT, `Failed to get session info, skipping IAM`, {
|
|
577
|
-
sessionId,
|
|
578
|
-
error: String(e)
|
|
579
|
-
});
|
|
642
|
+
if (!await isChildSession(client, sessionId)) {
|
|
643
|
+
log.debug(LOG.INJECT, `Session has no parentID (main session), skipping IAM`, { sessionId });
|
|
580
644
|
return;
|
|
581
645
|
}
|
|
582
646
|
registerSession(sessionId);
|
|
@@ -593,25 +657,37 @@ var plugin = async (ctx) => {
|
|
|
593
657
|
return;
|
|
594
658
|
}
|
|
595
659
|
const sessionId = lastUserMsg.info.sessionID;
|
|
660
|
+
if (!await isChildSession(client, sessionId)) {
|
|
661
|
+
log.debug(LOG.INJECT, `Skipping messages.transform for main session`, {
|
|
662
|
+
sessionId
|
|
663
|
+
});
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
596
666
|
const unhandled = getUnhandledMessages(sessionId);
|
|
667
|
+
const parallelAgents = getParallelAgents(sessionId);
|
|
597
668
|
log.debug(LOG.INJECT, `Checking for messages in transform`, {
|
|
598
669
|
sessionId,
|
|
599
|
-
unhandledCount: unhandled.length
|
|
670
|
+
unhandledCount: unhandled.length,
|
|
671
|
+
parallelAgentCount: parallelAgents.length
|
|
600
672
|
});
|
|
601
|
-
if (unhandled.length === 0) {
|
|
673
|
+
if (unhandled.length === 0 && parallelAgents.length === 0) {
|
|
674
|
+
log.info(LOG.INJECT, `No agents or messages to inject`, {
|
|
675
|
+
sessionId,
|
|
676
|
+
alias: getAlias(sessionId)
|
|
677
|
+
});
|
|
602
678
|
return;
|
|
603
679
|
}
|
|
604
|
-
log.info(LOG.INJECT, `Injecting
|
|
680
|
+
log.info(LOG.INJECT, `Injecting synthetic broadcast`, {
|
|
605
681
|
sessionId,
|
|
606
|
-
|
|
607
|
-
|
|
682
|
+
alias: getAlias(sessionId),
|
|
683
|
+
agentCount: parallelAgents.length,
|
|
684
|
+
agents: parallelAgents.map((a) => a.alias),
|
|
685
|
+
messageCount: unhandled.length,
|
|
686
|
+
messageIds: unhandled.map((m) => m.msgIndex)
|
|
608
687
|
});
|
|
609
|
-
const
|
|
688
|
+
const hasAnnounced = announcedSessions.has(sessionId);
|
|
689
|
+
const inboxMsg = createInboxMessage(sessionId, unhandled, lastUserMsg, parallelAgents, hasAnnounced);
|
|
610
690
|
output.messages.push(inboxMsg);
|
|
611
|
-
log.info(LOG.INJECT, `Injected inbox at end of message chain`, {
|
|
612
|
-
sessionId,
|
|
613
|
-
messageCount: unhandled.length
|
|
614
|
-
});
|
|
615
691
|
},
|
|
616
692
|
"experimental.config.transform": async (_input, output) => {
|
|
617
693
|
const experimental = output.experimental ?? {};
|
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;
|
|
@@ -11,7 +11,6 @@ export interface HandledMessage {
|
|
|
11
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:
|
|
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: Announce Yourself First\nYour first action should be calling `broadcast(message=\"what you're working on\")` to announce yourself. Until you do, other agents won't know your purpose. The synthetic tool result will show a hint reminding you to announce.\n\n## Sending Messages\n- `broadcast(message=\"...\")` \u2192 announce yourself or 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 hint: \"ACTION REQUIRED: Announce yourself...\", // only if you haven't announced\n agents: [{ name: \"agentA\", status: \"Working on X\" }],\n messages: [{ id: 1, from: \"agentA\", content: \"...\" }]\n}\n```\n\n- **hint**: Disappears after you announce yourself\n- **agents**: Other agents and their status (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,ssCA8BzB,CAAC"}
|