a2acalling 0.6.72 → 0.6.73

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.6.72",
3
- "installed_at": "2026-02-27T03:05:52.375Z",
2
+ "version": "0.6.73",
3
+ "installed_at": "2026-02-27T06:20:51.170Z",
4
4
  "files": [
5
5
  {
6
6
  "path": "CLAUDE.md",
package/ARCHITECTURE.md CHANGED
@@ -55,6 +55,16 @@ A2A Calling enables agent-to-agent communication across OpenClaw instances. Agen
55
55
  - **Config**: JSON at `~/.config/openclaw/a2a-config.json`
56
56
  - **Disclosure**: JSON at `~/.config/openclaw/a2a-disclosure.json`
57
57
 
58
+ ## Database Lifecycle Management (A2A-55)
59
+
60
+ All three data stores have automatic retention cleanup that runs on server startup:
61
+
62
+ - **Conversations**: `ConversationStore.pruneOld()` compresses messages 7+ days old, deletes concluded/timeout conversations 90+ days old. Active conversations are never deleted.
63
+ - **Logs**: `LogStore.pruneOld()` deletes entries 30+ days old. Auto-prune triggers on every 1000th write (best effort). `pruneAllLoggerStores()` iterates all cached stores.
64
+ - **Tokens**: `TokenStore.cleanupExpired()` removes tokens expired >1 hour (grace for in-flight calls) and revoked tokens >30 days old.
65
+
66
+ All retention periods are configurable via `a2a-config.json` `retention` section. SQLite VACUUM runs only after >100 rows deleted. All cleanup is best-effort — failures are logged but never prevent server startup.
67
+
58
68
  ## Permission System
59
69
 
60
70
  Three tiers with escalating capabilities:
package/CONVENTIONS.md CHANGED
@@ -113,6 +113,19 @@ Tokens have a tier (`public`, `friends`, `family`) and a disclosure level (`publ
113
113
  - Admin token comparison uses `timingSafeTokenEqual()` from `src/routes/a2a.js` — do NOT use `!==` for secret comparison
114
114
  - Query parameter parsing follows the dashboard.js pattern: `Math.min(max, Math.max(min, Number.parseInt(String(value), 10) || defaultValue))`
115
115
 
116
+ ## Retention & Cleanup (A2A-55)
117
+
118
+ All data stores implement retention cleanup following the `dashboard-events.js` auto-prune pattern:
119
+
120
+ - **Cleanup component**: Use `createLogger({ component: 'a2a.cleanup' })` for all retention logging
121
+ - **Best effort**: Prune failures are caught and logged as warnings — never crash the server
122
+ - **VACUUM threshold**: Only run SQLite VACUUM after >100 rows deleted (costly I/O)
123
+ - **Auto-prune**: Logger store prunes on every 1000th `write()` call (counter-based, like dashboard-events.js)
124
+ - **Recursion safety**: Logger `pruneOld()` uses `_pruning` flag to prevent auto-prune during explicit prune
125
+ - **Server startup**: `src/server.js` calls all three retention mechanisms after `writePidFile()`, before `updateManager.start()`
126
+ - **Config defaults**: `A2AConfig.getRetention()` merges partial config with defaults — never writes defaults to disk
127
+ - **Token grace period**: Expired tokens are kept for 1 hour after expiry (in-flight call protection)
128
+
116
129
  ## Anti-Patterns
117
130
 
118
131
  - Do NOT use `console.log` — use the structured logger
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2acalling",
3
- "version": "0.6.72",
3
+ "version": "0.6.73",
4
4
  "description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -5,8 +5,10 @@
5
5
  * - openclaw: uses `openclaw` CLI for turn handling, summaries, notifications
6
6
  * - claude: uses `claude` CLI as a real LLM subagent for conversations
7
7
  *
8
+ * - test: minimal runtime for CI/headless — echoes messages or spawns A2A_AGENT_COMMAND
9
+ *
8
10
  * Selection:
9
- * - A2A_RUNTIME=openclaw|claude|auto (default: auto)
11
+ * - A2A_RUNTIME=openclaw|claude|test|auto (default: auto)
10
12
  * - auto picks openclaw → claude → error (no supported CLI)
11
13
  */
12
14
 
@@ -43,6 +45,18 @@ function resolveRuntimeMode() {
43
45
  const hasOpenClaw = commandExists('openclaw');
44
46
  const hasClaude = commandExists('claude');
45
47
 
48
+ // A2A-66: test runtime for CI/headless environments — minimal runTurn with
49
+ // optional A2A_AGENT_COMMAND bridge support.
50
+ if (requested === 'test') {
51
+ return {
52
+ mode: 'test',
53
+ requested,
54
+ hasOpenClaw,
55
+ hasClaude,
56
+ reason: 'A2A_RUNTIME=test'
57
+ };
58
+ }
59
+
46
60
  if (requested === 'generic') {
47
61
  return {
48
62
  mode: 'none',
@@ -372,6 +386,39 @@ function createRuntimeAdapter(options = {}) {
372
386
  }
373
387
  }
374
388
 
389
+ // A2A-66: test runtime — spawn A2A_AGENT_COMMAND if set, otherwise echo.
390
+ // Uses shell: true so the command string is parsed by the shell (supports
391
+ // quoted args, paths with spaces, pipes, etc.).
392
+ if (modeInfo.mode === 'test') {
393
+ const agentCommand = process.env.A2A_AGENT_COMMAND;
394
+ if (agentCommand) {
395
+ const payload = JSON.stringify({ message, caller, context });
396
+ const result = spawnSync(agentCommand, {
397
+ input: payload,
398
+ encoding: 'utf8',
399
+ shell: true,
400
+ timeout: (timeoutMs || 65000) + 5000,
401
+ maxBuffer: 1024 * 1024,
402
+ cwd: workspaceDir,
403
+ env: process.env
404
+ });
405
+ if (result.error) {
406
+ throw result.error;
407
+ }
408
+ // A2A-66: check exit code — non-zero means the bridge command failed.
409
+ if (result.status !== 0) {
410
+ const stderr = String(result.stderr || '').trim();
411
+ throw new Error(
412
+ `A2A_AGENT_COMMAND exited with code ${result.status}` +
413
+ (stderr ? `: ${stderr.slice(0, 200)}` : '')
414
+ );
415
+ }
416
+ return String(result.stdout || '').trim() || '[test-runtime] Empty command output';
417
+ }
418
+ const snippet = cleanText(message || prompt || '', 120);
419
+ return `[test-runtime] Echo: ${snippet}`;
420
+ }
421
+
375
422
  if (modeInfo.mode !== 'openclaw') {
376
423
  throw new Error(
377
424
  `No supported A2A runtime available (mode=${modeInfo.mode}). ` +
@@ -457,6 +504,12 @@ function createRuntimeAdapter(options = {}) {
457
504
  throw new Error('Claude summary returned empty result');
458
505
  }
459
506
 
507
+ // A2A-66: test runtime — return canned summary.
508
+ if (modeInfo.mode === 'test') {
509
+ const text = 'Test conversation concluded.';
510
+ return { summary: text, ownerSummary: text };
511
+ }
512
+
460
513
  if (modeInfo.mode !== 'openclaw') {
461
514
  throw new Error(
462
515
  `No supported A2A runtime available for summarization (mode=${modeInfo.mode}). ` +
@@ -526,14 +579,15 @@ function createRuntimeAdapter(options = {}) {
526
579
  data: { level }
527
580
  });
528
581
 
529
- if (modeInfo.mode === 'claude') {
530
- // Claude mode: notifications are a no-op (no notification transport available)
531
- logger.debug('Notification skipped (claude mode has no notification transport)', {
532
- event: 'notify_skipped_claude',
582
+ if (modeInfo.mode === 'claude' || modeInfo.mode === 'test') {
583
+ // Claude/test mode: notifications are a no-op (no notification transport available)
584
+ logger.debug('Notification skipped (no notification transport in this mode)', {
585
+ event: 'notify_skipped',
533
586
  traceId,
534
587
  requestId,
535
588
  conversationId,
536
- tokenId: token?.id
589
+ tokenId: token?.id,
590
+ data: { mode: modeInfo.mode }
537
591
  });
538
592
  return;
539
593
  }