a2acalling 0.6.63 → 0.6.65

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.63",
3
- "installed_at": "2026-02-21T06:11:14.272Z",
2
+ "version": "0.6.65",
3
+ "installed_at": "2026-02-25T07:27:45.973Z",
4
4
  "files": [
5
5
  {
6
6
  "path": "CLAUDE.md",
@@ -0,0 +1,25 @@
1
+ # Ticket: Reduce Release Workflow Email Spam
2
+
3
+ ## Summary
4
+ Consolidate or batch release workflows to reduce GitHub notification email volume
5
+
6
+ ## Problem
7
+ Every commit/merge triggers a separate "Release (npm + GitHub) workflow run" notification email. During active development (like the current A2A-47/48/49 sprint), this floods the inbox.
8
+
9
+ ## Proposed Solution
10
+ 1. **Batch releases**: Only trigger release workflow once per PM run completion, not per-merge
11
+ 2. **Conditional triggers**: Skip release workflow for WIP branches, only run on main after all tickets in a batch are merged
12
+ 3. **Consolidate notifications**: Use GitHub's workflow_run event to chain workflows and send a single summary notification
13
+
14
+ ## Acceptance Criteria
15
+ - [ ] Release workflow runs maximum once per maestro PM batch
16
+ - [ ] No release emails for intermediate merges during a sprint
17
+ - [ ] Single "Release complete" notification at end of batch
18
+
19
+ ## Labels
20
+ - chore
21
+ - dx
22
+ - priority: medium
23
+
24
+ ## Team
25
+ a2a calling
package/CONVENTIONS.md CHANGED
@@ -63,6 +63,9 @@ All modules use CommonJS (`require`/`module.exports`). Each lib file exports a f
63
63
  - Uses Shoelace web components (`<sl-*>` elements)
64
64
  - Communicates via fetch to `/dashboard/api/*` endpoints
65
65
  - SSE for real-time updates via `src/lib/dashboard-events.js`
66
+ - Dark theme is the default; uses CSS custom properties for theming
67
+ - Sidebar navigation with tab switching (Contacts, Calls, Invites, Logs, Settings, Permissions, Health)
68
+ - Permissions tab uses tier cards with tool toggles and auto-save
66
69
 
67
70
  ## Permission Tiers
68
71
 
@@ -0,0 +1,359 @@
1
+ # A2A-48: Permissions Tab Redesign — Implementation Plan
2
+
3
+ ## Ticket
4
+ A2A-48: Permissions tab redesign with tier cards, tool toggles, and auto-save
5
+
6
+ ## Approach Summary
7
+ Replace the current form-based permissions UI (dropdown tier selector, checkboxes, Save button) with a card-based, auto-saving interface matching the A2A-46 concept mock. Three files change: index.html, style.css, app.js.
8
+
9
+ ---
10
+
11
+ ## HTML Changes (index.html)
12
+
13
+ ### Remove (lines 101-206 of #panel-permissions)
14
+ - `#tier-select` sl-select dropdown and row
15
+ - `#new-tier-btn` (moves to tier cards header)
16
+ - `#preview-caller-btn` (preview moves to right sidebar)
17
+ - `#show-drag-columns` checkbox and `#tier-columns` div
18
+ - `#tier-form` (name, description, tools checklist, topics list, goals list, Save Tier button)
19
+ - `#copy-from-tier` / `#copy-tier-btn` row — **REMOVED entirely** (copy-from functionality is retained only in the New Tier form's "Copy from" dropdown inside Settings details)
20
+ - Keep `#preview-dialog` (unchanged)
21
+
22
+ ### Add (new #panel-permissions structure)
23
+ ```
24
+ #panel-permissions
25
+ .perm-layout (flex row: main + sidebar)
26
+ .perm-main (flex:1, overflow-y:auto)
27
+ section: Active Tier
28
+ header with "Active Tier" + "+ New Tier" button
29
+ #tier-cards .tier-cards-grid (JS-rendered tier cards)
30
+
31
+ section: Active Configuration
32
+ header with "Active Configuration" + hint text
33
+ .config-columns (2-col grid)
34
+ .config-col: Active Topics (teal accent)
35
+ #active-topics-zone .drop-zone
36
+ .config-col: Active Goals (yellow accent)
37
+ #active-goals-zone .drop-zone
38
+
39
+ section: Allowed Tools
40
+ #tool-toggles .tool-toggles-grid (JS-rendered toggle cards)
41
+
42
+ section: Tier Warnings
43
+ #tier-warnings (existing, repositioned)
44
+
45
+ sl-details summary="Settings & Administration"
46
+ Defaults form (#defaults-form — unchanged internals)
47
+ New Tier form (#new-tier-form — unchanged internals, keeps #new-tier-copy-from)
48
+ Remote Callbook (#callbook-status — unchanged)
49
+ Auto Update (#auto-update-status — unchanged)
50
+ Paired Devices table — unchanged
51
+
52
+ .perm-sidebar (280px fixed right column)
53
+ #perm-preview .preview-card (inline caller preview)
54
+ #sidebar-topics .sidebar-list (all topics, draggable)
55
+ "+ Add Topic" button
56
+ #sidebar-goals .sidebar-list (all goals, draggable)
57
+ "+ Add Goal" button
58
+
59
+ sl-dialog#create-item-dialog (Create New Item modal — simplified, no icon selector)
60
+ sl-input#create-item-title
61
+ sl-textarea#create-item-desc
62
+ Create / Cancel buttons
63
+
64
+ sl-dialog#preview-dialog (existing — kept for full preview)
65
+ ```
66
+
67
+ ### Key DOM ID changes
68
+ | Old ID | New ID/Structure | Notes |
69
+ |--------|-----------------|-------|
70
+ | `tier-select` | `#tier-cards` container | Cards replace dropdown. **All code reading `tier-select.value` must use `state.activeTierId` instead.** |
71
+ | `tier-tools-list` | `#tool-toggles` | Toggle cards replace checkboxes |
72
+ | `tier-topics-list` | `#active-topics-zone` | Drop zone with teal cards |
73
+ | `tier-goals-list` | `#active-goals-zone` | Drop zone with yellow cards |
74
+ | `tier-form` | Removed | Auto-save replaces form |
75
+ | `tier-name` / `tier-description` | Removed | Tier names shown on cards |
76
+ | `show-drag-columns` / `tier-columns` | Removed | Sidebar replaces columns |
77
+ | `copy-from-tier` / `copy-tier-btn` | **Removed entirely** | Copy-tier functionality removed; New Tier form keeps "Copy from" dropdown |
78
+
79
+ ---
80
+
81
+ ## CSS Changes (style.css)
82
+
83
+ ### New classes to add (~130 lines)
84
+
85
+ **Layout:**
86
+ - `.perm-layout` — flex row, gap
87
+ - `.perm-main` — flex:1, overflow-y:auto, max-width
88
+ - `.perm-sidebar` — width:280px, shrink:0, hidden below 1280px
89
+ - `.perm-section` — margin-bottom spacing
90
+ - `.perm-section-header` — flex between h2 and action buttons
91
+ - `.perm-hint` — muted italic helper text
92
+ - `.perm-add-btn` — text button with + icon style
93
+
94
+ **Tier cards:**
95
+ - `.tier-cards-grid` — grid, repeat(auto-fill, minmax(140px,1fr)), gap
96
+ - `.tier-card` — card-bg, border, rounded-xl, padding, cursor, centered content, h-24
97
+ - `.tier-card:hover` — lighter bg, border-gray-600
98
+ - `.tier-card.active` — gradient border glow (pseudo-element with blur), border-blue-500
99
+ - `.tier-card .status-dot` — green pulsing dot (top-right)
100
+ - `.tier-card-icon` — material icon, colored per tier
101
+ - `.tier-card-name` — font-semibold, white
102
+
103
+ **Toggle switches:**
104
+ - `.toggle-switch` — inline-block, 44x24px
105
+ - `.toggle-switch input` — hidden (opacity:0)
106
+ - `.toggle-switch .slider` — rounded pill, #374151 bg, transition
107
+ - `.toggle-switch .slider:before` — white circle, 18x18px
108
+ - `input:checked + .slider` — blue bg + blue glow shadow
109
+ - `input:checked + .slider:before` — translateX(20px)
110
+ - `.tool-toggle-card` — glass-panel, rounded-xl, flex between info and toggle
111
+ - `.tool-toggle-card.enabled` — border-blue-500/30, subtle blue shadow
112
+ - `.tool-icon` — 40x40 rounded-lg icon container, colored per tool
113
+
114
+ **Configuration zones:**
115
+ - `.config-columns` — grid, 2 columns, gap
116
+ - `.config-col` — flex column
117
+ - `.config-col-header` — uppercase, small, tracking-wide, flex with badge
118
+ - `.config-col-header--teal` — teal-400 color
119
+ - `.config-col-header--yellow` — yellow-400 color
120
+ - `.status-dot` — 6px circle
121
+ - `.status-dot--teal` — teal bg + glow
122
+ - `.status-dot--yellow` — yellow bg + glow
123
+ - `.count-badge` — tiny badge with count
124
+ - `.count-badge--teal` / `--yellow` — colored variants
125
+ - `.perm-drop-zone` — dashed SVG border, min-height, rounded-xl
126
+ - `.perm-drop-zone.drag-over` — blue highlight
127
+ - `.active-item-card` — card with accent border + glow shadow + close button
128
+ - `.active-item-card--teal` — teal border/glow
129
+ - `.active-item-card--yellow` — yellow border/glow
130
+ - `.drop-placeholder` — dashed inner border, "+ Drop Topic/Goal" text
131
+
132
+ **Sidebar:**
133
+ - `.preview-card` — gradient bg, rounded-2xl, border, padding, relative overflow
134
+ - `.sidebar-list` — space-y, margin-top
135
+ - `.sidebar-list-header` — uppercase tracking-wide muted
136
+ - `.sidebar-item` — card-bg, border, rounded-lg, flex, cursor-move, group-hover
137
+ - `.sidebar-item.active-in-zone` — dimmed, dashed border, italic "(Active)" label
138
+ - `.sidebar-add-btn` — dashed border, full-width, muted + hover primary
139
+
140
+ **Responsive:**
141
+ - `@media (max-width: 1280px)` — hide `.perm-sidebar`, show fallback preview button in .perm-main
142
+
143
+ ### Modify existing
144
+ - Remove `.tools-checklist` rules (replaced by toggle cards)
145
+ - Remove `.tier-columns` / `.tier-column` rules (replaced by sidebar)
146
+ - Keep `.topic-row`, `.drag-handle`, `.topic-content` etc. (adapted for sidebar items)
147
+ - Keep `.tier-warning` rules (unchanged)
148
+ - Keep `.glass-panel` (reused for tool toggle cards)
149
+
150
+ ---
151
+
152
+ ## JS Changes (app.js)
153
+
154
+ ### New state
155
+ ```js
156
+ // Add to state object:
157
+ activeTierId: 'public', // currently selected tier (replaces tier-select dropdown value)
158
+ ```
159
+
160
+ ### Functions to REMOVE
161
+ - `fillTierSelects()` — replaced by renderTierCards() + populateInviteTierSelect()
162
+ - `renderTierColumns()` — replaced by sidebar lists
163
+ - `bindDragEvents()` — replaced by new sidebar↔zone drag handlers
164
+ - `bindItemListDelegation()` — replaced by direct delegation in bindPermissionsActions()
165
+
166
+ ### bootstrap() call site changes (CRITICAL)
167
+ The bootstrap() function must be updated:
168
+ - `bindSettingsActions()` → `bindPermissionsActions()` (renamed)
169
+ - Remove `bindItemListDelegation()` call (functionality merged into bindPermissionsActions)
170
+ - `tabLoaders.permissions` should call `loadSettings()` to ensure fresh data on tab switch (currently a no-op)
171
+
172
+ ### Functions to REWRITE
173
+
174
+ **`renderToolCheckboxes(allowedTools)` → `renderToolToggles(allowedTools)`**
175
+ - Renders TOOL_DESCRIPTIONS as toggle-switch cards (glass-panel + icon + name + desc + toggle)
176
+ - Each toggle has data-tool attribute
177
+ - Change event calls autoSaveTier()
178
+ - Active toggles get .enabled class (blue border glow)
179
+
180
+ **`renderTopicList(tier)` → `renderActiveTopics(tier)`**
181
+ - Renders topics in #active-topics-zone as .active-item-card--teal cards
182
+ - Each card has close button (removes + auto-saves)
183
+ - Includes "+ Drop Topic" placeholder at bottom
184
+ - Updates #topic-count badge
185
+
186
+ **`renderGoalList(tier)` → `renderActiveGoals(tier)`**
187
+ - Same pattern as topics but with yellow accent, renders in #active-goals-zone
188
+ - Updates #goal-count badge
189
+
190
+ **`renderTierEditor(tierId)` → `renderPermissions()`**
191
+ - Orchestrator: calls renderTierCards, renderActiveTopics, renderActiveGoals, renderToolToggles, renderTierWarnings, renderSidebarPreview, renderSidebarLists
192
+ - Uses state.activeTierId instead of reading tier-select.value
193
+
194
+ **`openCallerPreview()`** ← MUST BE REWRITTEN (not unchanged!)
195
+ - Current code at line 1591 reads `document.getElementById('tier-select').value` — that element is removed.
196
+ - Fix: Replace with `state.activeTierId`
197
+ - Rest of function logic (getPreviewData, dialog rendering) stays the same.
198
+
199
+ **`bindSettingsActions()` → `bindPermissionsActions()`**
200
+ - Remove: tier-form submit handler (Save Tier button gone)
201
+ - Remove: tier-select sl-change handler (cards replace it)
202
+ - Remove: copy-tier-btn click handler (copy-tier functionality removed)
203
+ - Remove: show-drag-columns handler (removed feature)
204
+ - Remove: preview-caller-btn handler (preview now inline in sidebar)
205
+ - Add: tier card click delegation → set state.activeTierId + renderPermissions()
206
+ - Add: tool toggle change delegation → autoSaveTier()
207
+ - Add: topic/goal close button delegation → remove + autoSaveTier()
208
+ - Add: sidebar add-topic/add-goal button → open create-item-dialog
209
+ - Add: create-item-dialog submit → add item + autoSaveTier()
210
+ - Keep: defaults-form submit handler
211
+ - Keep: new-tier-form submit handler — **BUT FIX LINE 1734**: replace `document.getElementById('tier-select').value = tierId` with `state.activeTierId = tierId; renderPermissions();`
212
+ - Keep: preview-close-btn handler
213
+ - Keep: callbook, auto-update, device handlers (unchanged)
214
+
215
+ **`loadSettings()`**
216
+ - Replace `fillTierSelects()` call with:
217
+ - Call `populateInviteTierSelect()` to populate `#invite-tier` and `#new-tier-copy-from`
218
+ - Call `renderPermissions()`
219
+ - Remove: `renderTierColumns()` call
220
+ - Keep: defaults field population
221
+
222
+ ### Functions to ADD
223
+
224
+ **`renderTierCards()`**
225
+ - Reads state.settings.tiers, renders grid of .tier-card elements
226
+ - Active card (state.activeTierId) gets .active class + green status-dot
227
+ - Each card: icon (mapped from tier id or custom), name, click handler
228
+
229
+ **`renderSidebarPreview(tierId)`**
230
+ - Uses getPreviewData(tierId) to render inline preview card
231
+ - Shows: tier name badge, "can discuss X topics to help Y goals using Z tools" summary
232
+ - Active/valid status indicators
233
+
234
+ **`renderSidebarLists(tier)`**
235
+ - Renders all topics in #sidebar-topics as .sidebar-item cards with drag handles
236
+ - Topics active in current tier shown dimmed with "(Active)" label
237
+ - Renders all goals in #sidebar-goals same pattern
238
+ - Each list has "+ Add Topic" / "+ Add Goal" button at bottom
239
+
240
+ **`autoSaveTier()`**
241
+ - Debounced (250ms) function
242
+ - Collects: allowed_tools from toggle states, topics from active zone, goals from active zone
243
+ - PUTs to `/settings/tiers/${state.activeTierId}`
244
+ - Shows subtle "Saved" notice on success
245
+ - NOTE: Uses `c.dataset.topic` for BOTH topics and goals — this is intentional because
246
+ `parseTopicObjects()` in dashboard.js:160 only reads `entry.topic`. Add WHY comment.
247
+
248
+ **`bindSidebarDrag()`**
249
+ - Makes .sidebar-item elements draggable
250
+ - Drop targets: #active-topics-zone and #active-goals-zone
251
+ - On drop: add item to active zone, mark as "(Active)" in sidebar, auto-save
252
+ - Reverse: close button on active items removes from zone, un-dims in sidebar
253
+
254
+ **`populateInviteTierSelect()`**
255
+ - Extracted from old fillTierSelects() — populates:
256
+ 1. `#invite-tier` (invites tab)
257
+ 2. `#new-tier-copy-from` (inside Settings details)
258
+ - Does NOT populate `#tier-select` (removed) or `#copy-from-tier` (removed)
259
+
260
+ ### Functions UNCHANGED
261
+ - `getPreviewData(tierId)` — reused by renderSidebarPreview
262
+ - `renderTierWarnings(tier)` — container ID stays #tier-warnings
263
+ - `renderCallbookStatus()` — unchanged, just inside Settings details now
264
+ - `renderAutoUpdateStatus()` — unchanged
265
+ - `loadDashboardStatus()` — unchanged
266
+
267
+ ---
268
+
269
+ ## Auto-Save Pattern
270
+
271
+ ```js
272
+ // A2A-48: debounced auto-save replaces explicit Save Tier button.
273
+ // 250ms delay prevents excessive API calls during rapid changes.
274
+ let _autoSaveTimer = null;
275
+ function autoSaveTier() {
276
+ clearTimeout(_autoSaveTimer);
277
+ _autoSaveTimer = setTimeout(async () => {
278
+ const tierId = state.activeTierId;
279
+ if (!tierId) return;
280
+
281
+ // Collect tools from toggle states
282
+ const toggles = document.querySelectorAll('#tool-toggles .toggle-switch input');
283
+ const allowed_tools = Array.from(toggles).filter(t => t.checked).map(t => t.dataset.tool);
284
+
285
+ // Collect topics from active zone
286
+ const topicCards = document.querySelectorAll('#active-topics-zone .active-item-card');
287
+ const topics = Array.from(topicCards).map(c => c.dataset.topic).filter(Boolean);
288
+ const manifestTopics = Array.from(topicCards).map(c => ({
289
+ topic: c.dataset.topic,
290
+ description: c.dataset.description || ''
291
+ })).filter(t => t.topic);
292
+
293
+ // A2A-48: uses dataset.topic for goals too (NOT dataset.objective) because
294
+ // parseTopicObjects() in dashboard.js:160 only reads entry.topic. The semantic
295
+ // distinction (objective vs topic) is UI-only; storage layer uses {topic, description}.
296
+ const goalCards = document.querySelectorAll('#active-goals-zone .active-item-card');
297
+ const goals = Array.from(goalCards).map(c => c.dataset.topic).filter(Boolean);
298
+ const manifestObjectives = Array.from(goalCards).map(c => ({
299
+ topic: c.dataset.topic,
300
+ description: c.dataset.description || ''
301
+ })).filter(g => g.topic);
302
+
303
+ const body = { allowed_tools, topics, goals, manifest: { topics: manifestTopics, objectives: manifestObjectives } };
304
+ try {
305
+ await request(`/settings/tiers/${encodeURIComponent(tierId)}`, {
306
+ method: 'PUT', body: JSON.stringify(body)
307
+ });
308
+ showNotice('Saved');
309
+ } catch (err) {
310
+ showNotice(`Save failed: ${err.message}`);
311
+ }
312
+ // Refresh state from server to stay in sync
313
+ const payload = await request('/settings');
314
+ state.settings = payload;
315
+ }, 250);
316
+ }
317
+ ```
318
+
319
+ ---
320
+
321
+ ## Critical Integration Points
322
+
323
+ 1. **state.activeTierId initialization**: Set to 'public' in state object. When permissions panel loads (`loadSettings()`), default to 'public' or first available tier.
324
+
325
+ 2. **Invite tier select**: `fillTierSelects()` currently populates `#invite-tier` for the Invites tab. After removing fillTierSelects, `populateInviteTierSelect()` handles this. Called from loadSettings().
326
+
327
+ 3. **Tier warnings**: `#tier-warnings` container stays in new HTML. `renderTierWarnings()` called from `renderPermissions()` unchanged.
328
+
329
+ 4. **Settings details contents**: The defaults form, new-tier form, callbook, auto-update, and device sections all reference existing DOM IDs. They move INSIDE `<sl-details>` but their IDs and inner structure stay the same. All existing bind handlers still find them.
330
+
331
+ 5. **Tab navigation (CORRECTED)**: `tabLoaders.permissions` is currently a no-op (`() => {}`). Update it to call `loadSettings()` to ensure fresh data when switching to Permissions tab. This is not a regression fix (existing code has same stale-data issue) but improves behavior while we're in here.
332
+
333
+ 6. **Hash navigation**: `openCallTranscript()` uses hash to switch tabs. No impact — permissions tab doesn't use hash navigation.
334
+
335
+ 7. **openCallerPreview() tier-select reference (FIXED)**: This function reads `document.getElementById('tier-select').value` at line 1591. Since `#tier-select` is removed, this MUST change to `state.activeTierId`. The function is listed as "Functions to REWRITE" above.
336
+
337
+ 8. **new-tier-form success path (FIXED)**: At line 1734, `document.getElementById('tier-select').value = tierId` must become `state.activeTierId = tierId; renderPermissions();`.
338
+
339
+ 9. **bootstrap() changes (FIXED)**: Must update: `bindSettingsActions()` → `bindPermissionsActions()`, remove `bindItemListDelegation()` call.
340
+
341
+ ---
342
+
343
+ ## Scope Decision
344
+
345
+ Drop the glass modal with icon selector and segmented Topic/Goal control. Instead, use a simplified `<sl-dialog>` with just title + description inputs (consistent with existing Shoelace patterns). This saves ~60-80 lines while keeping the "Create New Item" functionality.
346
+
347
+ Builder should track actual git diff size. If exceeding 550 lines, defer the sidebar drag feature (make sidebar read-only) as the primary cut.
348
+
349
+ ---
350
+
351
+ ## Testing Strategy
352
+
353
+ No new test files needed — this is a pure frontend SPA change. Existing backend tests (`npm test`) must pass unchanged since:
354
+ - No API changes
355
+ - No route changes
356
+ - No data model changes
357
+ - Dashboard API endpoints unchanged
358
+
359
+ Manual verification via browser: open dashboard, navigate to Permissions tab, verify all acceptance criteria visually.