a2acalling 0.6.68 → 0.6.69

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.68",
3
- "installed_at": "2026-02-25T19:26:27.869Z",
2
+ "version": "0.6.69",
3
+ "installed_at": "2026-02-26T05:13:54.868Z",
4
4
  "files": [
5
5
  {
6
6
  "path": "CLAUDE.md",
package/CONVENTIONS.md CHANGED
@@ -85,6 +85,22 @@ Dashboard API integration tests follow the pattern in `test/integration/dashboar
85
85
  - Call `loggerModule.closeAllLoggerStores()` in teardown to prevent SQLite handle leaks
86
86
  - Pass `convStore` directly via `options.convStore` when testing calls endpoints
87
87
 
88
+ ## Store Lifecycle (A2A-57)
89
+
90
+ All SQLite store classes (`ConversationStore`, `DashboardEventStore`, `CallbookStore`, `LoggerStore`) implement a `close()` method following this pattern:
91
+ ```js
92
+ close() {
93
+ if (this.db) {
94
+ try { this.db.close(); } catch (_) {}
95
+ this.db = null;
96
+ }
97
+ }
98
+ ```
99
+ - `close()` must be idempotent (safe to call multiple times)
100
+ - `close()` must be a no-op when DB was never initialized (`this.db === null`)
101
+ - The `server.js` `shutdown()` function closes all stores on SIGTERM/SIGINT
102
+ - Test teardown should call `store.close()` to prevent SQLite handle leaks
103
+
88
104
  ## Permission Tiers
89
105
 
90
106
  Tokens have a tier (`public`, `friends`, `family`) and a disclosure level (`public`, `minimal`, `none`). These are enforced at the route level in `src/routes/a2a.js`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2acalling",
3
- "version": "0.6.68",
3
+ "version": "0.6.69",
4
4
  "description": "Agent-to-agent calling for OpenClaw - A2A agent communication",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1307,7 +1307,6 @@ function renderPermissions() {
1307
1307
  renderTierWarnings(tier);
1308
1308
  renderSidebarPreview(state.activeTierId);
1309
1309
  renderSidebarLists(tier);
1310
- bindSidebarDrag();
1311
1310
  }
1312
1311
 
1313
1312
  // A2A-48: Renders the inline "Preview as Caller" card in the right sidebar.
@@ -1479,22 +1478,6 @@ function autoSaveTier() {
1479
1478
  }, 250);
1480
1479
  }
1481
1480
 
1482
- // A2A-48: Binds dragstart/dragend on sidebar items (re-created each render).
1483
- // Zone listeners (dragover/dragleave/drop) are bound ONCE in
1484
- // bindPermissionsActions() to avoid listener accumulation — the zone
1485
- // containers persist across renders while only their innerHTML changes.
1486
- function bindSidebarDrag() {
1487
- document.querySelectorAll('.sidebar-item[draggable="true"]').forEach(item => {
1488
- item.addEventListener('dragstart', (e) => {
1489
- const itemType = item.dataset.itemType || 'topic';
1490
- const name = item.dataset.sidebarTopic || item.dataset.sidebarGoal || '';
1491
- const desc = item.dataset.description || '';
1492
- e.dataTransfer.setData('application/json', JSON.stringify({ name, description: desc, type: itemType }));
1493
- item.style.opacity = '0.5';
1494
- });
1495
- item.addEventListener('dragend', () => { item.style.opacity = ''; });
1496
- });
1497
- }
1498
1481
 
1499
1482
  // A2A-50: Drop handler for active topic/goal zones. Routes items to the
1500
1483
  // CORRECT zone based on data.type (not the zone they were dropped on).
@@ -1537,9 +1520,6 @@ function handleZoneDrop(zone, e) {
1537
1520
  const freshTier = (state.settings?.tiers || []).find(t => t.id === state.activeTierId);
1538
1521
  if (freshTier) {
1539
1522
  renderSidebarLists(freshTier);
1540
- // A2A-51: Re-bind drag listeners after innerHTML replacement in renderSidebarLists().
1541
- // Without this, sidebar items lose dragstart/dragend handlers after the first drop.
1542
- bindSidebarDrag();
1543
1523
  }
1544
1524
  }, 300);
1545
1525
  }
@@ -1803,7 +1783,7 @@ function bindPermissionsActions() {
1803
1783
  // A2A-48: Drop zone listeners — bound ONCE here because the zone containers
1804
1784
  // (#active-topics-zone, #active-goals-zone) persist across renders. Only
1805
1785
  // their innerHTML is replaced by renderActiveTopics/renderActiveGoals.
1806
- // Binding in bindSidebarDrag() would cause listener accumulation.
1786
+ // Binding per-element would cause listener accumulation.
1807
1787
  // A2A-51: Uses dragenter/dragleave counter to prevent flickering when
1808
1788
  // cursor moves over child elements (cards, placeholder) inside the zone.
1809
1789
  const topicZone = document.getElementById('active-topics-zone');
@@ -1817,6 +1797,27 @@ function bindPermissionsActions() {
1817
1797
  zone.addEventListener('drop', (e) => { dragCounter = 0; handleZoneDrop(zone, e); });
1818
1798
  });
1819
1799
 
1800
+ // A2A-61: Delegated drag listeners on .perm-sidebar — survives innerHTML
1801
+ // rewrites in renderSidebarLists(). Replaces bindSidebarDrag() which bound
1802
+ // directly to elements destroyed on each render, causing the drag-drop
1803
+ // regression in A2A-41/48/50/51.
1804
+ const sidebar = document.querySelector('.perm-sidebar');
1805
+ if (sidebar) {
1806
+ sidebar.addEventListener('dragstart', (e) => {
1807
+ const item = e.target.closest('.sidebar-item[draggable="true"]');
1808
+ if (!item) return;
1809
+ const itemType = item.dataset.itemType || 'topic';
1810
+ const name = item.dataset.sidebarTopic || item.dataset.sidebarGoal || '';
1811
+ const desc = item.dataset.description || '';
1812
+ e.dataTransfer.setData('application/json', JSON.stringify({ name, description: desc, type: itemType }));
1813
+ item.style.opacity = '0.5';
1814
+ });
1815
+ sidebar.addEventListener('dragend', (e) => {
1816
+ const item = e.target.closest('.sidebar-item[draggable="true"]');
1817
+ if (item) item.style.opacity = '';
1818
+ });
1819
+ }
1820
+
1820
1821
  // A2A-48: Tool toggle change — auto-save and update card styling
1821
1822
  panel.addEventListener('change', (e) => {
1822
1823
  const toggle = e.target.closest('#tool-toggles .toggle-switch input');