a2acalling 0.2.2 โ†’ 0.3.1

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/bin/cli.js CHANGED
@@ -99,7 +99,7 @@ const commands = {
99
99
  name: args.flags.name || args.flags.n || 'unnamed',
100
100
  owner: args.flags.owner || args.flags.o || null,
101
101
  expires: args.flags.expires || args.flags.e || 'never',
102
- permissions: args.flags.permissions || args.flags.p || 'chat-only',
102
+ permissions: args.flags.permissions || args.flags.p || 'public',
103
103
  disclosure: args.flags.disclosure || args.flags.d || 'minimal',
104
104
  notify: args.flags.notify || 'all',
105
105
  maxCalls,
@@ -256,7 +256,7 @@ https://github.com/onthegonow/a2a_calling`;
256
256
  let permBadge = '';
257
257
  if (r.linked_token) {
258
258
  const tier = r.linked_token.tier || r.linked_token.permissions;
259
- permBadge = tier === 'tools-write' ? ' โšก' : tier === 'tools-read' ? ' ๐Ÿ”ง' : ' ๐ŸŒ';
259
+ permBadge = tier === 'family' ? ' โšก' : tier === 'friends' ? ' ๐Ÿ”ง' : ' ๐ŸŒ';
260
260
  }
261
261
 
262
262
  console.log(`${statusIcon} ${r.name}${ownerText}${permBadge}`);
@@ -270,7 +270,7 @@ https://github.com/onthegonow/a2a_calling`;
270
270
  console.log();
271
271
  }
272
272
 
273
- console.log('Legend: ๐ŸŒ chat-only ๐Ÿ”ง tools-read โšก tools-write');
273
+ console.log('Legend: ๐ŸŒ public ๐Ÿ”ง friends โšก family');
274
274
  },
275
275
 
276
276
  'contacts:add': (args) => {
@@ -343,7 +343,7 @@ https://github.com/onthegonow/a2a_calling`;
343
343
  const t = remote.linked_token;
344
344
  const tier = t.tier || t.permissions;
345
345
  const topics = t.allowed_topics || ['chat'];
346
- const tierIcon = tier === 'tools-write' ? 'โšก' : tier === 'tools-read' ? '๐Ÿ”ง' : '๐ŸŒ';
346
+ const tierIcon = tier === 'family' ? 'โšก' : tier === 'friends' ? '๐Ÿ”ง' : '๐ŸŒ';
347
347
  console.log(`๐Ÿ” Your token to them: ${t.id}`);
348
348
  console.log(` Tier: ${tierIcon} ${tier}`);
349
349
  console.log(` Topics: ${topics.join(', ')}`);
@@ -432,8 +432,8 @@ https://github.com/onthegonow/a2a_calling`;
432
432
  process.exit(1);
433
433
  }
434
434
 
435
- const permLabel = result.token.permissions === 'tools-write' ? 'โšก tools-write' :
436
- result.token.permissions === 'tools-read' ? '๐Ÿ”ง tools-read' : '๐ŸŒ chat-only';
435
+ const permLabel = result.token.tier === 'family' ? 'โšก family' :
436
+ result.token.tier === 'friends' ? '๐Ÿ”ง friends' : '๐ŸŒ public';
437
437
 
438
438
  console.log(`โœ… Linked token to contact`);
439
439
  console.log(` Contact: ${result.remote.name}`);
@@ -770,7 +770,7 @@ https://github.com/onthegonow/a2a_calling`;
770
770
  name,
771
771
  owner,
772
772
  expires: '7d',
773
- permissions: 'chat-only',
773
+ permissions: 'public',
774
774
  maxCalls: 100
775
775
  });
776
776
 
@@ -823,7 +823,7 @@ Commands:
823
823
  --name, -n Token/agent name
824
824
  --owner, -o Owner name (human behind the agent)
825
825
  --expires, -e Expiration (1h, 1d, 7d, 30d, never)
826
- --permissions, -p Tier (chat-only, tools-read, tools-write)
826
+ --permissions, -p Tier (public, friends, family)
827
827
  --topics Custom topics (comma-separated, overrides tier defaults)
828
828
  --disclosure, -d Disclosure level (public, minimal, none)
829
829
  --notify Owner notification (all, summary, none)
@@ -847,7 +847,7 @@ Contacts:
847
847
  contacts ping <n> Ping contact, update status
848
848
  contacts rm <n> Remove contact
849
849
 
850
- Permission badges: ๐ŸŒ chat-only ๐Ÿ”ง tools-read โšก tools-write
850
+ Permission badges: ๐ŸŒ public ๐Ÿ”ง friends โšก family
851
851
 
852
852
  Conversations:
853
853
  conversations List all conversations
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2acalling",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -272,7 +272,7 @@ function renderInvites() {
272
272
  tr.innerHTML = `
273
273
  <td>${invite.id}</td>
274
274
  <td>${invite.name || '-'}</td>
275
- <td>${invite.tier_label || invite.tier || '-'}</td>
275
+ <td>${invite.tier || '-'}</td>
276
276
  <td>${invite.calls_made || 0}${invite.max_calls ? `/${invite.max_calls}` : ''}</td>
277
277
  <td>${fmtDate(invite.expires_at)}</td>
278
278
  <td>${invite.revoked ? 'revoked' : 'active'}</td>
package/src/lib/config.js CHANGED
@@ -22,7 +22,7 @@ const DEFAULT_CONFIG = {
22
22
  public: {
23
23
  name: 'Public',
24
24
  description: 'Basic networking - safe for anyone',
25
- capabilities: [],
25
+ capabilities: ['context-read'],
26
26
  topics: [],
27
27
  goals: [],
28
28
  disclosure: 'minimal',
@@ -31,7 +31,7 @@ const DEFAULT_CONFIG = {
31
31
  friends: {
32
32
  name: 'Friends',
33
33
  description: 'Most capabilities, no sensitive financial data',
34
- capabilities: [],
34
+ capabilities: ['context-read', 'calendar.read', 'email.read', 'search'],
35
35
  topics: [],
36
36
  goals: [],
37
37
  disclosure: 'public',
@@ -40,7 +40,7 @@ const DEFAULT_CONFIG = {
40
40
  private: {
41
41
  name: 'Private',
42
42
  description: 'Full access - only for you',
43
- capabilities: [],
43
+ capabilities: ['context-read', 'calendar', 'email', 'search', 'tools', 'memory'],
44
44
  topics: [],
45
45
  goals: [],
46
46
  disclosure: 'public',
@@ -49,7 +49,7 @@ const DEFAULT_CONFIG = {
49
49
  custom: {
50
50
  name: 'Custom',
51
51
  description: 'User-defined permissions',
52
- capabilities: [],
52
+ capabilities: ['context-read'],
53
53
  topics: [],
54
54
  goals: [],
55
55
  disclosure: 'minimal',
package/src/lib/tokens.js CHANGED
@@ -90,16 +90,20 @@ class TokenStore {
90
90
  name = 'unnamed',
91
91
  owner = null,
92
92
  expires = '1d',
93
- permissions = 'chat-only',
93
+ permissions = 'public',
94
94
  disclosure = 'minimal',
95
95
  notify = 'all',
96
96
  maxCalls = 100, // Default limit, not unlimited
97
+ capabilities = null, // Array of capability strings, snapshotted at creation
97
98
  // Snapshot of actual capabilities at creation time
98
99
  allowedTopics = null, // Array of topic strings, e.g. ['chat', 'calendar.read']
99
100
  allowedGoals = null, // Array of goal strings, e.g. ['grow-network', 'find-collaborators']
100
101
  tierSettings = null // Object with tier-specific settings
101
102
  } = options;
102
103
 
104
+ // Map legacy tier values to labels
105
+ const tier = TokenStore.LEGACY_TIER_MAP[permissions] || permissions;
106
+
103
107
  const token = TokenStore.generateToken();
104
108
  const tokenHash = TokenStore.hashToken(token);
105
109
  const durationMs = TokenStore.parseDuration(expires);
@@ -119,34 +123,25 @@ class TokenStore {
119
123
  // Config not available, use defaults
120
124
  }
121
125
 
122
- // Default topics based on permissions tier (snapshot at creation)
126
+ // Default topics based on tier label (snapshot at creation)
123
127
  // User config overrides these defaults
124
128
  const defaultTopics = {
125
- 'chat-only': ['chat'],
126
129
  'public': configTiers.public?.topics || ['chat'],
127
- 'tools-read': ['chat', 'calendar.read', 'email.read', 'search'],
128
130
  'friends': configTiers.friends?.topics || ['chat', 'calendar.read', 'email.read', 'search'],
129
- 'tools-write': ['chat', 'calendar', 'email', 'search', 'tools'],
130
131
  'family': configTiers.family?.topics || ['chat', 'calendar', 'email', 'search', 'tools']
131
132
  };
132
133
 
133
- // Default goals based on permissions tier (snapshot at creation)
134
+ // Default goals based on tier label (snapshot at creation)
134
135
  const defaultGoals = {
135
- 'chat-only': [],
136
136
  'public': configTiers.public?.goals || [],
137
- 'tools-read': [],
138
137
  'friends': configTiers.friends?.goals || [],
139
- 'tools-write': [],
140
138
  'family': configTiers.family?.goals || []
141
139
  };
142
140
 
143
- // Normalize tier name
144
- const tierAliases = {
145
- 'public': 'chat-only',
146
- 'friends': 'tools-read',
147
- 'family': 'tools-write'
148
- };
149
- const normalizedTier = tierAliases[permissions] || permissions;
141
+ // Resolve capabilities: explicit > config > defaults
142
+ const defaultCapabilities = (configTiers[tier]?.capabilities?.length)
143
+ ? configTiers[tier].capabilities
144
+ : (TokenStore.DEFAULT_CAPABILITIES[tier] || ['context-read']);
150
145
 
151
146
  // Use separate random ID (not derived from token) to prevent prefix attacks
152
147
  const record = {
@@ -154,10 +149,10 @@ class TokenStore {
154
149
  token_hash: tokenHash,
155
150
  name,
156
151
  owner,
157
- tier: normalizedTier, // Normalized tier (chat-only, tools-read, tools-write)
158
- tier_label: permissions, // Original label (public, friends, family)
159
- allowed_topics: allowedTopics || defaultTopics[permissions] || ['chat'],
160
- allowed_goals: allowedGoals || defaultGoals[permissions] || [],
152
+ tier,
153
+ capabilities: capabilities || defaultCapabilities,
154
+ allowed_topics: allowedTopics || defaultTopics[tier] || ['chat'],
155
+ allowed_goals: allowedGoals || defaultGoals[tier] || [],
161
156
  tier_settings: tierSettings || {}, // Snapshot of settings at creation
162
157
  disclosure,
163
158
  notify,
@@ -220,11 +215,20 @@ class TokenStore {
220
215
  record.last_used = new Date().toISOString();
221
216
  this._save(db);
222
217
 
218
+ // Map legacy tier values to labels for old records
219
+ const tier = TokenStore.LEGACY_TIER_MAP[record.tier] || record.tier || record.permissions || 'public';
220
+
221
+ // Resolve capabilities: stored > defaults
222
+ const capabilities = record.capabilities
223
+ || TokenStore.DEFAULT_CAPABILITIES[tier]
224
+ || ['context-read'];
225
+
223
226
  return {
224
227
  valid: true,
225
228
  id: record.id,
226
229
  name: record.name,
227
- tier: record.tier || record.permissions, // backward compat
230
+ tier,
231
+ capabilities,
228
232
  allowed_topics: record.allowed_topics || ['chat'],
229
233
  allowed_goals: record.allowed_goals || [],
230
234
  tier_settings: record.tier_settings || {},
@@ -458,4 +462,18 @@ class TokenStore {
458
462
  }
459
463
  }
460
464
 
465
+ // Legacy tier values from old records โ†’ label mapping
466
+ TokenStore.LEGACY_TIER_MAP = {
467
+ 'chat-only': 'public',
468
+ 'tools-read': 'friends',
469
+ 'tools-write': 'family'
470
+ };
471
+
472
+ // Default capabilities per tier label (used when config has none)
473
+ TokenStore.DEFAULT_CAPABILITIES = {
474
+ 'public': ['context-read'],
475
+ 'friends': ['context-read', 'calendar.read', 'email.read', 'search'],
476
+ 'family': ['context-read', 'calendar', 'email', 'search', 'tools', 'memory']
477
+ };
478
+
461
479
  module.exports = { TokenStore };
package/src/routes/a2a.js CHANGED
@@ -229,6 +229,7 @@ function createRoutes(options = {}) {
229
229
  token_id: validation.id,
230
230
  token_name: validation.name,
231
231
  tier: validation.tier,
232
+ capabilities: validation.capabilities,
232
233
  allowed_topics: validation.allowed_topics,
233
234
  disclosure: validation.disclosure,
234
235
  caller: sanitizedCaller,
@@ -84,21 +84,22 @@ function parseTopicObjects(values) {
84
84
  return cleaned;
85
85
  }
86
86
 
87
- function inferPermissionForTier(tierId) {
88
- const alias = {
89
- public: 'chat-only',
90
- friends: 'tools-read',
91
- family: 'tools-write',
92
- private: 'tools-write'
93
- };
94
- return alias[tierId] || tierId;
95
- }
96
-
97
- function formatInviteMessage({ owner, agentName, inviteUrl, topics, expiresText }) {
87
+ function formatInviteMessage({ owner, agentName, inviteUrl, topics, goals, expiresText }) {
98
88
  const ownerText = owner || 'Someone';
99
- const topicsText = topics.length > 0 ? topics.join(', ') : 'chat';
100
- const expirationLine = expiresText === 'never' ? '' : `\n\nExpires: ${expiresText}`;
101
- return `A2A invite from ${ownerText}\n\nYour agent can reach ${agentName} for: ${topicsText}\n\n${inviteUrl}${expirationLine}`;
89
+ const topicsList = topics.length > 0 ? topics.join(' ยท ') : 'chat';
90
+ const goalsList = (goals || []).join(' ยท ');
91
+ const expirationLine = expiresText === 'never' ? '' : `\nโฐ ${expiresText}`;
92
+ return `๐Ÿ“ž๐Ÿ—ฃ๏ธ **Agent-to-Agent Call Invite**
93
+
94
+ ๐Ÿ‘ค **${ownerText}** would like your agent to call **${agentName}** and explore where our owners might collaborate.
95
+
96
+ ๐Ÿ’ฌ ${topicsList}${goalsList ? `\n๐ŸŽฏ ${goalsList}` : ''}
97
+
98
+ ${inviteUrl}${expirationLine}
99
+
100
+ โ”€โ”€ setup โ”€โ”€
101
+ npm i -g a2acalling && a2a add "${inviteUrl}" "${agentName}" && a2a call "${agentName}" "Hello from my owner!"
102
+ https://github.com/onthegonow/a2a_calling`;
102
103
  }
103
104
 
104
105
  function buildContext(options = {}) {
@@ -485,13 +486,11 @@ function createDashboardApiRouter(options = {}) {
485
486
 
486
487
  const allowedTopics = sanitizeStringArray(body.topics || tier.topics || []);
487
488
  const allowedGoals = sanitizeStringArray(body.goals || tier.goals || []);
488
- const permission = inferPermissionForTier(tierId);
489
-
490
489
  const { token, record } = context.tokenStore.create({
491
490
  name,
492
491
  owner,
493
492
  expires,
494
- permissions: permission,
493
+ permissions: tierId,
495
494
  disclosure,
496
495
  notify,
497
496
  maxCalls,
@@ -511,6 +510,7 @@ function createDashboardApiRouter(options = {}) {
511
510
  agentName: name,
512
511
  inviteUrl,
513
512
  topics: record.allowed_topics || [],
513
+ goals: record.allowed_goals || [],
514
514
  expiresText
515
515
  });
516
516