a2acalling 0.1.3 → 0.1.4
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 +1 -1
- package/package.json +1 -1
- package/src/server.js +88 -67
package/README.md
CHANGED
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -85,9 +85,74 @@ console.log(`[a2a] Agent: ${agentContext.name} (${agentContext.owner}'s agent)`)
|
|
|
85
85
|
console.log(`[a2a] API: ${apiConfig ? `${apiConfig.provider} ✓` : 'NOT FOUND ✗'}`);
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* Call
|
|
88
|
+
* Call agent via OpenClaw sub-agent (full tool access)
|
|
89
89
|
*/
|
|
90
90
|
async function callAgent(message, federationContext) {
|
|
91
|
+
const { execSync } = require('child_process');
|
|
92
|
+
|
|
93
|
+
const callerName = federationContext.caller?.name || 'Unknown Agent';
|
|
94
|
+
const callerOwner = federationContext.caller?.owner || '';
|
|
95
|
+
const ownerInfo = callerOwner ? ` (${callerOwner}'s agent)` : '';
|
|
96
|
+
const tierInfo = federationContext.tier || 'public';
|
|
97
|
+
const topics = federationContext.allowed_topics?.join(', ') || 'general chat';
|
|
98
|
+
const disclosure = federationContext.disclosure || 'minimal';
|
|
99
|
+
|
|
100
|
+
// Build the federation context for the sub-agent
|
|
101
|
+
const prompt = `[A2A Federation Call]
|
|
102
|
+
From: ${callerName}${ownerInfo}
|
|
103
|
+
Access Level: ${tierInfo}
|
|
104
|
+
Topics: ${topics}
|
|
105
|
+
Disclosure: ${disclosure}
|
|
106
|
+
|
|
107
|
+
Message: ${message}
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
Respond to this federated agent call. Be yourself - collaborative but protect private info based on disclosure level. Keep response concise (under 500 chars).`;
|
|
111
|
+
|
|
112
|
+
// Use a unique session ID for this conversation
|
|
113
|
+
const sessionId = `a2a-${federationContext.conversation_id || Date.now()}`;
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
// Write prompt to temp file to avoid shell escaping issues
|
|
117
|
+
const tmpFile = `/tmp/a2a-${Date.now()}.txt`;
|
|
118
|
+
fs.writeFileSync(tmpFile, prompt);
|
|
119
|
+
|
|
120
|
+
// Call openclaw agent to spawn a sub-agent
|
|
121
|
+
const result = execSync(
|
|
122
|
+
`cat "${tmpFile}" | openclaw agent --session-id "${sessionId}" --timeout 55 2>/dev/null`,
|
|
123
|
+
{
|
|
124
|
+
encoding: 'utf8',
|
|
125
|
+
timeout: 60000,
|
|
126
|
+
maxBuffer: 1024 * 1024,
|
|
127
|
+
cwd: process.env.OPENCLAW_WORKSPACE || '/root/clawd',
|
|
128
|
+
env: { ...process.env, FORCE_COLOR: '0' }
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Clean up temp file
|
|
133
|
+
try { fs.unlinkSync(tmpFile); } catch (e) {}
|
|
134
|
+
|
|
135
|
+
// Filter out plugin registration messages and return clean response
|
|
136
|
+
const lines = result.split('\n').filter(line =>
|
|
137
|
+
!line.includes('[telegram-topic-tracker]') &&
|
|
138
|
+
!line.includes('Plugin registered') &&
|
|
139
|
+
line.trim()
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
return lines.join('\n').trim() || '[No response]';
|
|
143
|
+
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error('[a2a] Sub-agent spawn failed:', err.message);
|
|
146
|
+
|
|
147
|
+
// Fallback to direct API call
|
|
148
|
+
return await callAgentDirect(message, federationContext);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Fallback: Call LLM directly via OpenRouter
|
|
154
|
+
*/
|
|
155
|
+
async function callAgentDirect(message, federationContext) {
|
|
91
156
|
if (!apiConfig) {
|
|
92
157
|
return '[Agent configuration error: No API key available]';
|
|
93
158
|
}
|
|
@@ -109,47 +174,26 @@ Disclosure level: ${federationContext.disclosure || 'minimal'}
|
|
|
109
174
|
|
|
110
175
|
Respond naturally as yourself. Be collaborative but protect your owner's private information based on the disclosure level. Keep responses concise.`;
|
|
111
176
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
model,
|
|
121
|
-
max_tokens: 1024,
|
|
122
|
-
messages: [
|
|
123
|
-
{ role: 'system', content: systemPrompt },
|
|
124
|
-
{ role: 'user', content: message }
|
|
125
|
-
]
|
|
126
|
-
})
|
|
127
|
-
: JSON.stringify({
|
|
128
|
-
model,
|
|
129
|
-
max_tokens: 1024,
|
|
130
|
-
system: systemPrompt,
|
|
131
|
-
messages: [{ role: 'user', content: message }]
|
|
132
|
-
});
|
|
177
|
+
const body = JSON.stringify({
|
|
178
|
+
model: 'anthropic/claude-sonnet-4',
|
|
179
|
+
max_tokens: 1024,
|
|
180
|
+
messages: [
|
|
181
|
+
{ role: 'system', content: systemPrompt },
|
|
182
|
+
{ role: 'user', content: message }
|
|
183
|
+
]
|
|
184
|
+
});
|
|
133
185
|
|
|
134
|
-
|
|
135
|
-
|
|
186
|
+
return new Promise((resolve) => {
|
|
187
|
+
const req = https.request({
|
|
188
|
+
hostname: 'openrouter.ai',
|
|
189
|
+
path: '/api/v1/chat/completions',
|
|
190
|
+
method: 'POST',
|
|
191
|
+
headers: {
|
|
136
192
|
'Content-Type': 'application/json',
|
|
137
193
|
'Authorization': `Bearer ${apiConfig.key}`,
|
|
138
194
|
'HTTP-Referer': 'https://openclaw.ai',
|
|
139
195
|
'X-Title': 'A2A Federation'
|
|
140
|
-
}
|
|
141
|
-
: {
|
|
142
|
-
'Content-Type': 'application/json',
|
|
143
|
-
'x-api-key': apiConfig.key,
|
|
144
|
-
'anthropic-version': '2023-06-01'
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
return new Promise((resolve) => {
|
|
148
|
-
const req = https.request({
|
|
149
|
-
hostname,
|
|
150
|
-
path: apiPath,
|
|
151
|
-
method: 'POST',
|
|
152
|
-
headers,
|
|
196
|
+
},
|
|
153
197
|
timeout: 55000
|
|
154
198
|
}, (res) => {
|
|
155
199
|
let data = '';
|
|
@@ -157,44 +201,21 @@ Respond naturally as yourself. Be collaborative but protect your owner's private
|
|
|
157
201
|
res.on('end', () => {
|
|
158
202
|
try {
|
|
159
203
|
const json = JSON.parse(data);
|
|
160
|
-
|
|
161
|
-
// OpenRouter format
|
|
162
204
|
if (json.choices && json.choices[0]?.message?.content) {
|
|
163
205
|
resolve(json.choices[0].message.content);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (json.content && json.content[0]?.text) {
|
|
169
|
-
resolve(json.content[0].text);
|
|
170
|
-
return;
|
|
206
|
+
} else if (json.error) {
|
|
207
|
+
resolve(`[Error: ${json.error.message || 'Unknown'}]`);
|
|
208
|
+
} else {
|
|
209
|
+
resolve('[No response]');
|
|
171
210
|
}
|
|
172
|
-
|
|
173
|
-
if (json.error) {
|
|
174
|
-
console.error('[a2a] API error:', json.error);
|
|
175
|
-
resolve(`[Agent error: ${json.error.message || JSON.stringify(json.error)}]`);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
console.error('[a2a] Unexpected response:', JSON.stringify(json).slice(0, 200));
|
|
180
|
-
resolve('[No response generated]');
|
|
181
211
|
} catch (e) {
|
|
182
|
-
|
|
183
|
-
resolve('[Agent response parsing error]');
|
|
212
|
+
resolve('[Parse error]');
|
|
184
213
|
}
|
|
185
214
|
});
|
|
186
215
|
});
|
|
187
216
|
|
|
188
|
-
req.on('error', (
|
|
189
|
-
|
|
190
|
-
resolve(`[Agent temporarily unavailable: ${e.message}]`);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
req.on('timeout', () => {
|
|
194
|
-
req.destroy();
|
|
195
|
-
resolve('[Agent response timeout]');
|
|
196
|
-
});
|
|
197
|
-
|
|
217
|
+
req.on('error', () => resolve('[Agent unavailable]'));
|
|
218
|
+
req.on('timeout', () => { req.destroy(); resolve('[Timeout]'); });
|
|
198
219
|
req.write(body);
|
|
199
220
|
req.end();
|
|
200
221
|
});
|