ai2ai 0.1.0 → 1.0.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.
- package/.keys/agent.key +3 -0
- package/.keys/agent.pub +3 -0
- package/.keys/x25519.key.der +0 -0
- package/.keys/x25519.pub.der +0 -0
- package/SKILL.md +39 -0
- package/ai2ai-client.js +351 -0
- package/ai2ai-conversations.js +324 -0
- package/{lib/crypto.js → ai2ai-crypto.js} +19 -39
- package/ai2ai-discovery.js +398 -0
- package/ai2ai-encryption.js +292 -0
- package/ai2ai-handlers.js +392 -0
- package/ai2ai-logger.js +148 -0
- package/ai2ai-queue.js +281 -0
- package/ai2ai-server.js +433 -0
- package/ai2ai-trust.js +137 -0
- package/client.js +434 -0
- package/contacts.example.json +45 -0
- package/contacts.json +57 -0
- package/conversations/06dfc9fc-e0fb-47a6-80ea-6f89b805dcc9.jsonl +1 -0
- package/conversations/06dfc9fc-e0fb-47a6-80ea-6f89b805dcc9.meta.json +31 -0
- package/conversations/132889ee-3c68-4a86-a465-829c467f6782.jsonl +1 -0
- package/conversations/132889ee-3c68-4a86-a465-829c467f6782.meta.json +27 -0
- package/conversations/16c99cf3-7250-4136-8d4a-f5214bcd32ba.jsonl +1 -0
- package/conversations/16c99cf3-7250-4136-8d4a-f5214bcd32ba.meta.json +27 -0
- package/conversations/3f62daf5-49cb-4f9b-9d25-de70625f46e2.jsonl +1 -0
- package/conversations/3f62daf5-49cb-4f9b-9d25-de70625f46e2.meta.json +31 -0
- package/conversations/532b39ab-d513-4e40-98ff-2d3df2d5f256.jsonl +1 -0
- package/conversations/532b39ab-d513-4e40-98ff-2d3df2d5f256.meta.json +27 -0
- package/conversations/5549dc7a-d62e-49f6-93b6-977da7908a11.jsonl +1 -0
- package/conversations/5549dc7a-d62e-49f6-93b6-977da7908a11.meta.json +27 -0
- package/conversations/610ab34a-c7f4-4d24-98b1-75e3f98835d3.jsonl +1 -0
- package/conversations/610ab34a-c7f4-4d24-98b1-75e3f98835d3.meta.json +31 -0
- package/conversations/611610be-28f2-4b31-9afc-b5e278334d8a.jsonl +1 -0
- package/conversations/611610be-28f2-4b31-9afc-b5e278334d8a.meta.json +27 -0
- package/conversations/69ed7660-73bc-4994-9070-db4b264ccd18.jsonl +1 -0
- package/conversations/69ed7660-73bc-4994-9070-db4b264ccd18.meta.json +27 -0
- package/conversations/85eb0bf1-8a1a-4e9b-8b98-963f6b274913.jsonl +1 -0
- package/conversations/85eb0bf1-8a1a-4e9b-8b98-963f6b274913.meta.json +27 -0
- package/conversations/8b1ee339-fcc5-4587-81cd-dc32ea69cfe0.jsonl +1 -0
- package/conversations/8b1ee339-fcc5-4587-81cd-dc32ea69cfe0.meta.json +31 -0
- package/conversations/9b628dc0-0a71-456b-9ba7-8ca2b0872c63.jsonl +1 -0
- package/conversations/9b628dc0-0a71-456b-9ba7-8ca2b0872c63.meta.json +27 -0
- package/conversations/ad3ef614-306b-414c-a5c5-ae0f2bd4e3d8.jsonl +1 -0
- package/conversations/ad3ef614-306b-414c-a5c5-ae0f2bd4e3d8.meta.json +27 -0
- package/conversations/b71f8bc4-3f34-4667-aad1-5ab899339fb0.jsonl +1 -0
- package/conversations/b71f8bc4-3f34-4667-aad1-5ab899339fb0.meta.json +27 -0
- package/conversations/daf8a65b-83eb-4f7e-8052-714457a8f6b0.jsonl +1 -0
- package/conversations/daf8a65b-83eb-4f7e-8052-714457a8f6b0.meta.json +27 -0
- package/conversations/f2728631-64b9-4267-a793-2d39e3ce8f5e.jsonl +1 -0
- package/conversations/f2728631-64b9-4267-a793-2d39e3ce8f5e.meta.json +27 -0
- package/conversations/test-conv-1771128087319.meta.json +27 -0
- package/conversations/test-conv-1771128515164.meta.json +27 -0
- package/conversations/test-conv-1771128546424.meta.json +27 -0
- package/conversations/test-conv-1771128606354.meta.json +27 -0
- package/conversations/test-group-1771128087322.meta.json +27 -0
- package/conversations/test-group-1771128515165.meta.json +27 -0
- package/conversations/test-group-1771128546425.meta.json +27 -0
- package/conversations/test-group-1771128606355.meta.json +27 -0
- package/demo-two-agents.js +395 -0
- package/integrations/express.js +96 -0
- package/integrations/openclaw.js +62 -0
- package/integrations/webhook.js +111 -0
- package/logs/ai2ai-2026-02-15.log +40 -0
- package/openclaw-integration.js +540 -0
- package/package.json +17 -24
- package/package.json.bak +24 -0
- package/pending/139dcb76-7778-4130-b448-c7828184a53f.json +28 -0
- package/pending/187a69f5-9391-41d0-87d6-34d479a6cc50.json +28 -0
- package/pending/2d07e1bb-51f8-4e13-b08b-f1b5b1dc3d1e.json +34 -0
- package/pending/2d13bdf4-a818-4629-bfdf-ac29b1a64ba5.json +28 -0
- package/pending/3029f00d-97a4-4928-9ff8-3500541c381d.json +31 -0
- package/pending/37a3fddb-73e1-4b85-8de5-2def875216bf.json +34 -0
- package/pending/4babfd35-aba7-479f-bc0f-f0c83e31d3db.json +34 -0
- package/pending/602c0022-993a-4b8a-9ba9-04e56ec59bb5.json +34 -0
- package/pending/af925c5f-bed5-4a46-83c3-d16c97d47627.json +28 -0
- package/pending/ba1474fe-41b7-412e-b702-0b74307510b9.json +31 -0
- package/pending/bcf800f6-c5bb-44a9-8e39-195bd624ff92.json +31 -0
- package/pending/c6683665-1321-49ed-8d21-5ae4250848e8.json +31 -0
- package/registry.js +406 -0
- package/reliability.js +467 -0
- package/security.js +386 -0
- package/test-v1.js +540 -0
- package/test.js +705 -0
- package/README.md +0 -184
- package/bin/ai2ai.js +0 -87
- package/lib/approve.js +0 -137
- package/lib/config.js +0 -120
- package/lib/connect.js +0 -89
- package/lib/contacts.js +0 -62
- package/lib/init.js +0 -148
- package/lib/pending.js +0 -114
- package/lib/protocol.js +0 -161
- package/lib/send.js +0 -135
- package/lib/start.js +0 -318
- package/lib/status.js +0 -70
package/.keys/agent.key
ADDED
package/.keys/agent.pub
ADDED
|
Binary file
|
|
Binary file
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# AI2AI — Agent-to-Agent Communication Skill
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
Enables your OpenClaw agent to communicate with other AI agents on behalf of your human. Implements the AI2AI protocol for decentralized agent negotiation.
|
|
5
|
+
|
|
6
|
+
## Commands
|
|
7
|
+
|
|
8
|
+
The human can say:
|
|
9
|
+
- **"Talk to [name]'s AI at [address]"** — Initiate handshake with another agent
|
|
10
|
+
- **"Schedule [event] with [name]"** — Start a schedule negotiation
|
|
11
|
+
- **"Send [name] a message via their AI"** — Relay a message through agents
|
|
12
|
+
- **"Show AI2AI contacts"** — List known agents
|
|
13
|
+
- **"AI2AI status"** — Show connection status and recent conversations
|
|
14
|
+
|
|
15
|
+
## How It Works
|
|
16
|
+
|
|
17
|
+
1. Human tells their agent what they want
|
|
18
|
+
2. Agent formats an AI2AI message and sends it to the other agent's endpoint
|
|
19
|
+
3. Other agent receives it, interprets it, and asks their human
|
|
20
|
+
4. Response comes back
|
|
21
|
+
5. Your agent tells you the result
|
|
22
|
+
|
|
23
|
+
All communication is structured JSON. Humans approve everything (unless trust is elevated).
|
|
24
|
+
|
|
25
|
+
## Files
|
|
26
|
+
- `ai2ai-server.js` — HTTP endpoint that receives incoming AI2AI messages
|
|
27
|
+
- `ai2ai-client.js` — Sends outgoing AI2AI messages
|
|
28
|
+
- `ai2ai-handlers.js` — Intent handlers (schedule, message, etc.)
|
|
29
|
+
- `ai2ai-trust.js` — Trust management and approval flow
|
|
30
|
+
- `ai2ai-crypto.js` — Ed25519 signing and verification
|
|
31
|
+
- `contacts.json` — Known agents and trust levels
|
|
32
|
+
- `conversations/` — Conversation history
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
Set in your OpenClaw config or via environment:
|
|
36
|
+
- `AI2AI_PORT` — Port for incoming connections (default: 18800)
|
|
37
|
+
- `AI2AI_AGENT_NAME` — Your agent's display name
|
|
38
|
+
- `AI2AI_HUMAN_NAME` — Your human's display name
|
|
39
|
+
- `AI2AI_TIMEZONE` — Your timezone
|
package/ai2ai-client.js
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI2AI Client — Send outgoing messages to other agents
|
|
3
|
+
* Supports encryption, queuing, multi-recipient, and logging.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const { loadOrCreateKeys, signMessage } = require('./ai2ai-crypto');
|
|
8
|
+
const { upsertContact, isBlocked, getContact } = require('./ai2ai-trust');
|
|
9
|
+
const { encryptPayloadX25519, loadOrCreateX25519Keys, isEncrypted } = require('./ai2ai-encryption');
|
|
10
|
+
const { queueAndSend } = require('./ai2ai-queue');
|
|
11
|
+
const { createConversation, transitionState, STATES } = require('./ai2ai-conversations');
|
|
12
|
+
const logger = require('./ai2ai-logger');
|
|
13
|
+
|
|
14
|
+
// Default config (override via environment or config file)
|
|
15
|
+
const CONFIG = {
|
|
16
|
+
agentName: process.env.AI2AI_AGENT_NAME || 'my-assistant',
|
|
17
|
+
humanName: process.env.AI2AI_HUMAN_NAME || 'Human',
|
|
18
|
+
timezone: process.env.AI2AI_TIMEZONE || 'UTC',
|
|
19
|
+
enableEncryption: process.env.AI2AI_ENCRYPTION !== 'false', // default on
|
|
20
|
+
enableQueue: process.env.AI2AI_QUEUE !== 'false', // default on
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a new AI2AI message envelope
|
|
25
|
+
* Supports multiple recipients via `to` array.
|
|
26
|
+
*/
|
|
27
|
+
function createEnvelope({ to, type, intent, payload, conversationId, requiresHumanApproval, participants }) {
|
|
28
|
+
const keys = loadOrCreateKeys();
|
|
29
|
+
|
|
30
|
+
// Normalize `to` — support single object or array
|
|
31
|
+
const toField = Array.isArray(to) ? to : to;
|
|
32
|
+
|
|
33
|
+
const envelope = {
|
|
34
|
+
ai2ai: '0.1',
|
|
35
|
+
id: crypto.randomUUID(),
|
|
36
|
+
timestamp: new Date().toISOString(),
|
|
37
|
+
from: {
|
|
38
|
+
agent: CONFIG.agentName,
|
|
39
|
+
node: `${CONFIG.agentName}-node`,
|
|
40
|
+
human: CONFIG.humanName,
|
|
41
|
+
},
|
|
42
|
+
to: toField,
|
|
43
|
+
conversation: conversationId || crypto.randomUUID(),
|
|
44
|
+
type,
|
|
45
|
+
intent: intent || null,
|
|
46
|
+
payload: payload || {},
|
|
47
|
+
requires_human_approval: requiresHumanApproval !== false,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Add participants for group conversations
|
|
51
|
+
if (participants) {
|
|
52
|
+
envelope.participants = participants;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Sign the message
|
|
56
|
+
envelope.signature = signMessage(envelope, keys.privateKey);
|
|
57
|
+
|
|
58
|
+
return envelope;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Optionally encrypt the payload field of an envelope
|
|
63
|
+
* Returns the envelope (mutated with encrypted payload, or unchanged)
|
|
64
|
+
*/
|
|
65
|
+
function maybeEncryptEnvelope(envelope) {
|
|
66
|
+
if (!CONFIG.enableEncryption) return envelope;
|
|
67
|
+
|
|
68
|
+
// Get recipient's X25519 public key from contacts
|
|
69
|
+
const recipientAgent = Array.isArray(envelope.to) ? envelope.to[0]?.agent : envelope.to?.agent;
|
|
70
|
+
if (!recipientAgent) return envelope;
|
|
71
|
+
|
|
72
|
+
const contact = getContact(recipientAgent);
|
|
73
|
+
if (!contact?.x25519PublicKey) return envelope; // No encryption key → send signed-only
|
|
74
|
+
|
|
75
|
+
const encrypted = encryptPayloadX25519(envelope.payload, contact.x25519PublicKey);
|
|
76
|
+
if (encrypted) {
|
|
77
|
+
envelope.payload = encrypted;
|
|
78
|
+
envelope._payloadEncrypted = true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return envelope;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Raw send function (used by queue system too)
|
|
86
|
+
*/
|
|
87
|
+
async function rawSend(endpoint, envelope) {
|
|
88
|
+
const response = await fetch(endpoint, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: {
|
|
91
|
+
'Content-Type': 'application/json',
|
|
92
|
+
'X-AI2AI-Version': '0.1',
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify(envelope),
|
|
95
|
+
signal: AbortSignal.timeout(30000),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return response.json();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Send a message to another agent's endpoint
|
|
107
|
+
* With optional queuing for offline agents.
|
|
108
|
+
*/
|
|
109
|
+
async function sendMessage(endpoint, envelope, options = {}) {
|
|
110
|
+
const recipientAgent = Array.isArray(envelope.to) ? envelope.to[0]?.agent : envelope.to?.agent;
|
|
111
|
+
|
|
112
|
+
// Check if agent is blocked
|
|
113
|
+
if (recipientAgent && isBlocked(recipientAgent)) {
|
|
114
|
+
throw new Error(`Agent ${recipientAgent} is blocked`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Optionally encrypt
|
|
118
|
+
maybeEncryptEnvelope(envelope);
|
|
119
|
+
|
|
120
|
+
// Log outgoing
|
|
121
|
+
logger.logOutgoing(envelope);
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const data = await rawSend(endpoint, envelope);
|
|
125
|
+
|
|
126
|
+
// Update contact with last interaction
|
|
127
|
+
if (recipientAgent) {
|
|
128
|
+
upsertContact(recipientAgent, {
|
|
129
|
+
endpoint,
|
|
130
|
+
humanName: Array.isArray(envelope.to) ? envelope.to[0]?.human : envelope.to?.human,
|
|
131
|
+
lastInteraction: new Date().toISOString(),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return data;
|
|
136
|
+
} catch (err) {
|
|
137
|
+
// If queue is enabled, queue the message for retry
|
|
138
|
+
if (CONFIG.enableQueue && options.queue !== false) {
|
|
139
|
+
logger.warn('CLIENT', `Direct send failed, queuing: ${err.message}`, { endpoint });
|
|
140
|
+
|
|
141
|
+
const result = await queueAndSend(endpoint, envelope, rawSend, {
|
|
142
|
+
onFailure: options.onDeliveryFailure || ((entry) => {
|
|
143
|
+
logger.error('CLIENT', `All retries failed for message to ${recipientAgent}`, {
|
|
144
|
+
queueId: entry.id,
|
|
145
|
+
});
|
|
146
|
+
}),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return { status: 'queued', queueId: result.queueId, message: 'Agent offline, message queued for retry' };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
throw new Error(`Failed to reach ${endpoint}: ${err.message}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Send to multiple recipients (group conversations)
|
|
158
|
+
*/
|
|
159
|
+
async function sendToMultiple(endpoints, envelope, options = {}) {
|
|
160
|
+
const results = {};
|
|
161
|
+
for (const { agent, endpoint } of endpoints) {
|
|
162
|
+
try {
|
|
163
|
+
// Create per-recipient envelope (same conversation, different `to`)
|
|
164
|
+
const recipientEnvelope = { ...envelope, to: { agent, human: agent, node: 'unknown' } };
|
|
165
|
+
recipientEnvelope.signature = signMessage(recipientEnvelope, loadOrCreateKeys().privateKey);
|
|
166
|
+
results[agent] = await sendMessage(endpoint, recipientEnvelope, options);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
results[agent] = { status: 'error', error: err.message };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return results;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Send a ping/handshake to discover another agent
|
|
176
|
+
*/
|
|
177
|
+
async function ping(endpoint) {
|
|
178
|
+
const keys = loadOrCreateKeys();
|
|
179
|
+
const { getFingerprint } = require('./ai2ai-crypto');
|
|
180
|
+
const { supportedIntents } = require('./ai2ai-handlers');
|
|
181
|
+
const x25519Keys = loadOrCreateX25519Keys();
|
|
182
|
+
|
|
183
|
+
const envelope = createEnvelope({
|
|
184
|
+
to: { agent: 'unknown', node: 'unknown', human: 'unknown' },
|
|
185
|
+
type: 'ping',
|
|
186
|
+
payload: {
|
|
187
|
+
capabilities: supportedIntents(),
|
|
188
|
+
languages: ['en'],
|
|
189
|
+
timezone: CONFIG.timezone,
|
|
190
|
+
availability_hours: '09:00-22:00',
|
|
191
|
+
model_info: 'local',
|
|
192
|
+
protocol_versions: ['0.1'],
|
|
193
|
+
public_key: keys.publicKey,
|
|
194
|
+
fingerprint: getFingerprint(keys.publicKey),
|
|
195
|
+
x25519_public_key: x25519Keys.publicKeyDer,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return sendMessage(endpoint, envelope, { queue: false }); // Don't queue pings
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Request a meeting with another agent
|
|
204
|
+
*/
|
|
205
|
+
async function requestMeeting(endpoint, { subject, proposedTimes, durationMinutes, location, notes, flexibility, to }) {
|
|
206
|
+
const conversationId = crypto.randomUUID();
|
|
207
|
+
|
|
208
|
+
const envelope = createEnvelope({
|
|
209
|
+
to: to || { agent: 'unknown' },
|
|
210
|
+
type: 'request',
|
|
211
|
+
intent: 'schedule.meeting',
|
|
212
|
+
conversationId,
|
|
213
|
+
payload: {
|
|
214
|
+
subject,
|
|
215
|
+
proposed_times: proposedTimes,
|
|
216
|
+
duration_minutes: durationMinutes || 60,
|
|
217
|
+
location_preference: location || null,
|
|
218
|
+
notes: notes || null,
|
|
219
|
+
flexibility: flexibility || 'medium',
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Track conversation
|
|
224
|
+
createConversation(conversationId, {
|
|
225
|
+
intent: 'schedule.meeting',
|
|
226
|
+
initiator: envelope.from,
|
|
227
|
+
recipient: envelope.to,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
return sendMessage(endpoint, envelope);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Relay a message to another agent's human
|
|
235
|
+
*/
|
|
236
|
+
async function relayMessage(endpoint, { message, urgency, replyRequested, to }) {
|
|
237
|
+
const envelope = createEnvelope({
|
|
238
|
+
to: to || { agent: 'unknown' },
|
|
239
|
+
type: 'request',
|
|
240
|
+
intent: 'message.relay',
|
|
241
|
+
payload: {
|
|
242
|
+
message,
|
|
243
|
+
urgency: urgency || 'low',
|
|
244
|
+
reply_requested: replyRequested !== false,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
return sendMessage(endpoint, envelope);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Request information from another agent
|
|
253
|
+
*/
|
|
254
|
+
async function requestInfo(endpoint, { question, context, to }) {
|
|
255
|
+
const envelope = createEnvelope({
|
|
256
|
+
to: to || { agent: 'unknown' },
|
|
257
|
+
type: 'request',
|
|
258
|
+
intent: 'info.request',
|
|
259
|
+
payload: {
|
|
260
|
+
question,
|
|
261
|
+
context: context || null,
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
return sendMessage(endpoint, envelope);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Request a commerce quote
|
|
270
|
+
*/
|
|
271
|
+
async function requestQuote(endpoint, { item, description, quantity, budget, currency, notes, to }) {
|
|
272
|
+
const conversationId = crypto.randomUUID();
|
|
273
|
+
|
|
274
|
+
const envelope = createEnvelope({
|
|
275
|
+
to: to || { agent: 'unknown' },
|
|
276
|
+
type: 'request',
|
|
277
|
+
intent: 'commerce.request',
|
|
278
|
+
conversationId,
|
|
279
|
+
requiresHumanApproval: true,
|
|
280
|
+
payload: {
|
|
281
|
+
item,
|
|
282
|
+
description: description || null,
|
|
283
|
+
quantity: quantity || null,
|
|
284
|
+
budget: budget || null,
|
|
285
|
+
currency: currency || 'USD',
|
|
286
|
+
notes: notes || null,
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
createConversation(conversationId, {
|
|
291
|
+
intent: 'commerce.request',
|
|
292
|
+
initiator: envelope.from,
|
|
293
|
+
recipient: envelope.to,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
return sendMessage(endpoint, envelope);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Initiate a group scheduling conversation
|
|
301
|
+
*/
|
|
302
|
+
async function requestGroupMeeting(endpoints, { subject, proposedTimes, durationMinutes, location, notes, participants }) {
|
|
303
|
+
const conversationId = crypto.randomUUID();
|
|
304
|
+
|
|
305
|
+
const participantList = participants || endpoints.map(e => ({
|
|
306
|
+
agent: e.agent,
|
|
307
|
+
human: e.human || e.agent,
|
|
308
|
+
}));
|
|
309
|
+
|
|
310
|
+
// Add self
|
|
311
|
+
participantList.unshift({ agent: CONFIG.agentName, human: CONFIG.humanName });
|
|
312
|
+
|
|
313
|
+
const envelope = createEnvelope({
|
|
314
|
+
to: participantList.slice(1), // All recipients
|
|
315
|
+
type: 'request',
|
|
316
|
+
intent: 'schedule.group',
|
|
317
|
+
conversationId,
|
|
318
|
+
participants: participantList,
|
|
319
|
+
payload: {
|
|
320
|
+
subject,
|
|
321
|
+
proposed_times: proposedTimes,
|
|
322
|
+
duration_minutes: durationMinutes || 60,
|
|
323
|
+
location_preference: location || null,
|
|
324
|
+
notes: notes || null,
|
|
325
|
+
participants: participantList,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
createConversation(conversationId, {
|
|
330
|
+
intent: 'schedule.group',
|
|
331
|
+
initiator: envelope.from,
|
|
332
|
+
participants: participantList,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
return sendToMultiple(endpoints, envelope);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports = {
|
|
339
|
+
CONFIG,
|
|
340
|
+
createEnvelope,
|
|
341
|
+
sendMessage,
|
|
342
|
+
sendToMultiple,
|
|
343
|
+
rawSend,
|
|
344
|
+
maybeEncryptEnvelope,
|
|
345
|
+
ping,
|
|
346
|
+
requestMeeting,
|
|
347
|
+
relayMessage,
|
|
348
|
+
requestInfo,
|
|
349
|
+
requestQuote,
|
|
350
|
+
requestGroupMeeting,
|
|
351
|
+
};
|