a2acalling 0.6.5 → 0.6.6
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 +13 -0
- package/SKILL.md +29 -1
- package/bin/cli.js +826 -439
- package/docs/plans/2026-02-14-agent-driven-disclosure-extraction.md +986 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +25 -0
- package/src/lib/disclosure.js +171 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "a2acalling",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"a2acalling": "scripts/install-openclaw.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
+
"postinstall": "node scripts/postinstall.js",
|
|
11
12
|
"start": "node src/server.js",
|
|
12
13
|
"test": "node test/run.js"
|
|
13
14
|
},
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Only show the banner for global installs; skip in CI, dev, and Docker builds.
|
|
4
|
+
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) process.exit(0);
|
|
5
|
+
if (process.env.npm_config_global !== 'true') process.exit(0);
|
|
6
|
+
|
|
7
|
+
console.log(`
|
|
8
|
+
╔══════════════════════════════════════════════════════════╗
|
|
9
|
+
║ 🤝 A2A Calling installed successfully! ║
|
|
10
|
+
╠══════════════════════════════════════════════════════════╣
|
|
11
|
+
║ ║
|
|
12
|
+
║ Next step: ║
|
|
13
|
+
║ a2a quickstart --hostname YOUR_DOMAIN:PORT ║
|
|
14
|
+
║ ║
|
|
15
|
+
║ Example: ║
|
|
16
|
+
║ a2a quickstart --hostname myserver.com:3001 ║
|
|
17
|
+
║ ║
|
|
18
|
+
║ This will: ║
|
|
19
|
+
║ • Configure your agent's disclosure topics ║
|
|
20
|
+
║ • Set up permission tiers (public/friends/family) ║
|
|
21
|
+
║ • Start the A2A server ║
|
|
22
|
+
║ • Generate your first invite to share ║
|
|
23
|
+
║ ║
|
|
24
|
+
╚══════════════════════════════════════════════════════════╝
|
|
25
|
+
`);
|
package/src/lib/disclosure.js
CHANGED
|
@@ -17,6 +17,62 @@ const MANIFEST_FILE = path.join(CONFIG_DIR, 'a2a-disclosure.json');
|
|
|
17
17
|
|
|
18
18
|
const TIER_HIERARCHY = ['public', 'friends', 'family'];
|
|
19
19
|
const logger = createLogger({ component: 'a2a.disclosure' });
|
|
20
|
+
const SKIP_FILES = new Set(['heartbeat', 'skill', 'claude']);
|
|
21
|
+
|
|
22
|
+
function normalizeTopic(raw) {
|
|
23
|
+
return String(raw || '').trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function dedupeByTopic(items) {
|
|
27
|
+
const seen = new Set();
|
|
28
|
+
const out = [];
|
|
29
|
+
for (const item of items) {
|
|
30
|
+
const topic = normalizeTopic(item && item.topic);
|
|
31
|
+
if (!topic || seen.has(topic.toLowerCase())) continue;
|
|
32
|
+
seen.add(topic.toLowerCase());
|
|
33
|
+
out.push({
|
|
34
|
+
topic,
|
|
35
|
+
detail: normalizeTopic(item && item.detail)
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseTopicLine(rawLine) {
|
|
42
|
+
const line = normalizeTopic(rawLine);
|
|
43
|
+
if (!line) return null;
|
|
44
|
+
|
|
45
|
+
const splitPoint = line.search(/\s+[-–—:]\s+/);
|
|
46
|
+
if (splitPoint > 10) {
|
|
47
|
+
const topic = normalizeTopic(line.slice(0, splitPoint));
|
|
48
|
+
const detail = normalizeTopic(line.slice(splitPoint + 3));
|
|
49
|
+
return { topic, detail };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { topic: line, detail: '' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isValidTopic(line) {
|
|
56
|
+
if (!line || line.length < 5) return false;
|
|
57
|
+
if (line.includes('`')) return false;
|
|
58
|
+
if (line.includes('http')) return false;
|
|
59
|
+
if (line.includes('**:')) return false;
|
|
60
|
+
if (line.startsWith('//')) return false;
|
|
61
|
+
if (line.includes('()')) return false;
|
|
62
|
+
if (/\d{4}-\d{2}-\d{2}/.test(line)) return false;
|
|
63
|
+
if (line.toLowerCase().includes('todo')) return false;
|
|
64
|
+
if (line.toLowerCase().includes('fixme')) return false;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function truncateAtWordBoundary(text, max = 60) {
|
|
69
|
+
const normalized = normalizeTopic(text);
|
|
70
|
+
if (normalized.length <= max) return normalized;
|
|
71
|
+
|
|
72
|
+
const truncated = normalized.slice(0, max);
|
|
73
|
+
const lastSpace = truncated.lastIndexOf(' ');
|
|
74
|
+
return (lastSpace > 20 ? truncated.slice(0, lastSpace) : truncated) + '...';
|
|
75
|
+
}
|
|
20
76
|
|
|
21
77
|
/**
|
|
22
78
|
* Load manifest from disk. Returns {} if not found.
|
|
@@ -128,8 +184,108 @@ function formatTopicsForPrompt(tierTopics) {
|
|
|
128
184
|
* For proper topic extraction, use buildExtractionPrompt() to instruct
|
|
129
185
|
* an agent, then validate the result with validateDisclosureSubmission().
|
|
130
186
|
*/
|
|
131
|
-
function generateDefaultManifest() {
|
|
187
|
+
function generateDefaultManifest(contextFiles = {}) {
|
|
132
188
|
const now = new Date().toISOString();
|
|
189
|
+
const source = {};
|
|
190
|
+
const raw = contextFiles || {};
|
|
191
|
+
Object.keys(raw).forEach((key) => {
|
|
192
|
+
if (!SKIP_FILES.has(key.toLowerCase())) {
|
|
193
|
+
source[key] = raw[key];
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const userContent = String(source.user || '');
|
|
198
|
+
const soulContent = String(source.soul || '');
|
|
199
|
+
function extractFromSource(content, sectionNames) {
|
|
200
|
+
const sectionPattern = new RegExp(
|
|
201
|
+
`##\\s*(?:${sectionNames.map(name => name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})[^\\n]*\\n([\\s\\S]*?)(?=\\n##|$)`,
|
|
202
|
+
'i'
|
|
203
|
+
);
|
|
204
|
+
const match = String(content || '').match(sectionPattern);
|
|
205
|
+
if (!match) {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return String(match[1] || '')
|
|
210
|
+
.split('\n')
|
|
211
|
+
.map(line => normalizeTopic(line))
|
|
212
|
+
.filter(line => line.startsWith('-') || line.startsWith('*'))
|
|
213
|
+
.map(line => normalizeTopic(line.replace(/^[\s\-\*]+/, '')))
|
|
214
|
+
.map(parseTopicLine)
|
|
215
|
+
.filter(topic => topic && isValidTopic(topic.topic))
|
|
216
|
+
.map(topic => ({
|
|
217
|
+
topic: truncateAtWordBoundary(topic.topic, 60),
|
|
218
|
+
detail: truncateAtWordBoundary(topic.detail || '', 120)
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const candidateTopics = dedupeByTopic([
|
|
223
|
+
...extractFromSource(userContent, ['Goals', 'Interests', 'Projects', 'Current']),
|
|
224
|
+
...extractFromSource(soulContent, ['Goals', 'Interests', 'Projects', 'Current', 'Values', 'Personal'])
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
if (candidateTopics.length === 0) {
|
|
228
|
+
return {
|
|
229
|
+
version: 1,
|
|
230
|
+
generated_at: now,
|
|
231
|
+
updated_at: now,
|
|
232
|
+
topics: {
|
|
233
|
+
public: {
|
|
234
|
+
lead_with: [{ topic: 'What I do', detail: 'Brief professional description' }],
|
|
235
|
+
discuss_freely: [{ topic: 'General interests', detail: 'Non-sensitive topics and hobbies' }],
|
|
236
|
+
deflect: [{ topic: 'Personal details', detail: 'Redirect to direct owner contact' }]
|
|
237
|
+
},
|
|
238
|
+
friends: { lead_with: [], discuss_freely: [], deflect: [] },
|
|
239
|
+
family: { lead_with: [], discuss_freely: [], deflect: [] }
|
|
240
|
+
},
|
|
241
|
+
never_disclose: ['API keys', 'Other users\' data', 'Financial figures'],
|
|
242
|
+
personality_notes: 'Direct and technical. Prefers depth over breadth.'
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const publicLead = [];
|
|
247
|
+
const publicDiscuss = [];
|
|
248
|
+
const publicDeflect = [];
|
|
249
|
+
const friendsLead = [];
|
|
250
|
+
const friendsDiscuss = [];
|
|
251
|
+
const familyDiscuss = [];
|
|
252
|
+
|
|
253
|
+
candidateTopics.forEach((entry, index) => {
|
|
254
|
+
const topic = truncateAtWordBoundary(entry.topic || '', 60);
|
|
255
|
+
const detail = truncateAtWordBoundary(entry.detail || 'Open discussion topic.', 120);
|
|
256
|
+
if (!topic) return;
|
|
257
|
+
|
|
258
|
+
const node = { topic, detail };
|
|
259
|
+
if (index < 2) {
|
|
260
|
+
publicLead.push(node);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (index < 6) {
|
|
264
|
+
publicDiscuss.push(node);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (index < 8) {
|
|
268
|
+
friendsLead.push(node);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (index < 12) {
|
|
272
|
+
friendsDiscuss.push(node);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (index < 14) {
|
|
276
|
+
familyDiscuss.push(node);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (publicLead.length === 0) {
|
|
281
|
+
publicLead.push({ topic: 'Open source', detail: 'General product and engineering topics.' });
|
|
282
|
+
}
|
|
283
|
+
if (publicDiscuss.length === 0) {
|
|
284
|
+
publicDiscuss.push({ topic: 'Collaboration', detail: 'Ways to collaborate and support each other.' });
|
|
285
|
+
}
|
|
286
|
+
if (publicDeflect.length === 0) {
|
|
287
|
+
publicDeflect.push({ topic: 'Personal details', detail: 'Redirect to direct owner contact.' });
|
|
288
|
+
}
|
|
133
289
|
|
|
134
290
|
return {
|
|
135
291
|
version: 1,
|
|
@@ -137,15 +293,23 @@ function generateDefaultManifest() {
|
|
|
137
293
|
updated_at: now,
|
|
138
294
|
topics: {
|
|
139
295
|
public: {
|
|
140
|
-
lead_with:
|
|
141
|
-
discuss_freely:
|
|
142
|
-
deflect:
|
|
296
|
+
lead_with: publicLead,
|
|
297
|
+
discuss_freely: publicDiscuss,
|
|
298
|
+
deflect: publicDeflect
|
|
143
299
|
},
|
|
144
|
-
friends: {
|
|
145
|
-
|
|
300
|
+
friends: {
|
|
301
|
+
lead_with: friendsLead,
|
|
302
|
+
discuss_freely: friendsDiscuss,
|
|
303
|
+
deflect: []
|
|
304
|
+
},
|
|
305
|
+
family: {
|
|
306
|
+
lead_with: [],
|
|
307
|
+
discuss_freely: familyDiscuss,
|
|
308
|
+
deflect: []
|
|
309
|
+
}
|
|
146
310
|
},
|
|
147
311
|
never_disclose: ['API keys', 'Other users\' data', 'Financial figures'],
|
|
148
|
-
personality_notes: 'Direct and
|
|
312
|
+
personality_notes: 'Direct and practical. Open to collaboration with clear boundaries.'
|
|
149
313
|
};
|
|
150
314
|
}
|
|
151
315
|
|