a2acalling 0.2.2 → 0.3.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/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-readtools-write');
273
+ console.log('Legend: 🌐 public 🔧 friendsfamily');
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-readtools-write
850
+ Permission badges: 🌐 public 🔧 friendsfamily
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.0",
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,16 +84,6 @@ 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
87
  function formatInviteMessage({ owner, agentName, inviteUrl, topics, expiresText }) {
98
88
  const ownerText = owner || 'Someone';
99
89
  const topicsText = topics.length > 0 ? topics.join(', ') : 'chat';
@@ -485,13 +475,11 @@ function createDashboardApiRouter(options = {}) {
485
475
 
486
476
  const allowedTopics = sanitizeStringArray(body.topics || tier.topics || []);
487
477
  const allowedGoals = sanitizeStringArray(body.goals || tier.goals || []);
488
- const permission = inferPermissionForTier(tierId);
489
-
490
478
  const { token, record } = context.tokenStore.create({
491
479
  name,
492
480
  owner,
493
481
  expires,
494
- permissions: permission,
482
+ permissions: tierId,
495
483
  disclosure,
496
484
  notify,
497
485
  maxCalls,