a2acalling 0.6.75 → 0.6.76

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.
Files changed (1025) hide show
  1. package/.a2a-manifest.json +2 -2
  2. package/.claude/worktrees/agent-a0a8dd02/.a2a-manifest.json +70 -0
  3. package/.claude/worktrees/agent-a0a8dd02/.c8rc.json +16 -0
  4. package/.claude/worktrees/agent-a0a8dd02/.claude/a2a-skill-reference.md +462 -0
  5. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-app.md +42 -0
  6. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-call.md +26 -0
  7. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-contacts.md +31 -0
  8. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-conversations.md +47 -0
  9. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-gui.md +30 -0
  10. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-invite.md +63 -0
  11. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-setup.md +30 -0
  12. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-skills.md +27 -0
  13. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-status.md +46 -0
  14. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-uninstall.md +36 -0
  15. package/.claude/worktrees/agent-a0a8dd02/.claude/commands/a2a-update.md +41 -0
  16. package/.claude/worktrees/agent-a0a8dd02/.node-version +1 -0
  17. package/.claude/worktrees/agent-a0a8dd02/ARCHITECTURE.md +135 -0
  18. package/.claude/worktrees/agent-a0a8dd02/CLAUDE-INSTALL.md +156 -0
  19. package/.claude/worktrees/agent-a0a8dd02/CONVENTIONS.md +179 -0
  20. package/.claude/worktrees/agent-a0a8dd02/README.md +470 -0
  21. package/.claude/worktrees/agent-a0a8dd02/SKILL.md +462 -0
  22. package/.claude/worktrees/agent-a0a8dd02/bin/cli.js +3184 -0
  23. package/.claude/worktrees/agent-a0a8dd02/biome.json +27 -0
  24. package/.claude/worktrees/agent-a0a8dd02/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  25. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  26. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  27. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-auto-updater.md +1284 -0
  28. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  29. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  30. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  31. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  32. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  33. package/.claude/worktrees/agent-a0a8dd02/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  34. package/.claude/worktrees/agent-a0a8dd02/docs/prompts/e2e-test-agent.md +368 -0
  35. package/.claude/worktrees/agent-a0a8dd02/docs/protocol.md +440 -0
  36. package/.claude/worktrees/agent-a0a8dd02/docs/signing-setup.md +49 -0
  37. package/.claude/worktrees/agent-a0a8dd02/eslint.config.js +16 -0
  38. package/.claude/worktrees/agent-a0a8dd02/knip.json +17 -0
  39. package/.claude/worktrees/agent-a0a8dd02/native/macos/index.html +179 -0
  40. package/.claude/worktrees/agent-a0a8dd02/native/macos/package.json +8 -0
  41. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/Cargo.lock +5875 -0
  42. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/Cargo.toml +25 -0
  43. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/binaries/.gitkeep +0 -0
  44. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/build.rs +3 -0
  45. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/capabilities/default.json +26 -0
  46. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/entitlements.plist +14 -0
  47. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/128x128.png +0 -0
  48. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  49. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/32x32.png +0 -0
  50. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/icon.icns +0 -0
  51. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/tray-connected.png +0 -0
  52. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  53. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/discovery.rs +184 -0
  54. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/health.rs +67 -0
  55. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/lib.rs +256 -0
  56. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/main.rs +6 -0
  57. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/notifications.rs +180 -0
  58. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/server.rs +306 -0
  59. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/src/updater.rs +124 -0
  60. package/.claude/worktrees/agent-a0a8dd02/native/macos/src-tauri/tauri.conf.json +58 -0
  61. package/.claude/worktrees/agent-a0a8dd02/package.json +54 -0
  62. package/.claude/worktrees/agent-a0a8dd02/pkg.config.json +14 -0
  63. package/.claude/worktrees/agent-a0a8dd02/scripts/build-standalone.sh +106 -0
  64. package/.claude/worktrees/agent-a0a8dd02/scripts/cleanup.js +251 -0
  65. package/.claude/worktrees/agent-a0a8dd02/scripts/generate-update-manifest.sh +42 -0
  66. package/.claude/worktrees/agent-a0a8dd02/scripts/install-openclaw.js +986 -0
  67. package/.claude/worktrees/agent-a0a8dd02/scripts/install-skills.js +234 -0
  68. package/.claude/worktrees/agent-a0a8dd02/scripts/postinstall.js +224 -0
  69. package/.claude/worktrees/agent-a0a8dd02/scripts/preuninstall.js +68 -0
  70. package/.claude/worktrees/agent-a0a8dd02/scripts/run-e2e.sh +44 -0
  71. package/.claude/worktrees/agent-a0a8dd02/scripts/smoke-test-standalone.sh +101 -0
  72. package/.claude/worktrees/agent-a0a8dd02/scripts/sync-version.sh +28 -0
  73. package/.claude/worktrees/agent-a0a8dd02/scripts/verify-app-bundle.sh +34 -0
  74. package/.claude/worktrees/agent-a0a8dd02/src/dashboard/public/app.js +2683 -0
  75. package/.claude/worktrees/agent-a0a8dd02/src/dashboard/public/index.html +393 -0
  76. package/.claude/worktrees/agent-a0a8dd02/src/dashboard/public/style.css +1294 -0
  77. package/.claude/worktrees/agent-a0a8dd02/src/index.js +76 -0
  78. package/.claude/worktrees/agent-a0a8dd02/src/lib/agent-card.js +111 -0
  79. package/.claude/worktrees/agent-a0a8dd02/src/lib/call-monitor.js +205 -0
  80. package/.claude/worktrees/agent-a0a8dd02/src/lib/callbook.js +366 -0
  81. package/.claude/worktrees/agent-a0a8dd02/src/lib/claude-subagent.js +696 -0
  82. package/.claude/worktrees/agent-a0a8dd02/src/lib/client.js +683 -0
  83. package/.claude/worktrees/agent-a0a8dd02/src/lib/config.js +480 -0
  84. package/.claude/worktrees/agent-a0a8dd02/src/lib/conversation-driver.js +608 -0
  85. package/.claude/worktrees/agent-a0a8dd02/src/lib/conversations.js +830 -0
  86. package/.claude/worktrees/agent-a0a8dd02/src/lib/crypto.js +113 -0
  87. package/.claude/worktrees/agent-a0a8dd02/src/lib/dashboard-events.js +213 -0
  88. package/.claude/worktrees/agent-a0a8dd02/src/lib/disclosure.js +792 -0
  89. package/.claude/worktrees/agent-a0a8dd02/src/lib/external-ip.js +211 -0
  90. package/.claude/worktrees/agent-a0a8dd02/src/lib/invite-host.js +223 -0
  91. package/.claude/worktrees/agent-a0a8dd02/src/lib/local-request.js +69 -0
  92. package/.claude/worktrees/agent-a0a8dd02/src/lib/logger.js +677 -0
  93. package/.claude/worktrees/agent-a0a8dd02/src/lib/openclaw-integration.js +339 -0
  94. package/.claude/worktrees/agent-a0a8dd02/src/lib/pid-file.js +103 -0
  95. package/.claude/worktrees/agent-a0a8dd02/src/lib/port-scanner.js +83 -0
  96. package/.claude/worktrees/agent-a0a8dd02/src/lib/prompt-template.js +355 -0
  97. package/.claude/worktrees/agent-a0a8dd02/src/lib/runtime-adapter.js +701 -0
  98. package/.claude/worktrees/agent-a0a8dd02/src/lib/summarizer.js +156 -0
  99. package/.claude/worktrees/agent-a0a8dd02/src/lib/summary-formatter.js +168 -0
  100. package/.claude/worktrees/agent-a0a8dd02/src/lib/summary-prompt.js +203 -0
  101. package/.claude/worktrees/agent-a0a8dd02/src/lib/tokens.js +868 -0
  102. package/.claude/worktrees/agent-a0a8dd02/src/lib/turn-timeout.js +52 -0
  103. package/.claude/worktrees/agent-a0a8dd02/src/lib/update-checker.js +93 -0
  104. package/.claude/worktrees/agent-a0a8dd02/src/lib/update-manager.js +313 -0
  105. package/.claude/worktrees/agent-a0a8dd02/src/routes/a2a.js +1213 -0
  106. package/.claude/worktrees/agent-a0a8dd02/src/routes/callbook.js +142 -0
  107. package/.claude/worktrees/agent-a0a8dd02/src/routes/dashboard.js +1578 -0
  108. package/.claude/worktrees/agent-a0a8dd02/src/server.js +1179 -0
  109. package/.claude/worktrees/agent-a3c12538/.a2a-manifest.json +70 -0
  110. package/.claude/worktrees/agent-a3c12538/.c8rc.json +16 -0
  111. package/.claude/worktrees/agent-a3c12538/.claude/a2a-skill-reference.md +462 -0
  112. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-app.md +42 -0
  113. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-call.md +26 -0
  114. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-contacts.md +31 -0
  115. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-conversations.md +47 -0
  116. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-gui.md +30 -0
  117. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-invite.md +63 -0
  118. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-setup.md +30 -0
  119. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-skills.md +27 -0
  120. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-status.md +46 -0
  121. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-uninstall.md +36 -0
  122. package/.claude/worktrees/agent-a3c12538/.claude/commands/a2a-update.md +41 -0
  123. package/.claude/worktrees/agent-a3c12538/.node-version +1 -0
  124. package/.claude/worktrees/agent-a3c12538/ARCHITECTURE.md +135 -0
  125. package/.claude/worktrees/agent-a3c12538/CLAUDE-INSTALL.md +156 -0
  126. package/.claude/worktrees/agent-a3c12538/CONVENTIONS.md +169 -0
  127. package/.claude/worktrees/agent-a3c12538/README.md +470 -0
  128. package/.claude/worktrees/agent-a3c12538/SKILL.md +462 -0
  129. package/.claude/worktrees/agent-a3c12538/bin/cli.js +3184 -0
  130. package/.claude/worktrees/agent-a3c12538/biome.json +27 -0
  131. package/.claude/worktrees/agent-a3c12538/docs/app.js +30 -0
  132. package/.claude/worktrees/agent-a3c12538/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  133. package/.claude/worktrees/agent-a3c12538/docs/assets/icon-32.png +0 -0
  134. package/.claude/worktrees/agent-a3c12538/docs/assets/icon-64.png +0 -0
  135. package/.claude/worktrees/agent-a3c12538/docs/index.html +117 -0
  136. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  137. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  138. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-auto-updater.md +1284 -0
  139. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  140. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  141. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  142. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  143. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  144. package/.claude/worktrees/agent-a3c12538/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  145. package/.claude/worktrees/agent-a3c12538/docs/prompts/e2e-test-agent.md +368 -0
  146. package/.claude/worktrees/agent-a3c12538/docs/protocol.md +440 -0
  147. package/.claude/worktrees/agent-a3c12538/docs/signing-setup.md +49 -0
  148. package/.claude/worktrees/agent-a3c12538/docs/style.css +209 -0
  149. package/.claude/worktrees/agent-a3c12538/eslint.config.js +16 -0
  150. package/.claude/worktrees/agent-a3c12538/knip.json +17 -0
  151. package/.claude/worktrees/agent-a3c12538/native/macos/index.html +179 -0
  152. package/.claude/worktrees/agent-a3c12538/native/macos/package.json +8 -0
  153. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/Cargo.lock +5875 -0
  154. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/Cargo.toml +24 -0
  155. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/binaries/.gitkeep +0 -0
  156. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/build.rs +3 -0
  157. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/capabilities/default.json +26 -0
  158. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/entitlements.plist +14 -0
  159. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/128x128.png +0 -0
  160. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  161. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/32x32.png +0 -0
  162. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/icon.icns +0 -0
  163. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/tray-connected.png +0 -0
  164. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  165. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/discovery.rs +184 -0
  166. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/health.rs +67 -0
  167. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/lib.rs +226 -0
  168. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/main.rs +6 -0
  169. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/notifications.rs +180 -0
  170. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/src/server.rs +306 -0
  171. package/.claude/worktrees/agent-a3c12538/native/macos/src-tauri/tauri.conf.json +50 -0
  172. package/.claude/worktrees/agent-a3c12538/package.json +54 -0
  173. package/.claude/worktrees/agent-a3c12538/pkg.config.json +14 -0
  174. package/.claude/worktrees/agent-a3c12538/scripts/build-standalone.sh +106 -0
  175. package/.claude/worktrees/agent-a3c12538/scripts/cleanup.js +251 -0
  176. package/.claude/worktrees/agent-a3c12538/scripts/install-openclaw.js +986 -0
  177. package/.claude/worktrees/agent-a3c12538/scripts/install-skills.js +234 -0
  178. package/.claude/worktrees/agent-a3c12538/scripts/postinstall.js +224 -0
  179. package/.claude/worktrees/agent-a3c12538/scripts/preuninstall.js +68 -0
  180. package/.claude/worktrees/agent-a3c12538/scripts/run-e2e.sh +44 -0
  181. package/.claude/worktrees/agent-a3c12538/scripts/smoke-test-standalone.sh +101 -0
  182. package/.claude/worktrees/agent-a3c12538/scripts/sync-version.sh +28 -0
  183. package/.claude/worktrees/agent-a3c12538/scripts/verify-app-bundle.sh +34 -0
  184. package/.claude/worktrees/agent-a3c12538/src/dashboard/public/app.js +2649 -0
  185. package/.claude/worktrees/agent-a3c12538/src/dashboard/public/index.html +386 -0
  186. package/.claude/worktrees/agent-a3c12538/src/dashboard/public/style.css +1274 -0
  187. package/.claude/worktrees/agent-a3c12538/src/index.js +76 -0
  188. package/.claude/worktrees/agent-a3c12538/src/lib/agent-card.js +111 -0
  189. package/.claude/worktrees/agent-a3c12538/src/lib/call-monitor.js +205 -0
  190. package/.claude/worktrees/agent-a3c12538/src/lib/callbook.js +366 -0
  191. package/.claude/worktrees/agent-a3c12538/src/lib/claude-subagent.js +696 -0
  192. package/.claude/worktrees/agent-a3c12538/src/lib/client.js +683 -0
  193. package/.claude/worktrees/agent-a3c12538/src/lib/config.js +480 -0
  194. package/.claude/worktrees/agent-a3c12538/src/lib/conversation-driver.js +608 -0
  195. package/.claude/worktrees/agent-a3c12538/src/lib/conversations.js +830 -0
  196. package/.claude/worktrees/agent-a3c12538/src/lib/crypto.js +113 -0
  197. package/.claude/worktrees/agent-a3c12538/src/lib/dashboard-events.js +213 -0
  198. package/.claude/worktrees/agent-a3c12538/src/lib/disclosure.js +792 -0
  199. package/.claude/worktrees/agent-a3c12538/src/lib/external-ip.js +211 -0
  200. package/.claude/worktrees/agent-a3c12538/src/lib/invite-host.js +223 -0
  201. package/.claude/worktrees/agent-a3c12538/src/lib/local-request.js +69 -0
  202. package/.claude/worktrees/agent-a3c12538/src/lib/logger.js +677 -0
  203. package/.claude/worktrees/agent-a3c12538/src/lib/openclaw-integration.js +339 -0
  204. package/.claude/worktrees/agent-a3c12538/src/lib/pid-file.js +103 -0
  205. package/.claude/worktrees/agent-a3c12538/src/lib/port-scanner.js +83 -0
  206. package/.claude/worktrees/agent-a3c12538/src/lib/prompt-template.js +355 -0
  207. package/.claude/worktrees/agent-a3c12538/src/lib/runtime-adapter.js +701 -0
  208. package/.claude/worktrees/agent-a3c12538/src/lib/summarizer.js +156 -0
  209. package/.claude/worktrees/agent-a3c12538/src/lib/summary-formatter.js +168 -0
  210. package/.claude/worktrees/agent-a3c12538/src/lib/summary-prompt.js +203 -0
  211. package/.claude/worktrees/agent-a3c12538/src/lib/tokens.js +868 -0
  212. package/.claude/worktrees/agent-a3c12538/src/lib/turn-timeout.js +52 -0
  213. package/.claude/worktrees/agent-a3c12538/src/lib/update-checker.js +93 -0
  214. package/.claude/worktrees/agent-a3c12538/src/lib/update-manager.js +313 -0
  215. package/.claude/worktrees/agent-a3c12538/src/routes/a2a.js +1213 -0
  216. package/.claude/worktrees/agent-a3c12538/src/routes/callbook.js +142 -0
  217. package/.claude/worktrees/agent-a3c12538/src/routes/dashboard.js +1578 -0
  218. package/.claude/worktrees/agent-a3c12538/src/server.js +1179 -0
  219. package/.claude/worktrees/agent-a5b87d75/.a2a-manifest.json +70 -0
  220. package/.claude/worktrees/agent-a5b87d75/.c8rc.json +16 -0
  221. package/.claude/worktrees/agent-a5b87d75/.claude/a2a-skill-reference.md +462 -0
  222. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-app.md +42 -0
  223. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-call.md +26 -0
  224. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-contacts.md +31 -0
  225. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-conversations.md +47 -0
  226. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-gui.md +30 -0
  227. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-invite.md +63 -0
  228. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-setup.md +30 -0
  229. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-skills.md +27 -0
  230. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-status.md +46 -0
  231. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-uninstall.md +36 -0
  232. package/.claude/worktrees/agent-a5b87d75/.claude/commands/a2a-update.md +41 -0
  233. package/.claude/worktrees/agent-a5b87d75/.node-version +1 -0
  234. package/.claude/worktrees/agent-a5b87d75/ARCHITECTURE.md +135 -0
  235. package/.claude/worktrees/agent-a5b87d75/CLAUDE-INSTALL.md +156 -0
  236. package/.claude/worktrees/agent-a5b87d75/CONVENTIONS.md +208 -0
  237. package/.claude/worktrees/agent-a5b87d75/README.md +470 -0
  238. package/.claude/worktrees/agent-a5b87d75/SKILL.md +462 -0
  239. package/.claude/worktrees/agent-a5b87d75/bin/cli.js +3184 -0
  240. package/.claude/worktrees/agent-a5b87d75/biome.json +27 -0
  241. package/.claude/worktrees/agent-a5b87d75/docs/app.js +30 -0
  242. package/.claude/worktrees/agent-a5b87d75/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  243. package/.claude/worktrees/agent-a5b87d75/docs/assets/icon-32.png +0 -0
  244. package/.claude/worktrees/agent-a5b87d75/docs/assets/icon-64.png +0 -0
  245. package/.claude/worktrees/agent-a5b87d75/docs/index.html +117 -0
  246. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  247. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  248. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-auto-updater.md +1284 -0
  249. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  250. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  251. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  252. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  253. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  254. package/.claude/worktrees/agent-a5b87d75/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  255. package/.claude/worktrees/agent-a5b87d75/docs/prompts/e2e-test-agent.md +368 -0
  256. package/.claude/worktrees/agent-a5b87d75/docs/protocol.md +440 -0
  257. package/.claude/worktrees/agent-a5b87d75/docs/signing-setup.md +49 -0
  258. package/.claude/worktrees/agent-a5b87d75/docs/style.css +209 -0
  259. package/.claude/worktrees/agent-a5b87d75/eslint.config.js +16 -0
  260. package/.claude/worktrees/agent-a5b87d75/knip.json +17 -0
  261. package/.claude/worktrees/agent-a5b87d75/native/macos/index.html +182 -0
  262. package/.claude/worktrees/agent-a5b87d75/native/macos/package.json +8 -0
  263. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/Cargo.lock +5875 -0
  264. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/Cargo.toml +25 -0
  265. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/binaries/.gitkeep +0 -0
  266. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/build.rs +3 -0
  267. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/capabilities/default.json +26 -0
  268. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/dmg-background.png +0 -0
  269. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/dmg-background@2x.png +0 -0
  270. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/entitlements.plist +14 -0
  271. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/128x128.png +0 -0
  272. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  273. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/256x256.png +0 -0
  274. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/32x32.png +0 -0
  275. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/512x512.png +0 -0
  276. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/512x512@2x.png +0 -0
  277. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/icon.icns +0 -0
  278. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/tray-connected.png +0 -0
  279. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  280. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/discovery.rs +221 -0
  281. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/health.rs +67 -0
  282. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/lib.rs +256 -0
  283. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/main.rs +6 -0
  284. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/notifications.rs +180 -0
  285. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/server.rs +306 -0
  286. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/src/updater.rs +124 -0
  287. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/tauri.conf.json +68 -0
  288. package/.claude/worktrees/agent-a5b87d75/native/macos/src-tauri/volume-icon.icns +0 -0
  289. package/.claude/worktrees/agent-a5b87d75/package.json +55 -0
  290. package/.claude/worktrees/agent-a5b87d75/pkg.config.json +14 -0
  291. package/.claude/worktrees/agent-a5b87d75/scripts/build-standalone.sh +106 -0
  292. package/.claude/worktrees/agent-a5b87d75/scripts/cleanup.js +251 -0
  293. package/.claude/worktrees/agent-a5b87d75/scripts/generate-update-manifest.sh +42 -0
  294. package/.claude/worktrees/agent-a5b87d75/scripts/install-openclaw.js +986 -0
  295. package/.claude/worktrees/agent-a5b87d75/scripts/install-skills.js +234 -0
  296. package/.claude/worktrees/agent-a5b87d75/scripts/postinstall.js +224 -0
  297. package/.claude/worktrees/agent-a5b87d75/scripts/preuninstall.js +68 -0
  298. package/.claude/worktrees/agent-a5b87d75/scripts/run-e2e.sh +52 -0
  299. package/.claude/worktrees/agent-a5b87d75/scripts/smoke-test-standalone.sh +101 -0
  300. package/.claude/worktrees/agent-a5b87d75/scripts/sync-version.sh +28 -0
  301. package/.claude/worktrees/agent-a5b87d75/scripts/verify-app-bundle.sh +34 -0
  302. package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/app.js +2683 -0
  303. package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/index.html +393 -0
  304. package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/onboarding.css +269 -0
  305. package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/onboarding.html +96 -0
  306. package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/onboarding.js +277 -0
  307. package/.claude/worktrees/agent-a5b87d75/src/dashboard/public/style.css +1294 -0
  308. package/.claude/worktrees/agent-a5b87d75/src/index.js +76 -0
  309. package/.claude/worktrees/agent-a5b87d75/src/lib/agent-card.js +111 -0
  310. package/.claude/worktrees/agent-a5b87d75/src/lib/call-monitor.js +205 -0
  311. package/.claude/worktrees/agent-a5b87d75/src/lib/callbook.js +366 -0
  312. package/.claude/worktrees/agent-a5b87d75/src/lib/claude-subagent.js +696 -0
  313. package/.claude/worktrees/agent-a5b87d75/src/lib/client.js +683 -0
  314. package/.claude/worktrees/agent-a5b87d75/src/lib/config.js +480 -0
  315. package/.claude/worktrees/agent-a5b87d75/src/lib/conversation-driver.js +608 -0
  316. package/.claude/worktrees/agent-a5b87d75/src/lib/conversations.js +830 -0
  317. package/.claude/worktrees/agent-a5b87d75/src/lib/crypto.js +113 -0
  318. package/.claude/worktrees/agent-a5b87d75/src/lib/dashboard-events.js +213 -0
  319. package/.claude/worktrees/agent-a5b87d75/src/lib/disclosure.js +792 -0
  320. package/.claude/worktrees/agent-a5b87d75/src/lib/external-ip.js +211 -0
  321. package/.claude/worktrees/agent-a5b87d75/src/lib/invite-host.js +223 -0
  322. package/.claude/worktrees/agent-a5b87d75/src/lib/local-request.js +69 -0
  323. package/.claude/worktrees/agent-a5b87d75/src/lib/logger.js +677 -0
  324. package/.claude/worktrees/agent-a5b87d75/src/lib/openclaw-integration.js +339 -0
  325. package/.claude/worktrees/agent-a5b87d75/src/lib/pid-file.js +103 -0
  326. package/.claude/worktrees/agent-a5b87d75/src/lib/port-scanner.js +83 -0
  327. package/.claude/worktrees/agent-a5b87d75/src/lib/prompt-template.js +355 -0
  328. package/.claude/worktrees/agent-a5b87d75/src/lib/runtime-adapter.js +701 -0
  329. package/.claude/worktrees/agent-a5b87d75/src/lib/summarizer.js +156 -0
  330. package/.claude/worktrees/agent-a5b87d75/src/lib/summary-formatter.js +168 -0
  331. package/.claude/worktrees/agent-a5b87d75/src/lib/summary-prompt.js +203 -0
  332. package/.claude/worktrees/agent-a5b87d75/src/lib/tokens.js +868 -0
  333. package/.claude/worktrees/agent-a5b87d75/src/lib/turn-timeout.js +52 -0
  334. package/.claude/worktrees/agent-a5b87d75/src/lib/update-checker.js +93 -0
  335. package/.claude/worktrees/agent-a5b87d75/src/lib/update-manager.js +313 -0
  336. package/.claude/worktrees/agent-a5b87d75/src/routes/a2a.js +1213 -0
  337. package/.claude/worktrees/agent-a5b87d75/src/routes/callbook.js +142 -0
  338. package/.claude/worktrees/agent-a5b87d75/src/routes/dashboard.js +1688 -0
  339. package/.claude/worktrees/agent-a5b87d75/src/server.js +1179 -0
  340. package/.claude/worktrees/agent-acefedbd/.a2a-manifest.json +70 -0
  341. package/.claude/worktrees/agent-acefedbd/.c8rc.json +16 -0
  342. package/.claude/worktrees/agent-acefedbd/.claude/a2a-skill-reference.md +462 -0
  343. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-app.md +42 -0
  344. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-call.md +26 -0
  345. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-contacts.md +31 -0
  346. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-conversations.md +47 -0
  347. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-gui.md +30 -0
  348. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-invite.md +63 -0
  349. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-setup.md +30 -0
  350. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-skills.md +27 -0
  351. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-status.md +46 -0
  352. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-uninstall.md +36 -0
  353. package/.claude/worktrees/agent-acefedbd/.claude/commands/a2a-update.md +41 -0
  354. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.a2a-manifest.json +70 -0
  355. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.c8rc.json +16 -0
  356. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/a2a-skill-reference.md +462 -0
  357. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-app.md +42 -0
  358. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-call.md +26 -0
  359. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-contacts.md +31 -0
  360. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-conversations.md +47 -0
  361. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-gui.md +30 -0
  362. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-invite.md +63 -0
  363. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-setup.md +30 -0
  364. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-skills.md +27 -0
  365. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-status.md +46 -0
  366. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-uninstall.md +36 -0
  367. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.claude/commands/a2a-update.md +41 -0
  368. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/.node-version +1 -0
  369. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/ARCHITECTURE.md +135 -0
  370. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/CLAUDE-INSTALL.md +156 -0
  371. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/CONVENTIONS.md +179 -0
  372. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/README.md +470 -0
  373. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/SKILL.md +462 -0
  374. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/bin/cli.js +3184 -0
  375. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/biome.json +27 -0
  376. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  377. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  378. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  379. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-auto-updater.md +1284 -0
  380. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  381. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  382. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  383. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  384. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  385. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  386. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/prompts/e2e-test-agent.md +368 -0
  387. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/protocol.md +440 -0
  388. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/docs/signing-setup.md +49 -0
  389. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/eslint.config.js +16 -0
  390. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/knip.json +17 -0
  391. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/index.html +179 -0
  392. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/package.json +8 -0
  393. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/Cargo.lock +5875 -0
  394. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/Cargo.toml +25 -0
  395. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/binaries/.gitkeep +0 -0
  396. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/build.rs +3 -0
  397. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/capabilities/default.json +26 -0
  398. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/entitlements.plist +14 -0
  399. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/128x128.png +0 -0
  400. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  401. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/32x32.png +0 -0
  402. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/icon.icns +0 -0
  403. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/tray-connected.png +0 -0
  404. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  405. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/discovery.rs +184 -0
  406. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/health.rs +67 -0
  407. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/lib.rs +255 -0
  408. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/main.rs +6 -0
  409. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/notifications.rs +180 -0
  410. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/server.rs +306 -0
  411. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/src/updater.rs +117 -0
  412. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/native/macos/src-tauri/tauri.conf.json +58 -0
  413. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/package.json +54 -0
  414. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/pkg.config.json +14 -0
  415. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/build-standalone.sh +106 -0
  416. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/cleanup.js +251 -0
  417. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/generate-update-manifest.sh +42 -0
  418. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/install-openclaw.js +986 -0
  419. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/install-skills.js +234 -0
  420. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/postinstall.js +224 -0
  421. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/preuninstall.js +68 -0
  422. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/run-e2e.sh +44 -0
  423. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/smoke-test-standalone.sh +101 -0
  424. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/sync-version.sh +28 -0
  425. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/scripts/verify-app-bundle.sh +34 -0
  426. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/dashboard/public/app.js +2683 -0
  427. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/dashboard/public/index.html +394 -0
  428. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/dashboard/public/style.css +1294 -0
  429. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/index.js +76 -0
  430. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/agent-card.js +111 -0
  431. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/call-monitor.js +205 -0
  432. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/callbook.js +366 -0
  433. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/claude-subagent.js +696 -0
  434. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/client.js +683 -0
  435. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/config.js +480 -0
  436. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/conversation-driver.js +608 -0
  437. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/conversations.js +830 -0
  438. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/crypto.js +113 -0
  439. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/dashboard-events.js +213 -0
  440. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/disclosure.js +792 -0
  441. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/external-ip.js +211 -0
  442. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/invite-host.js +223 -0
  443. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/local-request.js +69 -0
  444. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/logger.js +677 -0
  445. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/openclaw-integration.js +339 -0
  446. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/pid-file.js +103 -0
  447. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/port-scanner.js +83 -0
  448. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/prompt-template.js +355 -0
  449. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/runtime-adapter.js +701 -0
  450. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/summarizer.js +156 -0
  451. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/summary-formatter.js +168 -0
  452. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/summary-prompt.js +203 -0
  453. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/tokens.js +868 -0
  454. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/turn-timeout.js +52 -0
  455. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/update-checker.js +93 -0
  456. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/lib/update-manager.js +313 -0
  457. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/routes/a2a.js +1213 -0
  458. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/routes/callbook.js +142 -0
  459. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/routes/dashboard.js +1578 -0
  460. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a04dc0fe/src/server.js +1179 -0
  461. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.a2a-manifest.json +70 -0
  462. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.c8rc.json +16 -0
  463. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/a2a-skill-reference.md +462 -0
  464. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-app.md +42 -0
  465. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-call.md +26 -0
  466. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-contacts.md +31 -0
  467. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-conversations.md +47 -0
  468. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-gui.md +30 -0
  469. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-invite.md +63 -0
  470. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-setup.md +30 -0
  471. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-skills.md +27 -0
  472. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-status.md +46 -0
  473. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-uninstall.md +36 -0
  474. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.claude/commands/a2a-update.md +41 -0
  475. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/.node-version +1 -0
  476. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/ARCHITECTURE.md +135 -0
  477. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/CLAUDE-INSTALL.md +156 -0
  478. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/CONVENTIONS.md +178 -0
  479. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/README.md +470 -0
  480. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/SKILL.md +462 -0
  481. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/bin/cli.js +3184 -0
  482. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/biome.json +27 -0
  483. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  484. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  485. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  486. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-auto-updater.md +1284 -0
  487. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  488. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  489. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  490. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  491. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  492. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  493. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/prompts/e2e-test-agent.md +368 -0
  494. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/protocol.md +440 -0
  495. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/docs/signing-setup.md +49 -0
  496. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/eslint.config.js +16 -0
  497. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/knip.json +17 -0
  498. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/index.html +179 -0
  499. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/package.json +8 -0
  500. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/Cargo.lock +5875 -0
  501. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/Cargo.toml +24 -0
  502. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/binaries/.gitkeep +0 -0
  503. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/build.rs +3 -0
  504. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/capabilities/default.json +26 -0
  505. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/dmg-background.png +0 -0
  506. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/dmg-background@2x.png +0 -0
  507. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/entitlements.plist +14 -0
  508. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/128x128.png +0 -0
  509. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  510. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/256x256.png +0 -0
  511. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/32x32.png +0 -0
  512. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/512x512.png +0 -0
  513. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/512x512@2x.png +0 -0
  514. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/icon.icns +0 -0
  515. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/tray-connected.png +0 -0
  516. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  517. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/discovery.rs +184 -0
  518. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/health.rs +67 -0
  519. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/lib.rs +226 -0
  520. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/main.rs +6 -0
  521. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/notifications.rs +180 -0
  522. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/src/server.rs +306 -0
  523. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/tauri.conf.json +60 -0
  524. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/native/macos/src-tauri/volume-icon.icns +0 -0
  525. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/package.json +54 -0
  526. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/pkg.config.json +14 -0
  527. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/build-standalone.sh +106 -0
  528. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/cleanup.js +251 -0
  529. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/install-openclaw.js +986 -0
  530. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/install-skills.js +234 -0
  531. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/postinstall.js +224 -0
  532. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/preuninstall.js +68 -0
  533. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/run-e2e.sh +44 -0
  534. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/smoke-test-standalone.sh +101 -0
  535. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/sync-version.sh +28 -0
  536. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/scripts/verify-app-bundle.sh +34 -0
  537. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/dashboard/public/app.js +2649 -0
  538. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/dashboard/public/index.html +386 -0
  539. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/dashboard/public/style.css +1274 -0
  540. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/index.js +76 -0
  541. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/agent-card.js +111 -0
  542. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/call-monitor.js +205 -0
  543. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/callbook.js +366 -0
  544. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/claude-subagent.js +696 -0
  545. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/client.js +683 -0
  546. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/config.js +480 -0
  547. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/conversation-driver.js +608 -0
  548. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/conversations.js +830 -0
  549. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/crypto.js +113 -0
  550. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/dashboard-events.js +213 -0
  551. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/disclosure.js +792 -0
  552. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/external-ip.js +211 -0
  553. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/invite-host.js +223 -0
  554. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/local-request.js +69 -0
  555. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/logger.js +677 -0
  556. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/openclaw-integration.js +339 -0
  557. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/pid-file.js +103 -0
  558. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/port-scanner.js +83 -0
  559. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/prompt-template.js +355 -0
  560. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/runtime-adapter.js +701 -0
  561. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/summarizer.js +156 -0
  562. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/summary-formatter.js +168 -0
  563. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/summary-prompt.js +203 -0
  564. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/tokens.js +868 -0
  565. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/turn-timeout.js +52 -0
  566. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/update-checker.js +93 -0
  567. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/lib/update-manager.js +313 -0
  568. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/routes/a2a.js +1213 -0
  569. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/routes/callbook.js +142 -0
  570. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/routes/dashboard.js +1578 -0
  571. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-a1418bc4/src/server.js +1179 -0
  572. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.a2a-manifest.json +70 -0
  573. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.c8rc.json +16 -0
  574. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/a2a-skill-reference.md +462 -0
  575. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-app.md +42 -0
  576. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-call.md +26 -0
  577. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-contacts.md +31 -0
  578. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-conversations.md +47 -0
  579. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-gui.md +30 -0
  580. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-invite.md +63 -0
  581. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-setup.md +30 -0
  582. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-skills.md +27 -0
  583. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-status.md +46 -0
  584. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-uninstall.md +36 -0
  585. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.claude/commands/a2a-update.md +41 -0
  586. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/.node-version +1 -0
  587. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/ARCHITECTURE.md +135 -0
  588. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/CLAUDE-INSTALL.md +156 -0
  589. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/CONVENTIONS.md +169 -0
  590. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/README.md +470 -0
  591. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/SKILL.md +462 -0
  592. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/bin/cli.js +3184 -0
  593. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/biome.json +27 -0
  594. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/app.js +160 -0
  595. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  596. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/assets/icon-32.png +0 -0
  597. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/assets/icon-64.png +0 -0
  598. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/index.html +166 -0
  599. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  600. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  601. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-auto-updater.md +1284 -0
  602. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  603. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  604. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  605. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  606. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  607. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  608. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/prompts/e2e-test-agent.md +368 -0
  609. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/protocol.md +440 -0
  610. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/signing-setup.md +49 -0
  611. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/docs/style.css +457 -0
  612. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/eslint.config.js +16 -0
  613. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/knip.json +17 -0
  614. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/index.html +179 -0
  615. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/package.json +8 -0
  616. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/Cargo.lock +5875 -0
  617. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/Cargo.toml +24 -0
  618. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/binaries/.gitkeep +0 -0
  619. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/build.rs +3 -0
  620. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/capabilities/default.json +26 -0
  621. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/entitlements.plist +14 -0
  622. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/128x128.png +0 -0
  623. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  624. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/32x32.png +0 -0
  625. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/icon.icns +0 -0
  626. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/tray-connected.png +0 -0
  627. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  628. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/discovery.rs +184 -0
  629. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/health.rs +67 -0
  630. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/lib.rs +226 -0
  631. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/main.rs +6 -0
  632. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/notifications.rs +180 -0
  633. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/src/server.rs +306 -0
  634. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/native/macos/src-tauri/tauri.conf.json +50 -0
  635. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/package.json +54 -0
  636. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/pkg.config.json +14 -0
  637. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/build-standalone.sh +106 -0
  638. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/cleanup.js +251 -0
  639. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/install-openclaw.js +986 -0
  640. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/install-skills.js +234 -0
  641. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/postinstall.js +224 -0
  642. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/preuninstall.js +68 -0
  643. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/run-e2e.sh +44 -0
  644. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/smoke-test-standalone.sh +101 -0
  645. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/sync-version.sh +28 -0
  646. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/scripts/verify-app-bundle.sh +34 -0
  647. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/dashboard/public/app.js +2649 -0
  648. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/dashboard/public/index.html +386 -0
  649. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/dashboard/public/style.css +1274 -0
  650. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/index.js +76 -0
  651. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/agent-card.js +111 -0
  652. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/call-monitor.js +205 -0
  653. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/callbook.js +366 -0
  654. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/claude-subagent.js +696 -0
  655. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/client.js +683 -0
  656. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/config.js +480 -0
  657. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/conversation-driver.js +608 -0
  658. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/conversations.js +830 -0
  659. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/crypto.js +113 -0
  660. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/dashboard-events.js +213 -0
  661. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/disclosure.js +792 -0
  662. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/external-ip.js +211 -0
  663. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/invite-host.js +223 -0
  664. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/local-request.js +69 -0
  665. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/logger.js +677 -0
  666. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/openclaw-integration.js +339 -0
  667. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/pid-file.js +103 -0
  668. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/port-scanner.js +83 -0
  669. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/prompt-template.js +355 -0
  670. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/runtime-adapter.js +701 -0
  671. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/summarizer.js +156 -0
  672. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/summary-formatter.js +168 -0
  673. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/summary-prompt.js +203 -0
  674. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/tokens.js +868 -0
  675. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/turn-timeout.js +52 -0
  676. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/update-checker.js +93 -0
  677. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/lib/update-manager.js +313 -0
  678. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/routes/a2a.js +1213 -0
  679. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/routes/callbook.js +142 -0
  680. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/routes/dashboard.js +1578 -0
  681. package/.claude/worktrees/agent-acefedbd/.claude/worktrees/agent-aa4cd927/src/server.js +1179 -0
  682. package/.claude/worktrees/agent-acefedbd/.node-version +1 -0
  683. package/.claude/worktrees/agent-acefedbd/ARCHITECTURE.md +135 -0
  684. package/.claude/worktrees/agent-acefedbd/CLAUDE-INSTALL.md +156 -0
  685. package/.claude/worktrees/agent-acefedbd/CONVENTIONS.md +177 -0
  686. package/.claude/worktrees/agent-acefedbd/README.md +470 -0
  687. package/.claude/worktrees/agent-acefedbd/SKILL.md +462 -0
  688. package/.claude/worktrees/agent-acefedbd/bin/cli.js +3184 -0
  689. package/.claude/worktrees/agent-acefedbd/biome.json +27 -0
  690. package/.claude/worktrees/agent-acefedbd/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  691. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  692. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  693. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-auto-updater.md +1284 -0
  694. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  695. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  696. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  697. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  698. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  699. package/.claude/worktrees/agent-acefedbd/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  700. package/.claude/worktrees/agent-acefedbd/docs/prompts/e2e-test-agent.md +368 -0
  701. package/.claude/worktrees/agent-acefedbd/docs/protocol.md +440 -0
  702. package/.claude/worktrees/agent-acefedbd/docs/signing-setup.md +49 -0
  703. package/.claude/worktrees/agent-acefedbd/eslint.config.js +16 -0
  704. package/.claude/worktrees/agent-acefedbd/knip.json +17 -0
  705. package/.claude/worktrees/agent-acefedbd/native/macos/index.html +182 -0
  706. package/.claude/worktrees/agent-acefedbd/native/macos/package.json +8 -0
  707. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/Cargo.lock +5875 -0
  708. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/Cargo.toml +24 -0
  709. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/binaries/.gitkeep +0 -0
  710. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/build.rs +3 -0
  711. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/capabilities/default.json +26 -0
  712. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/entitlements.plist +14 -0
  713. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/128x128.png +0 -0
  714. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  715. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/32x32.png +0 -0
  716. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/icon.icns +0 -0
  717. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/tray-connected.png +0 -0
  718. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  719. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/discovery.rs +221 -0
  720. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/health.rs +67 -0
  721. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/lib.rs +226 -0
  722. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/main.rs +6 -0
  723. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/notifications.rs +180 -0
  724. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/src/server.rs +306 -0
  725. package/.claude/worktrees/agent-acefedbd/native/macos/src-tauri/tauri.conf.json +50 -0
  726. package/.claude/worktrees/agent-acefedbd/package.json +54 -0
  727. package/.claude/worktrees/agent-acefedbd/pkg.config.json +14 -0
  728. package/.claude/worktrees/agent-acefedbd/scripts/build-standalone.sh +106 -0
  729. package/.claude/worktrees/agent-acefedbd/scripts/cleanup.js +251 -0
  730. package/.claude/worktrees/agent-acefedbd/scripts/install-openclaw.js +986 -0
  731. package/.claude/worktrees/agent-acefedbd/scripts/install-skills.js +234 -0
  732. package/.claude/worktrees/agent-acefedbd/scripts/postinstall.js +224 -0
  733. package/.claude/worktrees/agent-acefedbd/scripts/preuninstall.js +68 -0
  734. package/.claude/worktrees/agent-acefedbd/scripts/run-e2e.sh +44 -0
  735. package/.claude/worktrees/agent-acefedbd/scripts/smoke-test-standalone.sh +101 -0
  736. package/.claude/worktrees/agent-acefedbd/scripts/sync-version.sh +28 -0
  737. package/.claude/worktrees/agent-acefedbd/scripts/verify-app-bundle.sh +34 -0
  738. package/.claude/worktrees/agent-acefedbd/src/dashboard/public/app.js +2649 -0
  739. package/.claude/worktrees/agent-acefedbd/src/dashboard/public/index.html +386 -0
  740. package/.claude/worktrees/agent-acefedbd/src/dashboard/public/onboarding.css +269 -0
  741. package/.claude/worktrees/agent-acefedbd/src/dashboard/public/onboarding.html +96 -0
  742. package/.claude/worktrees/agent-acefedbd/src/dashboard/public/onboarding.js +277 -0
  743. package/.claude/worktrees/agent-acefedbd/src/dashboard/public/style.css +1274 -0
  744. package/.claude/worktrees/agent-acefedbd/src/index.js +76 -0
  745. package/.claude/worktrees/agent-acefedbd/src/lib/agent-card.js +111 -0
  746. package/.claude/worktrees/agent-acefedbd/src/lib/call-monitor.js +205 -0
  747. package/.claude/worktrees/agent-acefedbd/src/lib/callbook.js +366 -0
  748. package/.claude/worktrees/agent-acefedbd/src/lib/claude-subagent.js +696 -0
  749. package/.claude/worktrees/agent-acefedbd/src/lib/client.js +683 -0
  750. package/.claude/worktrees/agent-acefedbd/src/lib/config.js +480 -0
  751. package/.claude/worktrees/agent-acefedbd/src/lib/conversation-driver.js +608 -0
  752. package/.claude/worktrees/agent-acefedbd/src/lib/conversations.js +830 -0
  753. package/.claude/worktrees/agent-acefedbd/src/lib/crypto.js +113 -0
  754. package/.claude/worktrees/agent-acefedbd/src/lib/dashboard-events.js +213 -0
  755. package/.claude/worktrees/agent-acefedbd/src/lib/disclosure.js +792 -0
  756. package/.claude/worktrees/agent-acefedbd/src/lib/external-ip.js +211 -0
  757. package/.claude/worktrees/agent-acefedbd/src/lib/invite-host.js +223 -0
  758. package/.claude/worktrees/agent-acefedbd/src/lib/local-request.js +69 -0
  759. package/.claude/worktrees/agent-acefedbd/src/lib/logger.js +677 -0
  760. package/.claude/worktrees/agent-acefedbd/src/lib/openclaw-integration.js +339 -0
  761. package/.claude/worktrees/agent-acefedbd/src/lib/pid-file.js +103 -0
  762. package/.claude/worktrees/agent-acefedbd/src/lib/port-scanner.js +83 -0
  763. package/.claude/worktrees/agent-acefedbd/src/lib/prompt-template.js +355 -0
  764. package/.claude/worktrees/agent-acefedbd/src/lib/runtime-adapter.js +701 -0
  765. package/.claude/worktrees/agent-acefedbd/src/lib/summarizer.js +156 -0
  766. package/.claude/worktrees/agent-acefedbd/src/lib/summary-formatter.js +168 -0
  767. package/.claude/worktrees/agent-acefedbd/src/lib/summary-prompt.js +203 -0
  768. package/.claude/worktrees/agent-acefedbd/src/lib/tokens.js +868 -0
  769. package/.claude/worktrees/agent-acefedbd/src/lib/turn-timeout.js +52 -0
  770. package/.claude/worktrees/agent-acefedbd/src/lib/update-checker.js +93 -0
  771. package/.claude/worktrees/agent-acefedbd/src/lib/update-manager.js +313 -0
  772. package/.claude/worktrees/agent-acefedbd/src/routes/a2a.js +1213 -0
  773. package/.claude/worktrees/agent-acefedbd/src/routes/callbook.js +142 -0
  774. package/.claude/worktrees/agent-acefedbd/src/routes/dashboard.js +1688 -0
  775. package/.claude/worktrees/agent-acefedbd/src/server.js +1179 -0
  776. package/.claude/worktrees/agent-ad420869/.a2a-manifest.json +70 -0
  777. package/.claude/worktrees/agent-ad420869/.c8rc.json +16 -0
  778. package/.claude/worktrees/agent-ad420869/.claude/a2a-skill-reference.md +462 -0
  779. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-app.md +42 -0
  780. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-call.md +26 -0
  781. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-contacts.md +31 -0
  782. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-conversations.md +47 -0
  783. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-gui.md +30 -0
  784. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-invite.md +63 -0
  785. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-setup.md +30 -0
  786. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-skills.md +27 -0
  787. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-status.md +46 -0
  788. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-uninstall.md +36 -0
  789. package/.claude/worktrees/agent-ad420869/.claude/commands/a2a-update.md +41 -0
  790. package/.claude/worktrees/agent-ad420869/.node-version +1 -0
  791. package/.claude/worktrees/agent-ad420869/ARCHITECTURE.md +135 -0
  792. package/.claude/worktrees/agent-ad420869/CLAUDE-INSTALL.md +156 -0
  793. package/.claude/worktrees/agent-ad420869/CONVENTIONS.md +178 -0
  794. package/.claude/worktrees/agent-ad420869/README.md +470 -0
  795. package/.claude/worktrees/agent-ad420869/SKILL.md +462 -0
  796. package/.claude/worktrees/agent-ad420869/bin/cli.js +3184 -0
  797. package/.claude/worktrees/agent-ad420869/biome.json +27 -0
  798. package/.claude/worktrees/agent-ad420869/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  799. package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  800. package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  801. package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-auto-updater.md +1284 -0
  802. package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  803. package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  804. package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  805. package/.claude/worktrees/agent-ad420869/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  806. package/.claude/worktrees/agent-ad420869/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  807. package/.claude/worktrees/agent-ad420869/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  808. package/.claude/worktrees/agent-ad420869/docs/prompts/e2e-test-agent.md +368 -0
  809. package/.claude/worktrees/agent-ad420869/docs/protocol.md +440 -0
  810. package/.claude/worktrees/agent-ad420869/docs/signing-setup.md +49 -0
  811. package/.claude/worktrees/agent-ad420869/eslint.config.js +16 -0
  812. package/.claude/worktrees/agent-ad420869/knip.json +17 -0
  813. package/.claude/worktrees/agent-ad420869/native/macos/index.html +182 -0
  814. package/.claude/worktrees/agent-ad420869/native/macos/package.json +8 -0
  815. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/Cargo.lock +5875 -0
  816. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/Cargo.toml +24 -0
  817. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/binaries/.gitkeep +0 -0
  818. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/build.rs +3 -0
  819. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/capabilities/default.json +26 -0
  820. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/entitlements.plist +14 -0
  821. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/128x128.png +0 -0
  822. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  823. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/32x32.png +0 -0
  824. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/icon.icns +0 -0
  825. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/tray-connected.png +0 -0
  826. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  827. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/discovery.rs +221 -0
  828. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/health.rs +67 -0
  829. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/lib.rs +226 -0
  830. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/main.rs +6 -0
  831. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/notifications.rs +180 -0
  832. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/src/server.rs +306 -0
  833. package/.claude/worktrees/agent-ad420869/native/macos/src-tauri/tauri.conf.json +50 -0
  834. package/.claude/worktrees/agent-ad420869/package.json +54 -0
  835. package/.claude/worktrees/agent-ad420869/pkg.config.json +14 -0
  836. package/.claude/worktrees/agent-ad420869/scripts/build-standalone.sh +106 -0
  837. package/.claude/worktrees/agent-ad420869/scripts/cleanup.js +251 -0
  838. package/.claude/worktrees/agent-ad420869/scripts/install-openclaw.js +986 -0
  839. package/.claude/worktrees/agent-ad420869/scripts/install-skills.js +234 -0
  840. package/.claude/worktrees/agent-ad420869/scripts/postinstall.js +224 -0
  841. package/.claude/worktrees/agent-ad420869/scripts/preuninstall.js +68 -0
  842. package/.claude/worktrees/agent-ad420869/scripts/run-e2e.sh +44 -0
  843. package/.claude/worktrees/agent-ad420869/scripts/smoke-test-standalone.sh +101 -0
  844. package/.claude/worktrees/agent-ad420869/scripts/sync-version.sh +28 -0
  845. package/.claude/worktrees/agent-ad420869/scripts/verify-app-bundle.sh +34 -0
  846. package/.claude/worktrees/agent-ad420869/src/dashboard/public/app.js +2683 -0
  847. package/.claude/worktrees/agent-ad420869/src/dashboard/public/index.html +394 -0
  848. package/.claude/worktrees/agent-ad420869/src/dashboard/public/onboarding.css +269 -0
  849. package/.claude/worktrees/agent-ad420869/src/dashboard/public/onboarding.html +96 -0
  850. package/.claude/worktrees/agent-ad420869/src/dashboard/public/onboarding.js +277 -0
  851. package/.claude/worktrees/agent-ad420869/src/dashboard/public/style.css +1294 -0
  852. package/.claude/worktrees/agent-ad420869/src/index.js +76 -0
  853. package/.claude/worktrees/agent-ad420869/src/lib/agent-card.js +111 -0
  854. package/.claude/worktrees/agent-ad420869/src/lib/call-monitor.js +205 -0
  855. package/.claude/worktrees/agent-ad420869/src/lib/callbook.js +366 -0
  856. package/.claude/worktrees/agent-ad420869/src/lib/claude-subagent.js +696 -0
  857. package/.claude/worktrees/agent-ad420869/src/lib/client.js +683 -0
  858. package/.claude/worktrees/agent-ad420869/src/lib/config.js +480 -0
  859. package/.claude/worktrees/agent-ad420869/src/lib/conversation-driver.js +608 -0
  860. package/.claude/worktrees/agent-ad420869/src/lib/conversations.js +830 -0
  861. package/.claude/worktrees/agent-ad420869/src/lib/crypto.js +113 -0
  862. package/.claude/worktrees/agent-ad420869/src/lib/dashboard-events.js +213 -0
  863. package/.claude/worktrees/agent-ad420869/src/lib/disclosure.js +792 -0
  864. package/.claude/worktrees/agent-ad420869/src/lib/external-ip.js +211 -0
  865. package/.claude/worktrees/agent-ad420869/src/lib/invite-host.js +223 -0
  866. package/.claude/worktrees/agent-ad420869/src/lib/local-request.js +69 -0
  867. package/.claude/worktrees/agent-ad420869/src/lib/logger.js +677 -0
  868. package/.claude/worktrees/agent-ad420869/src/lib/openclaw-integration.js +339 -0
  869. package/.claude/worktrees/agent-ad420869/src/lib/pid-file.js +103 -0
  870. package/.claude/worktrees/agent-ad420869/src/lib/port-scanner.js +83 -0
  871. package/.claude/worktrees/agent-ad420869/src/lib/prompt-template.js +355 -0
  872. package/.claude/worktrees/agent-ad420869/src/lib/runtime-adapter.js +701 -0
  873. package/.claude/worktrees/agent-ad420869/src/lib/summarizer.js +156 -0
  874. package/.claude/worktrees/agent-ad420869/src/lib/summary-formatter.js +168 -0
  875. package/.claude/worktrees/agent-ad420869/src/lib/summary-prompt.js +203 -0
  876. package/.claude/worktrees/agent-ad420869/src/lib/tokens.js +868 -0
  877. package/.claude/worktrees/agent-ad420869/src/lib/turn-timeout.js +52 -0
  878. package/.claude/worktrees/agent-ad420869/src/lib/update-checker.js +93 -0
  879. package/.claude/worktrees/agent-ad420869/src/lib/update-manager.js +313 -0
  880. package/.claude/worktrees/agent-ad420869/src/routes/a2a.js +1213 -0
  881. package/.claude/worktrees/agent-ad420869/src/routes/callbook.js +142 -0
  882. package/.claude/worktrees/agent-ad420869/src/routes/dashboard.js +1688 -0
  883. package/.claude/worktrees/agent-ad420869/src/server.js +1179 -0
  884. package/.claude/worktrees/agent-af1f3b59/.c8rc.json +16 -0
  885. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-app.md +42 -0
  886. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-call.md +26 -0
  887. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-contacts.md +31 -0
  888. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-conversations.md +47 -0
  889. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-gui.md +30 -0
  890. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-invite.md +63 -0
  891. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-setup.md +30 -0
  892. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-skills.md +27 -0
  893. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-status.md +46 -0
  894. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-uninstall.md +36 -0
  895. package/.claude/worktrees/agent-af1f3b59/.claude/commands/a2a-update.md +41 -0
  896. package/.claude/worktrees/agent-af1f3b59/.node-version +1 -0
  897. package/.claude/worktrees/agent-af1f3b59/ARCHITECTURE.md +135 -0
  898. package/.claude/worktrees/agent-af1f3b59/CLAUDE-INSTALL.md +156 -0
  899. package/.claude/worktrees/agent-af1f3b59/CONVENTIONS.md +178 -0
  900. package/.claude/worktrees/agent-af1f3b59/README.md +470 -0
  901. package/.claude/worktrees/agent-af1f3b59/SKILL.md +462 -0
  902. package/.claude/worktrees/agent-af1f3b59/bin/cli.js +3184 -0
  903. package/.claude/worktrees/agent-af1f3b59/biome.json +27 -0
  904. package/.claude/worktrees/agent-af1f3b59/docs/assessments/2026-02-27-google-a2a-protocol-assessment.md +292 -0
  905. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-15-port-fallback-warning-design.md +611 -0
  906. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-a2a-callbook-macos-app.md +1660 -0
  907. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-auto-updater.md +1284 -0
  908. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-bugfixes-22-24.md +246 -0
  909. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
  910. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-16-orphan-process-fix.md +962 -0
  911. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
  912. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-03-01-a2a-68-openclaw-integration-tests.md +676 -0
  913. package/.claude/worktrees/agent-af1f3b59/docs/plans/2026-03-01-a2a-77-invoke-security-tests.md +661 -0
  914. package/.claude/worktrees/agent-af1f3b59/docs/prompts/e2e-test-agent.md +368 -0
  915. package/.claude/worktrees/agent-af1f3b59/docs/protocol.md +440 -0
  916. package/.claude/worktrees/agent-af1f3b59/docs/signing-setup.md +49 -0
  917. package/.claude/worktrees/agent-af1f3b59/eslint.config.js +16 -0
  918. package/.claude/worktrees/agent-af1f3b59/knip.json +17 -0
  919. package/.claude/worktrees/agent-af1f3b59/native/macos/index.html +179 -0
  920. package/.claude/worktrees/agent-af1f3b59/native/macos/package.json +8 -0
  921. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/Cargo.lock +5875 -0
  922. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/Cargo.toml +24 -0
  923. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/binaries/.gitkeep +0 -0
  924. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/build.rs +3 -0
  925. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/capabilities/default.json +26 -0
  926. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/dmg-background.png +0 -0
  927. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/dmg-background@2x.png +0 -0
  928. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/entitlements.plist +14 -0
  929. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/128x128.png +0 -0
  930. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/128x128@2x.png +0 -0
  931. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/256x256.png +0 -0
  932. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/32x32.png +0 -0
  933. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/512x512.png +0 -0
  934. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/512x512@2x.png +0 -0
  935. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/icon.icns +0 -0
  936. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/tray-connected.png +0 -0
  937. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/icons/tray-disconnected.png +0 -0
  938. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/discovery.rs +184 -0
  939. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/health.rs +67 -0
  940. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/lib.rs +226 -0
  941. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/main.rs +6 -0
  942. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/notifications.rs +180 -0
  943. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/src/server.rs +306 -0
  944. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/tauri.conf.json +60 -0
  945. package/.claude/worktrees/agent-af1f3b59/native/macos/src-tauri/volume-icon.icns +0 -0
  946. package/.claude/worktrees/agent-af1f3b59/package.json +54 -0
  947. package/.claude/worktrees/agent-af1f3b59/pkg.config.json +14 -0
  948. package/.claude/worktrees/agent-af1f3b59/scripts/build-standalone.sh +106 -0
  949. package/.claude/worktrees/agent-af1f3b59/scripts/cleanup.js +251 -0
  950. package/.claude/worktrees/agent-af1f3b59/scripts/install-openclaw.js +986 -0
  951. package/.claude/worktrees/agent-af1f3b59/scripts/install-skills.js +234 -0
  952. package/.claude/worktrees/agent-af1f3b59/scripts/postinstall.js +224 -0
  953. package/.claude/worktrees/agent-af1f3b59/scripts/preuninstall.js +68 -0
  954. package/.claude/worktrees/agent-af1f3b59/scripts/run-e2e.sh +44 -0
  955. package/.claude/worktrees/agent-af1f3b59/scripts/smoke-test-standalone.sh +101 -0
  956. package/.claude/worktrees/agent-af1f3b59/scripts/sync-version.sh +28 -0
  957. package/.claude/worktrees/agent-af1f3b59/scripts/verify-app-bundle.sh +34 -0
  958. package/.claude/worktrees/agent-af1f3b59/src/dashboard/public/app.js +2649 -0
  959. package/.claude/worktrees/agent-af1f3b59/src/dashboard/public/index.html +386 -0
  960. package/.claude/worktrees/agent-af1f3b59/src/dashboard/public/style.css +1274 -0
  961. package/.claude/worktrees/agent-af1f3b59/src/index.js +76 -0
  962. package/.claude/worktrees/agent-af1f3b59/src/lib/agent-card.js +111 -0
  963. package/.claude/worktrees/agent-af1f3b59/src/lib/call-monitor.js +205 -0
  964. package/.claude/worktrees/agent-af1f3b59/src/lib/callbook.js +366 -0
  965. package/.claude/worktrees/agent-af1f3b59/src/lib/claude-subagent.js +696 -0
  966. package/.claude/worktrees/agent-af1f3b59/src/lib/client.js +683 -0
  967. package/.claude/worktrees/agent-af1f3b59/src/lib/config.js +480 -0
  968. package/.claude/worktrees/agent-af1f3b59/src/lib/conversation-driver.js +608 -0
  969. package/.claude/worktrees/agent-af1f3b59/src/lib/conversations.js +830 -0
  970. package/.claude/worktrees/agent-af1f3b59/src/lib/crypto.js +113 -0
  971. package/.claude/worktrees/agent-af1f3b59/src/lib/dashboard-events.js +213 -0
  972. package/.claude/worktrees/agent-af1f3b59/src/lib/disclosure.js +792 -0
  973. package/.claude/worktrees/agent-af1f3b59/src/lib/external-ip.js +211 -0
  974. package/.claude/worktrees/agent-af1f3b59/src/lib/invite-host.js +223 -0
  975. package/.claude/worktrees/agent-af1f3b59/src/lib/local-request.js +69 -0
  976. package/.claude/worktrees/agent-af1f3b59/src/lib/logger.js +677 -0
  977. package/.claude/worktrees/agent-af1f3b59/src/lib/openclaw-integration.js +339 -0
  978. package/.claude/worktrees/agent-af1f3b59/src/lib/pid-file.js +103 -0
  979. package/.claude/worktrees/agent-af1f3b59/src/lib/port-scanner.js +83 -0
  980. package/.claude/worktrees/agent-af1f3b59/src/lib/prompt-template.js +355 -0
  981. package/.claude/worktrees/agent-af1f3b59/src/lib/runtime-adapter.js +701 -0
  982. package/.claude/worktrees/agent-af1f3b59/src/lib/summarizer.js +156 -0
  983. package/.claude/worktrees/agent-af1f3b59/src/lib/summary-formatter.js +168 -0
  984. package/.claude/worktrees/agent-af1f3b59/src/lib/summary-prompt.js +203 -0
  985. package/.claude/worktrees/agent-af1f3b59/src/lib/tokens.js +868 -0
  986. package/.claude/worktrees/agent-af1f3b59/src/lib/turn-timeout.js +52 -0
  987. package/.claude/worktrees/agent-af1f3b59/src/lib/update-checker.js +93 -0
  988. package/.claude/worktrees/agent-af1f3b59/src/lib/update-manager.js +313 -0
  989. package/.claude/worktrees/agent-af1f3b59/src/routes/a2a.js +1213 -0
  990. package/.claude/worktrees/agent-af1f3b59/src/routes/callbook.js +142 -0
  991. package/.claude/worktrees/agent-af1f3b59/src/routes/dashboard.js +1578 -0
  992. package/.claude/worktrees/agent-af1f3b59/src/server.js +1179 -0
  993. package/CONVENTIONS.md +39 -0
  994. package/docs/app.js +30 -0
  995. package/docs/assets/icon-32.png +0 -0
  996. package/docs/assets/icon-64.png +0 -0
  997. package/docs/index.html +117 -0
  998. package/docs/plans/2026-03-06-a2a-100-tauri-auto-updater.md +519 -0
  999. package/docs/plans/2026-03-06-a2a-101-dmg-installer-polish.md +229 -0
  1000. package/docs/plans/2026-03-06-a2a-102-landing-page.md +611 -0
  1001. package/docs/plans/2026-03-06-a2a-103-standalone-e2e-tests.md +810 -0
  1002. package/docs/plans/2026-03-06-a2a-99-native-onboarding-wizard.md +1261 -0
  1003. package/docs/style.css +209 -0
  1004. package/native/macos/index.html +7 -4
  1005. package/native/macos/src-tauri/Cargo.toml +1 -0
  1006. package/native/macos/src-tauri/dmg-background.png +0 -0
  1007. package/native/macos/src-tauri/dmg-background@2x.png +0 -0
  1008. package/native/macos/src-tauri/icons/256x256.png +0 -0
  1009. package/native/macos/src-tauri/icons/512x512.png +0 -0
  1010. package/native/macos/src-tauri/icons/512x512@2x.png +0 -0
  1011. package/native/macos/src-tauri/src/discovery.rs +37 -0
  1012. package/native/macos/src-tauri/src/lib.rs +32 -2
  1013. package/native/macos/src-tauri/src/updater.rs +124 -0
  1014. package/native/macos/src-tauri/tauri.conf.json +21 -3
  1015. package/native/macos/src-tauri/volume-icon.icns +0 -0
  1016. package/package.json +2 -1
  1017. package/scripts/generate-update-manifest.sh +42 -0
  1018. package/scripts/run-e2e.sh +8 -0
  1019. package/src/dashboard/public/app.js +34 -0
  1020. package/src/dashboard/public/index.html +7 -0
  1021. package/src/dashboard/public/onboarding.css +269 -0
  1022. package/src/dashboard/public/onboarding.html +96 -0
  1023. package/src/dashboard/public/onboarding.js +277 -0
  1024. package/src/dashboard/public/style.css +20 -0
  1025. package/src/routes/dashboard.js +110 -0
@@ -0,0 +1,2683 @@
1
+ const state = {
2
+ settings: null,
3
+ dashboardStatus: null,
4
+ autoUpdate: null,
5
+ callbookDevices: [],
6
+ contacts: [],
7
+ selectedContactId: null,
8
+ selectedContactCalls: [],
9
+ contactCallResult: null,
10
+ calls: [],
11
+ invites: [],
12
+ logs: [],
13
+ logStats: null,
14
+ trace: null,
15
+ realtime: {
16
+ connected: false,
17
+ lastEventId: null
18
+ },
19
+ // A2A-47: Track active panel for sidebar navigation (replaces sl-tab-group)
20
+ activeTab: 'contacts',
21
+ // A2A-48: Track currently selected tier for the permissions panel.
22
+ // Replaces the old #tier-select dropdown value.
23
+ activeTierId: 'public'
24
+ };
25
+
26
+ let dashboardEventSource = null;
27
+ let reconnectTimer = null;
28
+ let refreshTimer = null;
29
+
30
+ function showNotice(message) {
31
+ const el = document.getElementById('notice');
32
+ el.textContent = message;
33
+ el.style.display = 'block';
34
+ setTimeout(() => {
35
+ el.style.display = 'none';
36
+ }, 3500);
37
+ }
38
+
39
+ function scheduleRealtimeRefresh() {
40
+ clearTimeout(refreshTimer);
41
+ refreshTimer = setTimeout(() => {
42
+ Promise.all([
43
+ loadContacts().catch(() => {}),
44
+ loadCalls().catch(() => {})
45
+ ]).catch(() => {});
46
+ }, 250);
47
+ }
48
+
49
+ function notifyRealtime(title, body) {
50
+ const safeTitle = String(title || '').trim();
51
+ if (!safeTitle) return;
52
+ const safeBody = String(body || '').trim();
53
+ if (typeof window.Notification === 'undefined') return;
54
+ if (Notification.permission === 'granted') {
55
+ try {
56
+ // In A2A.app WebView this maps to native macOS notifications.
57
+ new Notification(safeTitle, safeBody ? { body: safeBody } : undefined);
58
+ } catch (err) {
59
+ // Ignore notification errors.
60
+ }
61
+ return;
62
+ }
63
+ if (Notification.permission !== 'denied') {
64
+ Notification.requestPermission().catch(() => {});
65
+ }
66
+ }
67
+
68
+ function handleRealtimeEvent(eventData) {
69
+ const type = String(eventData?.type || '').trim();
70
+ const payload = eventData?.payload || {};
71
+ if (!type) return;
72
+
73
+ if (type === 'call.inbound') {
74
+ const caller = payload.caller_name || 'Unknown agent';
75
+ showNotice(`Inbound call: ${caller}`);
76
+ notifyRealtime(`Inbound call from ${caller}`, 'Open A2A Callbook to respond.');
77
+ scheduleRealtimeRefresh();
78
+ return;
79
+ }
80
+
81
+ if (type === 'summary.completed') {
82
+ const contact = payload.contact_name || 'conversation';
83
+ showNotice(`Summary complete: ${contact}`);
84
+ notifyRealtime('Summary complete', `Conversation with ${contact} has a summary.`);
85
+ scheduleRealtimeRefresh();
86
+ return;
87
+ }
88
+
89
+ if (type === 'contact.status.changed') {
90
+ const contactId = String(payload.contact_id || '');
91
+ const status = String(payload.status || '');
92
+ if (contactId && status) {
93
+ state.contacts = (state.contacts || []).map((contact) => {
94
+ if (String(contact.id) !== contactId) return contact;
95
+ return { ...contact, status };
96
+ });
97
+ renderContacts();
98
+ renderContactDetail();
99
+ } else {
100
+ scheduleRealtimeRefresh();
101
+ }
102
+ return;
103
+ }
104
+
105
+ if (type === 'invite.used') {
106
+ showNotice('Callbook install link used');
107
+ loadCallbookDevices().catch(() => {});
108
+ return;
109
+ }
110
+
111
+ if (type === 'call.updated') {
112
+ scheduleRealtimeRefresh();
113
+ return;
114
+ }
115
+ }
116
+
117
+ function connectRealtimeEvents() {
118
+ clearTimeout(reconnectTimer);
119
+ if (dashboardEventSource) {
120
+ dashboardEventSource.close();
121
+ dashboardEventSource = null;
122
+ }
123
+
124
+ const qs = new URLSearchParams();
125
+ if (state.realtime.lastEventId) {
126
+ qs.set('since', String(state.realtime.lastEventId));
127
+ }
128
+ const endpoint = `/api/a2a/dashboard/events${qs.toString() ? `?${qs.toString()}` : ''}`;
129
+ const source = new EventSource(endpoint);
130
+ dashboardEventSource = source;
131
+
132
+ source.onopen = () => {
133
+ state.realtime.connected = true;
134
+ };
135
+
136
+ source.onerror = () => {
137
+ state.realtime.connected = false;
138
+ if (dashboardEventSource === source) {
139
+ dashboardEventSource.close();
140
+ dashboardEventSource = null;
141
+ }
142
+ reconnectTimer = setTimeout(connectRealtimeEvents, 1500);
143
+ };
144
+
145
+ const onAnyEvent = (evt) => {
146
+ let payload = null;
147
+ try {
148
+ payload = evt?.data ? JSON.parse(evt.data) : null;
149
+ } catch (_) {
150
+ payload = null;
151
+ }
152
+ if (!payload || typeof payload !== 'object') return;
153
+ if (payload.id) {
154
+ state.realtime.lastEventId = String(payload.id);
155
+ } else if (evt?.lastEventId) {
156
+ state.realtime.lastEventId = String(evt.lastEventId);
157
+ }
158
+ handleRealtimeEvent(payload);
159
+ };
160
+
161
+ source.onmessage = onAnyEvent;
162
+ source.addEventListener('call.inbound', onAnyEvent);
163
+ source.addEventListener('call.updated', onAnyEvent);
164
+ source.addEventListener('summary.completed', onAnyEvent);
165
+ source.addEventListener('invite.used', onAnyEvent);
166
+ source.addEventListener('contact.status.changed', onAnyEvent);
167
+ }
168
+
169
+ window.addEventListener('beforeunload', () => {
170
+ if (dashboardEventSource) {
171
+ dashboardEventSource.close();
172
+ dashboardEventSource = null;
173
+ }
174
+ });
175
+
176
+ async function request(path, options = {}) {
177
+ const res = await fetch(`/api/a2a/dashboard${path}`, {
178
+ headers: { 'Content-Type': 'application/json', ...(options.headers || {}) },
179
+ ...options
180
+ });
181
+ const payload = await res.json().catch(() => ({}));
182
+ if (!res.ok || payload.success === false) {
183
+ throw new Error(payload.message || payload.error || `Request failed: ${res.status}`);
184
+ }
185
+ return payload;
186
+ }
187
+
188
+ function toLines(values) {
189
+ return (values || []).join('\n');
190
+ }
191
+
192
+ function fromLines(value) {
193
+ return value
194
+ .split('\n')
195
+ .map(v => v.trim())
196
+ .filter(Boolean);
197
+ }
198
+
199
+ function fmtDate(value) {
200
+ if (!value) return '-';
201
+ try {
202
+ return new Date(value).toLocaleString();
203
+ } catch (err) {
204
+ return String(value);
205
+ }
206
+ }
207
+
208
+ function esc(text) {
209
+ return String(text ?? '')
210
+ .replaceAll('&', '&')
211
+ .replaceAll('<', '&lt;')
212
+ .replaceAll('>', '&gt;')
213
+ .replaceAll('"', '&quot;')
214
+ .replaceAll("'", '&#039;');
215
+ }
216
+
217
+ function formatUpdaterState(stateValue) {
218
+ const state = String(stateValue || '').trim() || 'unknown';
219
+ return state.replaceAll('_', ' ');
220
+ }
221
+
222
+ function badgeVariant(stateValue) {
223
+ const state = String(stateValue || '').trim();
224
+ if (state === 'failed') return 'danger';
225
+ if (state === 'waiting_for_safe_restart' || state === 'checking' || state === 'downloading' || state === 'applying' || state === 'restarting') {
226
+ return 'warning';
227
+ }
228
+ return 'success';
229
+ }
230
+
231
+ async function copyText(value) {
232
+ const text = String(value || '');
233
+ if (!text) return false;
234
+ try {
235
+ if (navigator.clipboard && navigator.clipboard.writeText) {
236
+ await navigator.clipboard.writeText(text);
237
+ return true;
238
+ }
239
+ } catch (err) {
240
+ // fall back
241
+ }
242
+ try {
243
+ const ta = document.createElement('textarea');
244
+ ta.value = text;
245
+ ta.style.position = 'fixed';
246
+ ta.style.left = '-9999px';
247
+ document.body.appendChild(ta);
248
+ ta.select();
249
+ document.execCommand('copy');
250
+ document.body.removeChild(ta);
251
+ return true;
252
+ } catch (err) {
253
+ return false;
254
+ }
255
+ }
256
+
257
+ // A2A-47: Panel name → section title mapping for the content header
258
+ // A2A-50: Added 'settings' panel for relocated admin settings
259
+ const panelTitles = {
260
+ contacts: 'Contacts',
261
+ calls: 'Calls',
262
+ permissions: 'Permissions',
263
+ invites: 'Invites',
264
+ logs: 'Logs',
265
+ health: 'Health',
266
+ settings: 'Settings'
267
+ };
268
+
269
+ // A2A-47: Show a specific panel and update sidebar + header state.
270
+ // Replaces the old sl-tab-group navigation.
271
+ function showPanel(name) {
272
+ const validPanels = Object.keys(panelTitles);
273
+ if (!validPanels.includes(name)) name = 'contacts';
274
+
275
+ // Hide all panels, show the target
276
+ document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
277
+ const target = document.getElementById('panel-' + name);
278
+ if (target) target.classList.add('active');
279
+
280
+ // Update sidebar active state
281
+ document.querySelectorAll('.nav-item').forEach(item => {
282
+ if (item.dataset.panel === name) {
283
+ item.classList.add('active');
284
+ } else {
285
+ item.classList.remove('active');
286
+ }
287
+ });
288
+
289
+ // Update header title
290
+ const titleEl = document.getElementById('section-title');
291
+ if (titleEl) titleEl.textContent = panelTitles[name] || name;
292
+
293
+ // Update state and hash
294
+ state.activeTab = name;
295
+ try {
296
+ if (window.location.hash.slice(1) !== name) {
297
+ window.location.hash = name;
298
+ }
299
+ } catch (err) {}
300
+
301
+ // Trigger data loading for the active tab
302
+ if (typeof onTabSwitch === 'function') onTabSwitch(name);
303
+ }
304
+
305
+ function bindTabs() {
306
+ // A2A-47: Sidebar nav click handler
307
+ document.querySelectorAll('.nav-item').forEach(item => {
308
+ item.addEventListener('click', (e) => {
309
+ e.preventDefault();
310
+ const panel = item.dataset.panel;
311
+ if (panel) showPanel(panel);
312
+ });
313
+ });
314
+
315
+ // Deep-link support: activate the panel matching the URL hash
316
+ const activateFromHash = () => {
317
+ let hash = window.location.hash.slice(1);
318
+ // A2A-50: #settings now points to the real Settings panel (moved from
319
+ // being a backward-compat alias for #permissions in A2A-41).
320
+ if (hash) {
321
+ showPanel(hash);
322
+ }
323
+ };
324
+
325
+ window.addEventListener('hashchange', activateFromHash);
326
+
327
+ // On initial load, activate from hash
328
+ activateFromHash();
329
+ }
330
+
331
+ function norm(value) {
332
+ return String(value || '').replace(/\s+/g, ' ').trim().toLowerCase();
333
+ }
334
+
335
+ function getLocalOwnerName() {
336
+ return state.dashboardStatus?.agent?.owner_name || state.dashboardStatus?.agent?.ownerName || '';
337
+ }
338
+
339
+ function isMine(contact) {
340
+ return Boolean(contact?.is_mine);
341
+ }
342
+
343
+ function formatLocation(contact) {
344
+ const host = String(contact?.host || contact?.web_address || '').trim();
345
+ const server = String(contact?.server_name || contact?.serverName || '').trim();
346
+ if (server && host && norm(server) !== norm(host)) {
347
+ return `${server} (${host})`;
348
+ }
349
+ return server || host || '-';
350
+ }
351
+
352
+ function contactLabel(contact) {
353
+ return String(contact?.name || '').trim() || String(contact?.host || '').trim() || '-';
354
+ }
355
+
356
+ function getPinnedContacts() {
357
+ try {
358
+ const raw = localStorage.getItem('a2a-pinned-contacts');
359
+ if (!raw) return [];
360
+ const parsed = JSON.parse(raw);
361
+ return Array.isArray(parsed) ? parsed : [];
362
+ } catch (err) {
363
+ return [];
364
+ }
365
+ }
366
+
367
+ function togglePin(contactId) {
368
+ const id = String(contactId || '');
369
+ if (!id) return;
370
+ const pinned = getPinnedContacts();
371
+ const index = pinned.indexOf(id);
372
+ if (index >= 0) {
373
+ pinned.splice(index, 1);
374
+ } else {
375
+ pinned.push(id);
376
+ }
377
+ try {
378
+ localStorage.setItem('a2a-pinned-contacts', JSON.stringify(pinned));
379
+ } catch (err) {
380
+ // localStorage may be unavailable
381
+ }
382
+ renderContacts();
383
+ }
384
+
385
+ function renderContacts() {
386
+ const el = document.getElementById('contacts-sections');
387
+ if (!el) return;
388
+
389
+ const contacts = Array.isArray(state.contacts) ? state.contacts.slice() : [];
390
+ const selected = state.selectedContactId ? String(state.selectedContactId) : '';
391
+
392
+ const myAgents = contacts
393
+ .filter(c => isMine(c))
394
+ .sort((a, b) => contactLabel(a).localeCompare(contactLabel(b)));
395
+
396
+ const pinnedIds = getPinnedContacts();
397
+ const lastCalled = contacts
398
+ .filter(c => c && c.last_call_at && !isMine(c))
399
+ .sort((a, b) => {
400
+ const aPinned = pinnedIds.includes(String(a.id));
401
+ const bPinned = pinnedIds.includes(String(b.id));
402
+ if (aPinned && !bPinned) return -1;
403
+ if (!aPinned && bPinned) return 1;
404
+ return String(b.last_call_at || '').localeCompare(String(a.last_call_at || ''));
405
+ })
406
+ .slice(0, 12);
407
+
408
+ const rowHtml = (c, opts = {}) => {
409
+ const canCall = Boolean(c?.can_call);
410
+ const mine = Boolean(c?.is_mine);
411
+ const lastSummary = String(c?.last_owner_summary || c?.last_summary || '').trim();
412
+ const summaryPreview = lastSummary ? lastSummary.slice(0, 120) : '-';
413
+ const lastCallAt = c?.last_call_at ? fmtDate(c.last_call_at) : '-';
414
+ const calls = Number.isFinite(c?.call_count) ? c.call_count : (c?.call_count || 0);
415
+ const isSelected = selected && String(c?.id) === selected;
416
+ const isPinned = pinnedIds.includes(String(c?.id));
417
+
418
+ const actionBits = [];
419
+ if (opts.showPin) {
420
+ actionBits.push(`<sl-icon-button name="${isPinned ? 'pin-fill' : 'pin'}" class="pin-btn${isPinned ? ' pinned' : ''}" data-pin-contact="${esc(c.id)}" title="${isPinned ? 'Unpin' : 'Pin to top'}"></sl-icon-button>`);
421
+ }
422
+ if (c?.last_call_id) {
423
+ actionBits.push(`<sl-button size="small" data-open-call="${esc(c.last_call_id)}">Transcript</sl-button>`);
424
+ }
425
+ actionBits.push(`<sl-button size="small" data-toggle-mine="${esc(c.id)}">${mine ? 'Unmark mine' : 'Mark mine'}</sl-button>`);
426
+ actionBits.push(`<sl-button size="small" variant="danger" data-remove-contact="${esc(c.id)}">Remove</sl-button>`);
427
+
428
+ const locationCell = opts.showLocation ? `<td>${esc(formatLocation(c))}</td>` : '';
429
+ const ownerCell = opts.showOwner ? `<td>${esc(c?.owner || '-')}</td>` : '';
430
+ const summaryCell = opts.showSummary ? `<td title="${esc(lastSummary)}">${esc(summaryPreview)}</td>` : '';
431
+
432
+ return `
433
+ <tr ${isSelected ? 'data-selected="1"' : ''}>
434
+ <td>
435
+ <div class="row" style="margin:0;">
436
+ <sl-button variant="text" size="small" data-contact-select="${esc(c.id)}">${esc(contactLabel(c))}</sl-button>
437
+ <sl-button size="small" variant="primary" data-contact-call="${esc(c.id)}" ${canCall ? '' : 'disabled'}>Call</sl-button>
438
+ </div>
439
+ </td>
440
+ ${locationCell}
441
+ ${ownerCell}
442
+ <td><span class="contact-status" data-status="${esc(c?.status || 'unknown')}">${esc(c?.status || '-')}</span></td>
443
+ <td>${esc(String(calls))}</td>
444
+ <td>${esc(lastCallAt)}</td>
445
+ ${summaryCell}
446
+ <td>${actionBits.join(' ')}</td>
447
+ </tr>
448
+ `;
449
+ };
450
+
451
+ const tableHtml = (rows, opts = {}) => {
452
+ const cols = [];
453
+ cols.push('<th>Agent</th>');
454
+ if (opts.showLocation) cols.push('<th>Location</th>');
455
+ if (opts.showOwner) cols.push('<th>Owner</th>');
456
+ cols.push('<th>Status</th>');
457
+ cols.push('<th>Calls</th>');
458
+ cols.push('<th>Last Call</th>');
459
+ if (opts.showSummary) cols.push('<th>Last Summary</th>');
460
+ cols.push('<th>Action</th>');
461
+
462
+ if (!rows.length) {
463
+ return `<table><thead><tr>${cols.join('')}</tr></thead><tbody><tr><td colspan="${cols.length}">(none)</td></tr></tbody></table>`;
464
+ }
465
+
466
+ return `<table><thead><tr>${cols.join('')}</tr></thead><tbody>${rows.map(c => rowHtml(c, opts)).join('')}</tbody></table>`;
467
+ };
468
+
469
+ const myAgentsSection = `
470
+ <sl-card>
471
+ <h3>My agents</h3>
472
+ ${tableHtml(myAgents, { showLocation: true, showOwner: false, showSummary: false })}
473
+ </sl-card>
474
+ `;
475
+
476
+ const lastCalledSection = `
477
+ <sl-card>
478
+ <h3>Last called agents</h3>
479
+ ${tableHtml(lastCalled, { showLocation: false, showOwner: true, showSummary: false, showPin: true })}
480
+ </sl-card>
481
+ `;
482
+
483
+ const otherContacts = contacts.filter(c => !isMine(c));
484
+ const otherGroups = new Map();
485
+ for (const c of otherContacts) {
486
+ const owner = String(c?.owner || '').trim() || '(unknown owner)';
487
+ if (!otherGroups.has(owner)) otherGroups.set(owner, []);
488
+ otherGroups.get(owner).push(c);
489
+ }
490
+
491
+ const otherOwners = Array.from(otherGroups.keys()).sort((a, b) => {
492
+ if (a === '(unknown owner)' && b !== '(unknown owner)') return 1;
493
+ if (a !== '(unknown owner)' && b === '(unknown owner)') return -1;
494
+ return a.localeCompare(b);
495
+ });
496
+
497
+ const groupedSections = otherOwners.map(owner => {
498
+ const rows = (otherGroups.get(owner) || []).slice().sort((a, b) => contactLabel(a).localeCompare(contactLabel(b)));
499
+ return `
500
+ <sl-card>
501
+ <h3>${esc(owner)}</h3>
502
+ ${tableHtml(rows, { showLocation: false, showOwner: false, showSummary: true })}
503
+ </sl-card>
504
+ `;
505
+ }).join('');
506
+
507
+ const otherAgentsHeading = otherOwners.length
508
+ ? `<h3 style="margin-top:1rem;">Other Agents</h3>`
509
+ : '';
510
+
511
+ el.innerHTML = `${myAgentsSection}${lastCalledSection}${otherAgentsHeading}${groupedSections}`;
512
+ }
513
+
514
+ async function loadContacts() {
515
+ const payload = await request('/contacts');
516
+ state.contacts = payload.contacts || [];
517
+ renderContacts();
518
+ renderContactDetail();
519
+ }
520
+
521
+ function bindContactsActions() {
522
+ const form = document.getElementById('add-contact-form');
523
+ if (!form) return;
524
+
525
+ // Cancel button collapses the sl-details
526
+ const cancelBtn = document.getElementById('add-contact-cancel');
527
+ const addDetails = document.getElementById('add-contact-details');
528
+ if (cancelBtn && addDetails) {
529
+ cancelBtn.addEventListener('click', () => {
530
+ addDetails.open = false;
531
+ });
532
+ }
533
+
534
+ const urlEl = document.getElementById('add-contact-url');
535
+ const mineEl = document.getElementById('add-contact-mine');
536
+ const serverNameEl = document.getElementById('add-contact-server-name');
537
+ const defaultServerNameFromUrl = () => {
538
+ if (!urlEl || !serverNameEl) return;
539
+ if (mineEl && !mineEl.checked) return;
540
+ if (serverNameEl.value.trim()) return;
541
+ const match = String(urlEl.value || '').trim().match(/^(?:a2a|oclaw):\/\/([^/]+)\//);
542
+ if (match && match[1]) {
543
+ serverNameEl.value = match[1];
544
+ }
545
+ };
546
+ urlEl?.addEventListener('sl-blur', defaultServerNameFromUrl);
547
+ urlEl?.addEventListener('sl-change', defaultServerNameFromUrl);
548
+ mineEl?.addEventListener('sl-change', () => {
549
+ if (!serverNameEl) return;
550
+ serverNameEl.disabled = !mineEl.checked;
551
+ if (mineEl.checked) {
552
+ defaultServerNameFromUrl();
553
+ }
554
+ });
555
+ if (serverNameEl && mineEl) {
556
+ serverNameEl.disabled = !mineEl.checked;
557
+ }
558
+
559
+ form.addEventListener('submit', async (e) => {
560
+ e.preventDefault();
561
+ const url = document.getElementById('add-contact-url').value.trim();
562
+ const name = document.getElementById('add-contact-name').value.trim();
563
+ const owner = document.getElementById('add-contact-owner').value.trim();
564
+ const isMineVal = Boolean(document.getElementById('add-contact-mine')?.checked);
565
+ const serverName = document.getElementById('add-contact-server-name').value.trim();
566
+ const tagsRaw = document.getElementById('add-contact-tags').value.trim();
567
+ const notes = document.getElementById('add-contact-notes').value.trim();
568
+ const fieldsRaw = document.getElementById('add-contact-fields').value.trim();
569
+ const tags = tagsRaw
570
+ ? tagsRaw.split(',').map(v => v.trim()).filter(Boolean).slice(0, 30)
571
+ : [];
572
+
573
+ let fields = {};
574
+ if (fieldsRaw) {
575
+ try {
576
+ const parsed = JSON.parse(fieldsRaw);
577
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
578
+ throw new Error('Fields must be a JSON object');
579
+ }
580
+ fields = parsed;
581
+ } catch (err) {
582
+ showNotice(`Fields JSON invalid: ${err.message}`);
583
+ return;
584
+ }
585
+ }
586
+
587
+ try {
588
+ await request('/contacts', {
589
+ method: 'POST',
590
+ body: JSON.stringify({
591
+ invite_url: url,
592
+ name: name || undefined,
593
+ owner: owner || undefined,
594
+ is_mine: isMineVal,
595
+ server_name: serverName || undefined,
596
+ tags,
597
+ notes: notes || undefined,
598
+ fields
599
+ })
600
+ });
601
+ showNotice('Contact added');
602
+ form.reset();
603
+ // Collapse the sl-details after successful add
604
+ if (addDetails) addDetails.open = false;
605
+ await loadContacts();
606
+ } catch (err) {
607
+ showNotice(err.message);
608
+ }
609
+ });
610
+
611
+ // A2A-47: Event delegation on the contacts panel (was sl-tab-panel, now div#panel-contacts)
612
+ const panel = document.querySelector('#panel-contacts');
613
+ panel?.addEventListener('click', async (e) => {
614
+ const pinBtn = e.target.closest('[data-pin-contact]');
615
+ if (pinBtn) {
616
+ e.preventDefault();
617
+ const id = pinBtn.dataset.pinContact;
618
+ if (id) togglePin(id);
619
+ return;
620
+ }
621
+
622
+ const selectBtn = e.target.closest('[data-contact-select]');
623
+ if (selectBtn) {
624
+ e.preventDefault();
625
+ const id = selectBtn.dataset.contactSelect;
626
+ if (id) {
627
+ await loadCallsForContact(id);
628
+ }
629
+ return;
630
+ }
631
+
632
+ const openBtn = e.target.closest('[data-open-call]');
633
+ if (openBtn) {
634
+ e.preventDefault();
635
+ openCallTranscript(openBtn.dataset.openCall);
636
+ return;
637
+ }
638
+
639
+ const mineBtn = e.target.closest('[data-toggle-mine]');
640
+ if (mineBtn) {
641
+ e.preventDefault();
642
+ const id = mineBtn.dataset.toggleMine;
643
+ if (!id) return;
644
+
645
+ const contact = (state.contacts || []).find(c => String(c.id) === String(id));
646
+ const next = contact ? !Boolean(contact.is_mine) : true;
647
+
648
+ mineBtn.disabled = true;
649
+ try {
650
+ await request(`/contacts/${encodeURIComponent(id)}`, {
651
+ method: 'PUT',
652
+ body: JSON.stringify({ is_mine: next })
653
+ });
654
+ showNotice(next ? 'Marked as mine' : 'Unmarked');
655
+ await loadContacts();
656
+ if (state.selectedContactId && String(state.selectedContactId) === String(id)) {
657
+ await loadCallsForContact(id);
658
+ }
659
+ } catch (err) {
660
+ showNotice(err.message);
661
+ mineBtn.disabled = false;
662
+ }
663
+ return;
664
+ }
665
+
666
+ const removeBtn = e.target.closest('[data-remove-contact]');
667
+ if (removeBtn) {
668
+ e.preventDefault();
669
+ const id = removeBtn.dataset.removeContact;
670
+ if (!id) return;
671
+ removeBtn.disabled = true;
672
+ try {
673
+ await request(`/contacts/${encodeURIComponent(id)}`, { method: 'DELETE' });
674
+ showNotice('Contact removed');
675
+ if (state.selectedContactId && String(state.selectedContactId) === String(id)) {
676
+ state.selectedContactId = null;
677
+ state.selectedContactCalls = [];
678
+ state.contactCallResult = null;
679
+ }
680
+ await loadContacts();
681
+ } catch (err) {
682
+ showNotice(err.message);
683
+ removeBtn.disabled = false;
684
+ }
685
+ return;
686
+ }
687
+
688
+ const callBtn = e.target.closest('[data-contact-call]');
689
+ if (callBtn) {
690
+ e.preventDefault();
691
+ const id = callBtn.dataset.contactCall;
692
+ if (!id) return;
693
+
694
+ const contact = (state.contacts || []).find(c => String(c.id) === String(id));
695
+ if (!contact) {
696
+ showNotice('Contact not found');
697
+ return;
698
+ }
699
+ if (!contact.can_call) {
700
+ showNotice('This contact has no callable A2A endpoint stored.');
701
+ return;
702
+ }
703
+
704
+ // Quick-call: use existing draft message if available, else prompt.
705
+ let message = '';
706
+ const draftEl = document.getElementById('contact-call-message');
707
+ if (state.selectedContactId && String(state.selectedContactId) === String(id) && draftEl && draftEl.value.trim()) {
708
+ message = draftEl.value.trim();
709
+ } else {
710
+ const prompted = window.prompt(`Message to send to ${contactLabel(contact)}:`, 'Hello from my agent.');
711
+ if (prompted === null) return;
712
+ message = String(prompted || '').trim();
713
+ }
714
+
715
+ if (!message) {
716
+ showNotice('Message required');
717
+ return;
718
+ }
719
+ await callContact(id, message);
720
+ return;
721
+ }
722
+ });
723
+ }
724
+
725
+ function renderCalls() {
726
+ const tbody = document.querySelector('#calls-table tbody');
727
+ tbody.innerHTML = '';
728
+ state.calls.forEach(call => {
729
+ const tr = document.createElement('tr');
730
+ tr.innerHTML = `
731
+ <td>${call.id}</td>
732
+ <td>${call.contact?.name || call.contact_name || '-'}</td>
733
+ <td>${call.status || '-'}</td>
734
+ <td>${call.message_count || 0}</td>
735
+ <td>${fmtDate(call.last_message_at)}</td>
736
+ <td>${(call.summary || call.owner_summary || '-').slice(0, 120)}</td>
737
+ `;
738
+ tr.addEventListener('click', () => loadCallDetail(call.id));
739
+ tbody.appendChild(tr);
740
+ });
741
+ }
742
+
743
+ async function loadCalls() {
744
+ const payload = await request('/calls?limit=200');
745
+ state.calls = payload.calls || [];
746
+ renderCalls();
747
+ }
748
+
749
+ async function loadCallDetail(conversationId) {
750
+ const payload = await request(`/calls/${encodeURIComponent(conversationId)}?messages=40`);
751
+ const call = payload.call;
752
+ const el = document.getElementById('call-detail');
753
+
754
+ // Summary: prefer agent-generated summary, fall back to owner summary
755
+ const summaryText = call.summary || call.ownerContext?.summary || '';
756
+ const summaryHtml = summaryText
757
+ ? `<pre class="summary">${esc(summaryText)}</pre>`
758
+ : `<p class="summary-pending"><em>${call.status === 'active' ? 'Call in progress\u2026' : 'Summary pending\u2026'}</em></p>`;
759
+
760
+ // Full transcript in a collapsible section
761
+ const messages = (call.recentMessages || []);
762
+ const transcriptLines = messages
763
+ .map(msg => `[${esc(fmtDate(msg.timestamp))}] ${esc(msg.direction)}: ${esc(msg.content)}`)
764
+ .join('\n\n');
765
+ const totalMessages = call.messageCount || messages.length;
766
+ const countLabel = messages.length < totalMessages
767
+ ? `${messages.length} of ${totalMessages} messages`
768
+ : `${messages.length} message${messages.length === 1 ? '' : 's'}`;
769
+ const transcriptHtml = messages.length
770
+ ? `<sl-details class="transcript-details" summary="Full Transcript (${countLabel})">
771
+ <pre class="transcript">${transcriptLines}</pre>
772
+ </sl-details>`
773
+ : '';
774
+
775
+ el.innerHTML = `
776
+ <h3>Call Detail: ${esc(call.id)}</h3>
777
+ <p><strong>Contact:</strong> ${esc(call.contact?.name || call.contact || '-')}</p>
778
+ <p><strong>Status:</strong> ${esc(call.status || '-')}</p>
779
+ <p><strong>Summary:</strong></p>
780
+ ${summaryHtml}
781
+ ${transcriptHtml}
782
+ `;
783
+ }
784
+
785
+ function renderContactDetail() {
786
+ const el = document.getElementById('contact-detail');
787
+ if (!el) return;
788
+
789
+ const contactId = state.selectedContactId ? String(state.selectedContactId) : '';
790
+ if (!contactId) {
791
+ el.innerHTML = '<strong>Select a contact to view details and call history.</strong>';
792
+ return;
793
+ }
794
+
795
+ const contact = (state.contacts || []).find(c => String(c.id) === contactId) || null;
796
+ if (!contact) {
797
+ el.innerHTML = '<strong>Selected contact not found.</strong>';
798
+ return;
799
+ }
800
+
801
+ const calls = Array.isArray(state.selectedContactCalls) ? state.selectedContactCalls : [];
802
+ const canCall = Boolean(contact.can_call);
803
+
804
+ const tagsText = Array.isArray(contact.tags) ? contact.tags.join(', ') : '';
805
+ const fieldsText = (() => {
806
+ try {
807
+ const obj = (contact.fields && typeof contact.fields === 'object') ? contact.fields : {};
808
+ return JSON.stringify(obj, null, 2);
809
+ } catch (err) {
810
+ return '{}';
811
+ }
812
+ })();
813
+
814
+ const result = state.contactCallResult;
815
+ const resultHtml = result
816
+ ? `<div style="margin-top:0.6rem;">
817
+ <strong>Last call result:</strong> ${result.success ? 'success' : 'failed'}<br>
818
+ ${result.conversation_id ? `Conversation: <span class="mono">${esc(result.conversation_id)}</span> <sl-button size="small" data-open-call="${esc(result.conversation_id)}">Transcript</sl-button><br>` : ''}
819
+ ${result.error ? `<span class="mono">${esc(result.error)}</span><br>` : ''}
820
+ ${result.response ? `<pre class="summary">${esc(String(result.response))}</pre>` : ''}
821
+ </div>`
822
+ : '';
823
+
824
+ const callRows = calls.map(call => {
825
+ const summary = String(call.summary || call.owner_summary || '').trim();
826
+ const preview = summary ? summary.slice(0, 140) : '-';
827
+ return `
828
+ <tr>
829
+ <td class="mono">${esc(call.id)}</td>
830
+ <td>${esc(call.status || '-')}</td>
831
+ <td>${esc(fmtDate(call.last_message_at))}</td>
832
+ <td title="${esc(summary)}">${esc(preview)}</td>
833
+ <td><sl-button size="small" data-open-call="${esc(call.id)}">Transcript</sl-button></td>
834
+ </tr>
835
+ `;
836
+ }).join('');
837
+
838
+ el.innerHTML = `
839
+ <div class="row">
840
+ <h3 style="margin:0;">Contact: ${esc(contactLabel(contact))}</h3>
841
+ <sl-button size="small" variant="primary" data-contact-call="${esc(contact.id)}" ${canCall ? '' : 'disabled'}>Call</sl-button>
842
+ <sl-button size="small" variant="danger" data-remove-contact="${esc(contact.id)}">Remove</sl-button>
843
+ </div>
844
+
845
+ <div class="row" style="margin-bottom:0.4rem;">
846
+ <div><strong>Mine:</strong> ${contact.is_mine ? 'yes' : 'no'}</div>
847
+ <div><strong>Owner:</strong> ${esc(contact.owner || '-')}</div>
848
+ <div><strong>Web address:</strong> <span class="mono">${esc(contact.web_address || contact.host || '-')}</span></div>
849
+ <div><strong>Server name:</strong> ${esc(contact.server_name || '-')}</div>
850
+ </div>
851
+ <div class="row">
852
+ <div><strong>Status:</strong> ${esc(contact.status || '-')}</div>
853
+ <div><strong>Total calls:</strong> ${esc(String(contact.call_count || 0))}</div>
854
+ <div><strong>Last call:</strong> ${esc(contact.last_call_at ? fmtDate(contact.last_call_at) : '-')}</div>
855
+ </div>
856
+
857
+ ${resultHtml}
858
+
859
+ <sl-details summary="Edit contact" open style="margin-top:0.8rem;">
860
+ <form id="contact-edit-form" data-contact-id="${esc(contact.id)}" style="margin-top:0.6rem;">
861
+ <sl-input id="contact-edit-name" label="Agent name" value="${esc(contact.name || '')}"></sl-input>
862
+ <sl-input id="contact-edit-owner" label="Owner name" value="${esc(contact.owner || '')}"></sl-input>
863
+ <sl-checkbox id="contact-edit-mine" ${contact.is_mine ? 'checked' : ''}>Mark as mine (personal agent)</sl-checkbox>
864
+ <sl-input id="contact-edit-server-name" label="Server name (my agents only)" value="${esc(contact.server_name || '')}" ${contact.is_mine ? '' : 'disabled'}></sl-input>
865
+ <sl-input id="contact-edit-tags" label="Tags" value="${esc(tagsText)}" placeholder="comma,separated"></sl-input>
866
+ <sl-textarea id="contact-edit-notes" label="Notes" rows="3" value="${esc(contact.notes || '')}"></sl-textarea>
867
+ <sl-textarea id="contact-edit-fields" label="Fields (JSON)" rows="5" value="${esc(fieldsText)}"></sl-textarea>
868
+ <div class="row">
869
+ <sl-button type="submit" variant="primary" size="small">Save</sl-button>
870
+ </div>
871
+ </form>
872
+ </sl-details>
873
+
874
+ <sl-details summary="Call" open style="margin-top:0.8rem;">
875
+ <form id="contact-call-form" data-contact-id="${esc(contact.id)}" style="margin-top:0.6rem;">
876
+ <sl-textarea id="contact-call-message" label="Message" rows="4" placeholder="Message to send"></sl-textarea>
877
+ <div class="row">
878
+ <sl-button type="submit" variant="primary" size="small" ${canCall ? '' : 'disabled'}>Call</sl-button>
879
+ </div>
880
+ </form>
881
+ </sl-details>
882
+
883
+ <sl-details summary="Call history" style="margin-top:0.8rem;">
884
+ <div style="margin-top:0.6rem;">
885
+ <table>
886
+ <thead><tr><th>ID</th><th>Status</th><th>Updated</th><th>Summary</th><th>Action</th></tr></thead>
887
+ <tbody>${callRows || '<tr><td colspan="5">No calls found.</td></tr>'}</tbody>
888
+ </table>
889
+ </div>
890
+ </sl-details>
891
+ `;
892
+
893
+ const editForm = document.getElementById('contact-edit-form');
894
+ if (editForm) {
895
+ const mineEl = document.getElementById('contact-edit-mine');
896
+ const serverNameEl = document.getElementById('contact-edit-server-name');
897
+ mineEl?.addEventListener('sl-change', () => {
898
+ if (!serverNameEl) return;
899
+ serverNameEl.disabled = !mineEl.checked;
900
+ });
901
+
902
+ editForm.addEventListener('submit', async (e) => {
903
+ e.preventDefault();
904
+ const id = editForm.dataset.contactId;
905
+ if (!id) return;
906
+
907
+ const tagsRaw = document.getElementById('contact-edit-tags').value.trim();
908
+ const tags = tagsRaw
909
+ ? tagsRaw.split(',').map(v => v.trim()).filter(Boolean).slice(0, 30)
910
+ : [];
911
+
912
+ let fields = {};
913
+ const fieldsRaw = document.getElementById('contact-edit-fields').value.trim();
914
+ if (fieldsRaw) {
915
+ try {
916
+ const parsed = JSON.parse(fieldsRaw);
917
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
918
+ throw new Error('Fields must be a JSON object');
919
+ }
920
+ fields = parsed;
921
+ } catch (err) {
922
+ showNotice(`Fields JSON invalid: ${err.message}`);
923
+ return;
924
+ }
925
+ }
926
+
927
+ try {
928
+ await request(`/contacts/${encodeURIComponent(id)}`, {
929
+ method: 'PUT',
930
+ body: JSON.stringify({
931
+ name: document.getElementById('contact-edit-name').value,
932
+ owner: document.getElementById('contact-edit-owner').value,
933
+ is_mine: Boolean(document.getElementById('contact-edit-mine')?.checked),
934
+ server_name: document.getElementById('contact-edit-server-name').value,
935
+ notes: document.getElementById('contact-edit-notes').value,
936
+ tags,
937
+ fields
938
+ })
939
+ });
940
+ showNotice('Contact saved');
941
+ await loadContacts();
942
+ await loadCallsForContact(id);
943
+ } catch (err) {
944
+ showNotice(err.message);
945
+ }
946
+ });
947
+ }
948
+
949
+ const callForm = document.getElementById('contact-call-form');
950
+ if (callForm) {
951
+ callForm.addEventListener('submit', async (e) => {
952
+ e.preventDefault();
953
+ const id = callForm.dataset.contactId;
954
+ if (!id) return;
955
+ const message = document.getElementById('contact-call-message').value.trim();
956
+ if (!message) {
957
+ showNotice('Message required');
958
+ return;
959
+ }
960
+ await callContact(id, message);
961
+ });
962
+ }
963
+ }
964
+
965
+ function openCallTranscript(conversationId) {
966
+ const id = String(conversationId || '').trim();
967
+ if (!id) return;
968
+ try { window.location.hash = 'calls'; } catch (err) {}
969
+ // Let hashchange tab switch complete before rendering details.
970
+ setTimeout(() => loadCallDetail(id).catch(err => showNotice(err.message)), 50);
971
+ }
972
+
973
+ async function callContact(contactId, message) {
974
+ const id = String(contactId || '').trim();
975
+ if (!id) return;
976
+ state.selectedContactId = id;
977
+ state.contactCallResult = { success: false, error: null, response: null, conversation_id: null };
978
+ renderContactDetail();
979
+
980
+ try {
981
+ const result = await request(`/contacts/${encodeURIComponent(id)}/call`, {
982
+ method: 'POST',
983
+ body: JSON.stringify({ message })
984
+ });
985
+ state.contactCallResult = {
986
+ success: true,
987
+ response: result.response || '',
988
+ conversation_id: result.conversation_id || null
989
+ };
990
+ showNotice('Call complete');
991
+ await Promise.all([loadContacts(), loadCalls()]);
992
+ await loadCallsForContact(id);
993
+ } catch (err) {
994
+ state.contactCallResult = { success: false, error: err.message, response: null, conversation_id: null };
995
+ renderContactDetail();
996
+ showNotice(err.message);
997
+ }
998
+ }
999
+
1000
+ async function loadCallsForContact(contactId, contactName) {
1001
+ const id = String(contactId || '').trim();
1002
+ if (!id) return;
1003
+ state.selectedContactId = id;
1004
+
1005
+ try {
1006
+ const payload = await request(`/contacts/${encodeURIComponent(id)}/calls?limit=100`);
1007
+ state.selectedContactCalls = payload.calls || [];
1008
+ } catch (err) {
1009
+ state.selectedContactCalls = [];
1010
+ }
1011
+
1012
+ renderContacts();
1013
+ renderContactDetail();
1014
+ }
1015
+
1016
+ function readLogFilters() {
1017
+ const level = document.getElementById('logs-level').value.trim();
1018
+ const component = document.getElementById('logs-component').value.trim();
1019
+ const event = document.getElementById('logs-event').value.trim();
1020
+ const traceId = document.getElementById('logs-trace').value.trim();
1021
+ const conversationId = document.getElementById('logs-conversation').value.trim();
1022
+ const tokenId = document.getElementById('logs-token').value.trim();
1023
+ const search = document.getElementById('logs-search').value.trim();
1024
+ const limit = Number.parseInt(document.getElementById('logs-limit').value, 10) || 200;
1025
+
1026
+ const params = new URLSearchParams();
1027
+ params.set('limit', String(Math.min(1000, Math.max(1, limit))));
1028
+ if (level) params.set('level', level);
1029
+ if (component) params.set('component', component);
1030
+ if (event) params.set('event', event);
1031
+ if (traceId) params.set('trace_id', traceId);
1032
+ if (conversationId) params.set('conversation_id', conversationId);
1033
+ if (tokenId) params.set('token_id', tokenId);
1034
+ if (search) params.set('search', search);
1035
+
1036
+ return params;
1037
+ }
1038
+
1039
+ function renderLogStats() {
1040
+ const el = document.getElementById('log-stats');
1041
+ if (!state.logStats) {
1042
+ el.textContent = '';
1043
+ el.style.display = 'none';
1044
+ return;
1045
+ }
1046
+ const stats = state.logStats;
1047
+ const levels = Object.entries(stats.by_level || {}).sort((a, b) => a[0].localeCompare(b[0]));
1048
+ const components = Object.entries(stats.by_component || {})
1049
+ .sort((a, b) => b[1] - a[1])
1050
+ .slice(0, 12);
1051
+
1052
+ el.style.display = 'block';
1053
+ el.innerHTML = `
1054
+ <div class="row">
1055
+ <strong>Total:</strong> ${stats.total || 0}
1056
+ </div>
1057
+ <div class="row">
1058
+ <strong>By level:</strong> ${levels.map(([k, v]) => `${esc(k)}=${v}`).join(' \u00b7 ') || '(none)'}
1059
+ </div>
1060
+ <div class="row">
1061
+ <strong>Top components:</strong> ${components.map(([k, v]) => `${esc(k)}=${v}`).join(' \u00b7 ') || '(none)'}
1062
+ </div>
1063
+ `;
1064
+ }
1065
+
1066
+ function renderTraceDetail() {
1067
+ const el = document.getElementById('trace-detail');
1068
+ if (!state.trace || !Array.isArray(state.trace.logs)) {
1069
+ el.textContent = '';
1070
+ el.style.display = 'none';
1071
+ return;
1072
+ }
1073
+ el.style.display = 'block';
1074
+ const logs = state.trace.logs || [];
1075
+ const lines = logs.map(row => {
1076
+ const msg = row.message || '';
1077
+ const meta = [
1078
+ row.component ? row.component : null,
1079
+ row.event ? row.event : null,
1080
+ row.error_code ? `code=${row.error_code}` : null,
1081
+ row.status_code ? `status=${row.status_code}` : null
1082
+ ].filter(Boolean).join(' ');
1083
+ return `[${fmtDate(row.timestamp)}] ${row.level?.toUpperCase() || ''} ${meta}\n${msg}${row.hint ? `\nHint: ${row.hint}` : ''}`;
1084
+ }).join('\n\n');
1085
+
1086
+ el.innerHTML = `
1087
+ <div class="row">
1088
+ <h3 style="margin:0;">Trace: <span class="mono">${esc(state.trace.trace_id || '')}</span></h3>
1089
+ <sl-button id="clear-trace" size="small">Clear</sl-button>
1090
+ </div>
1091
+ <pre class="summary mono">${esc(lines || 'No trace logs.')}</pre>
1092
+ `;
1093
+ const clearBtn = document.getElementById('clear-trace');
1094
+ if (clearBtn) {
1095
+ clearBtn.addEventListener('click', () => {
1096
+ state.trace = null;
1097
+ renderTraceDetail();
1098
+ });
1099
+ }
1100
+ }
1101
+
1102
+ function renderLogs() {
1103
+ const tbody = document.querySelector('#logs-table tbody');
1104
+ tbody.innerHTML = '';
1105
+
1106
+ state.logs.forEach(row => {
1107
+ const tr = document.createElement('tr');
1108
+ const trace = row.trace_id || '';
1109
+ tr.innerHTML = `
1110
+ <td>${esc(fmtDate(row.timestamp))}</td>
1111
+ <td><span class="log-level" data-level="${esc(row.level || '')}">${esc(row.level || '-')}</span></td>
1112
+ <td>${esc(row.component || '-')}</td>
1113
+ <td>${esc(row.event || '-')}</td>
1114
+ <td title="${esc(row.message || '')}">${esc(String(row.message || '').slice(0, 120) || '-')}</td>
1115
+ <td class="mono">${esc(trace ? trace.slice(0, 14) + '\u2026' : '-')}</td>
1116
+ <td class="mono">${esc(row.conversation_id ? row.conversation_id.slice(0, 14) + '\u2026' : '-')}</td>
1117
+ <td class="mono">${esc(row.token_id || '-')}</td>
1118
+ <td>${esc(row.error_code || '-')}</td>
1119
+ <td>${esc(row.status_code ?? '-')}</td>
1120
+ `;
1121
+ if (trace) {
1122
+ tr.addEventListener('click', () => loadTrace(trace).catch(err => showNotice(err.message)));
1123
+ }
1124
+ tbody.appendChild(tr);
1125
+ });
1126
+ }
1127
+
1128
+ async function loadLogs() {
1129
+ const qs = readLogFilters().toString();
1130
+ const payload = await request(`/logs?${qs}`);
1131
+ state.logs = payload.logs || [];
1132
+ renderLogs();
1133
+ }
1134
+
1135
+ async function loadLogStats() {
1136
+ const payload = await request('/logs/stats');
1137
+ state.logStats = payload.stats || null;
1138
+ renderLogStats();
1139
+ }
1140
+
1141
+ async function loadTrace(traceId) {
1142
+ const payload = await request(`/logs/trace/${encodeURIComponent(traceId)}?limit=500`);
1143
+ state.trace = payload;
1144
+ renderTraceDetail();
1145
+ }
1146
+
1147
+ // A2A-41: emoji map for visual tier differentiation. Standard tiers get
1148
+ // recognizable icons; custom/user-created tiers get a wrench.
1149
+ const TIER_EMOJIS = { public: '\u{1F310}', friends: '\u{1F46B}', family: '\u{1F468}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F466}' };
1150
+
1151
+ // A2A-41: tool descriptions for the checkbox UI. These match the tools
1152
+ // available in Claude Code that an agent owner might want to expose to callers.
1153
+ const TOOL_DESCRIPTIONS = {
1154
+ 'Bash': 'Execute shell commands \u2014 full access, can run anything',
1155
+ 'Bash(readonly)': 'Execute read-only shell commands \u2014 no writes, no installs',
1156
+ 'Read': 'Read files from the workspace',
1157
+ 'Grep': 'Search file contents with regex patterns',
1158
+ 'Glob': 'Find files by name patterns',
1159
+ 'WebSearch': 'Search the web for information',
1160
+ 'WebFetch': 'Fetch and read web page content'
1161
+ };
1162
+
1163
+ // A2A-41: standard tier order for inheritance. Custom tiers are not in this list.
1164
+ const TIER_ORDER = ['public', 'friends', 'family'];
1165
+
1166
+ // A2A-48: Material icon mapping for tier cards. Standard tiers get recognizable
1167
+ // icons; custom tiers get a wrench icon. Used by renderTierCards().
1168
+ const TIER_ICONS = { public: 'public', friends: 'group', family: 'family_restroom' };
1169
+
1170
+ // A2A-48: Color mapping for tool icons in toggle cards. Gives each tool a
1171
+ // distinct color matching the concept mock's visual differentiation.
1172
+ const TOOL_ICON_MAP = {
1173
+ 'Bash': { icon: 'terminal', bg: 'rgba(99,102,241,0.2)', color: '#818CF8', border: 'rgba(99,102,241,0.2)' },
1174
+ 'Bash(readonly)': { icon: 'terminal', bg: 'rgba(99,102,241,0.15)', color: '#A5B4FC', border: 'rgba(99,102,241,0.15)' },
1175
+ 'Read': { icon: 'visibility', bg: 'rgba(59,130,246,0.2)', color: '#60A5FA', border: 'rgba(59,130,246,0.2)' },
1176
+ 'Grep': { icon: 'search', bg: 'rgba(139,92,246,0.2)', color: '#A78BFA', border: 'rgba(139,92,246,0.2)' },
1177
+ 'Glob': { icon: 'folder_open', bg: 'rgba(16,185,129,0.2)', color: '#34D399', border: 'rgba(16,185,129,0.2)' },
1178
+ 'WebSearch': { icon: 'public', bg: 'rgba(245,158,11,0.2)', color: '#FBBF24', border: 'rgba(245,158,11,0.2)' },
1179
+ 'WebFetch': { icon: 'language', bg: 'rgba(236,72,153,0.2)', color: '#F472B6', border: 'rgba(236,72,153,0.2)' }
1180
+ };
1181
+
1182
+ // A2A-48: Renders tier cards grid. Active card gets .active class with glow.
1183
+ function renderTierCards() {
1184
+ const container = document.getElementById('tier-cards');
1185
+ if (!container) return;
1186
+ const tiers = (state.settings?.tiers || []).slice().sort((a, b) => {
1187
+ const aIdx = TIER_ORDER.indexOf(a.id);
1188
+ const bIdx = TIER_ORDER.indexOf(b.id);
1189
+ if (aIdx >= 0 && bIdx >= 0) return aIdx - bIdx;
1190
+ if (aIdx >= 0) return -1;
1191
+ if (bIdx >= 0) return 1;
1192
+ return a.id.localeCompare(b.id);
1193
+ });
1194
+
1195
+ container.innerHTML = tiers.map(tier => {
1196
+ const isActive = tier.id === state.activeTierId;
1197
+ const icon = TIER_ICONS[tier.id] || 'build';
1198
+ const iconColor = isActive ? '#60A5FA' : '#6B7280';
1199
+ return `
1200
+ <div class="tier-card${isActive ? ' active' : ''}" data-tier-id="${esc(tier.id)}">
1201
+ <span class="material-symbols-outlined tier-card-icon" style="color:${iconColor};">${icon}</span>
1202
+ <span class="tier-card-name">${esc(tier.name || tier.id)}</span>
1203
+ ${isActive ? '<div class="status-dot status-dot--green"></div>' : ''}
1204
+ </div>
1205
+ `;
1206
+ }).join('');
1207
+ }
1208
+
1209
+ // A2A-48: Renders tool toggle cards (replaces checkboxes). Each tool is a
1210
+ // glass-panel card with icon, name, description, and a toggle switch.
1211
+ // Toggle change triggers autoSaveTier() for immediate persistence.
1212
+ function renderToolToggles(allowedTools) {
1213
+ const container = document.getElementById('tool-toggles');
1214
+ if (!container) return;
1215
+ container.innerHTML = Object.entries(TOOL_DESCRIPTIONS).map(([tool, desc]) => {
1216
+ const checked = (allowedTools || []).includes(tool);
1217
+ const iconInfo = TOOL_ICON_MAP[tool] || { icon: 'extension', bg: 'rgba(100,116,139,0.2)', color: '#94A3B8', border: 'rgba(100,116,139,0.2)' };
1218
+ return `
1219
+ <div class="tool-toggle-card${checked ? ' enabled' : ''}">
1220
+ <div class="tool-toggle-info">
1221
+ <div class="tool-icon" style="background:${iconInfo.bg};color:${iconInfo.color};border:1px solid ${iconInfo.border};">
1222
+ <span class="material-symbols-outlined">${iconInfo.icon}</span>
1223
+ </div>
1224
+ <div>
1225
+ <h3>${esc(tool)}</h3>
1226
+ <p>${esc(desc)}</p>
1227
+ </div>
1228
+ </div>
1229
+ <label class="toggle-switch">
1230
+ <input type="checkbox" data-tool="${esc(tool)}" ${checked ? 'checked' : ''}>
1231
+ <span class="slider"></span>
1232
+ </label>
1233
+ </div>
1234
+ `;
1235
+ }).join('');
1236
+ }
1237
+
1238
+ // A2A-48: Renders active topics in the drop zone as teal-accented cards.
1239
+ // Each card has a close button for removal. Updates #topic-count badge.
1240
+ function renderActiveTopics(tier) {
1241
+ const container = document.getElementById('active-topics-zone');
1242
+ if (!container) return;
1243
+ const manifestTopics = tier.manifest?.topics || [];
1244
+ const flatTopics = tier.topics || [];
1245
+
1246
+ const allTopics = manifestTopics.length > 0
1247
+ ? manifestTopics.map(t => ({ label: t.topic, desc: t.description || '' }))
1248
+ : flatTopics.map(t => ({ label: t, desc: '' }));
1249
+
1250
+ const cardsHtml = allTopics.map(t => `
1251
+ <div class="active-item-card active-item-card--teal" data-topic="${esc(t.label)}" data-description="${esc(t.desc)}">
1252
+ <div>
1253
+ <div class="item-name">${esc(t.label)}</div>
1254
+ <div class="item-type-label">Topic</div>
1255
+ </div>
1256
+ <button class="item-close-btn" data-remove-topic="${esc(t.label)}">
1257
+ <span class="material-symbols-outlined" style="font-size:16px;">close</span>
1258
+ </button>
1259
+ </div>
1260
+ `).join('');
1261
+
1262
+ container.innerHTML = cardsHtml + '<div class="drop-placeholder"><span>+ Drop Topic</span></div>';
1263
+
1264
+ const badge = document.getElementById('topic-count');
1265
+ if (badge) badge.textContent = `${allTopics.length} Active`;
1266
+ }
1267
+
1268
+ // A2A-48: Renders active goals in the drop zone as yellow-accented cards.
1269
+ // Same pattern as topics but with yellow color variant.
1270
+ function renderActiveGoals(tier) {
1271
+ const container = document.getElementById('active-goals-zone');
1272
+ if (!container) return;
1273
+ const manifestGoals = tier.manifest?.objectives || [];
1274
+ const flatGoals = tier.goals || [];
1275
+
1276
+ const allGoals = manifestGoals.length > 0
1277
+ ? manifestGoals.map(g => ({ label: g.objective || g.topic, desc: g.description || '' }))
1278
+ : flatGoals.map(g => ({ label: g, desc: '' }));
1279
+
1280
+ const cardsHtml = allGoals.map(g => `
1281
+ <div class="active-item-card active-item-card--yellow" data-topic="${esc(g.label)}" data-description="${esc(g.desc)}">
1282
+ <div>
1283
+ <div class="item-name">${esc(g.label)}</div>
1284
+ <div class="item-type-label">Goal</div>
1285
+ </div>
1286
+ <button class="item-close-btn" data-remove-goal="${esc(g.label)}">
1287
+ <span class="material-symbols-outlined" style="font-size:16px;">close</span>
1288
+ </button>
1289
+ </div>
1290
+ `).join('');
1291
+
1292
+ container.innerHTML = cardsHtml + '<div class="drop-placeholder"><span>+ Drop Goal</span></div>';
1293
+
1294
+ const badge = document.getElementById('goal-count');
1295
+ if (badge) badge.textContent = `${allGoals.length} Active`;
1296
+ }
1297
+
1298
+ // A2A-48: Orchestrator that renders the entire permissions panel.
1299
+ // Uses state.activeTierId instead of reading a dropdown value.
1300
+ function renderPermissions() {
1301
+ const tier = (state.settings?.tiers || []).find(t => t.id === state.activeTierId);
1302
+ if (!tier) return;
1303
+ renderTierCards();
1304
+ renderActiveTopics(tier);
1305
+ renderActiveGoals(tier);
1306
+ renderToolToggles(tier.allowed_tools);
1307
+ renderTierWarnings(tier);
1308
+ renderSidebarPreview(state.activeTierId);
1309
+ renderSidebarLists(tier);
1310
+ }
1311
+
1312
+ // A2A-48: Renders the inline "Preview as Caller" card in the right sidebar.
1313
+ // Reuses getPreviewData() to show merged topics, goals, and tool count.
1314
+ function renderSidebarPreview(tierId) {
1315
+ const container = document.getElementById('perm-preview');
1316
+ if (!container) return;
1317
+ const data = getPreviewData(tierId);
1318
+ const tierName = (state.settings?.tiers || []).find(t => t.id === tierId)?.name || tierId;
1319
+ const topicNames = data.topics.map(t => t.topic).filter(Boolean);
1320
+ const goalNames = data.objectives.map(g => g.objective || g.topic).filter(Boolean);
1321
+ const toolCount = data.tools.size;
1322
+
1323
+ const topicText = topicNames.length > 0 ? `<strong style="color:#2DD4BF;">${esc(topicNames.join(', '))}</strong>` : '<em>no topics</em>';
1324
+ const goalText = goalNames.length > 0 ? `<strong style="color:#FBBF24;">${esc(goalNames.join(', '))}</strong>` : '<em>no goals</em>';
1325
+
1326
+ container.innerHTML = `
1327
+ <div class="preview-card-inner">
1328
+ <div class="sidebar-list-header">Preview as Caller</div>
1329
+ <div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.5rem;">
1330
+ <span class="material-symbols-outlined" style="color:#60A5FA;">smart_toy</span>
1331
+ <div>
1332
+ <div style="font-weight:600;font-size:0.85rem;color:var(--ink);">Agent Permission</div>
1333
+ <span class="preview-tier-badge">${esc(tierName)} Tier</span>
1334
+ </div>
1335
+ </div>
1336
+ <div class="preview-summary">
1337
+ This agent can discuss ${topicText} to help ${goalText} using <strong>${toolCount} tool${toolCount !== 1 ? 's' : ''}</strong>.
1338
+ </div>
1339
+ <div class="preview-footer">
1340
+ <span style="display:flex;align-items:center;gap:0.3rem;">
1341
+ <span class="status-dot status-dot--green"></span> Active
1342
+ </span>
1343
+ <span style="font-family:monospace;opacity:0.7;">JSON Valid</span>
1344
+ </div>
1345
+ </div>
1346
+ `;
1347
+ }
1348
+
1349
+ // A2A-48: Renders all topics and goals in the right sidebar as draggable items.
1350
+ // Items active in the current tier are dimmed with an "(Active)" label.
1351
+ function renderSidebarLists(tier) {
1352
+ const topicContainer = document.getElementById('sidebar-topics');
1353
+ const goalContainer = document.getElementById('sidebar-goals');
1354
+ if (!topicContainer || !goalContainer || !tier) return;
1355
+
1356
+ // Collect ALL topics/goals across tiers, but prioritize the currently
1357
+ // rendered tier first so sidebar drag payloads carry the right metadata.
1358
+ // This also ensures unsaved in-DOM items from create/drop appear immediately.
1359
+ const baseTiers = state.settings?.tiers || [];
1360
+ const allTiers = [tier, ...baseTiers.filter(t => t.id !== tier.id)];
1361
+ const allTopicMap = new Map();
1362
+ const allGoalMap = new Map();
1363
+ for (const t of allTiers) {
1364
+ const mTopics = t.manifest?.topics || [];
1365
+ const fTopics = t.topics || [];
1366
+ const topics = mTopics.length > 0 ? mTopics : fTopics.map(x => ({ topic: x, description: '' }));
1367
+ for (const item of topics) {
1368
+ if (!item.topic) continue;
1369
+ const existingDesc = allTopicMap.get(item.topic);
1370
+ const nextDesc = item.description || '';
1371
+ if (!allTopicMap.has(item.topic) || (!existingDesc && nextDesc)) {
1372
+ allTopicMap.set(item.topic, nextDesc);
1373
+ }
1374
+ }
1375
+
1376
+ const mGoals = t.manifest?.objectives || [];
1377
+ const fGoals = t.goals || [];
1378
+ const goals = mGoals.length > 0 ? mGoals : fGoals.map(x => ({ topic: x, description: '' }));
1379
+ for (const item of goals) {
1380
+ const label = item.objective || item.topic;
1381
+ if (!label) continue;
1382
+ const existingDesc = allGoalMap.get(label);
1383
+ const nextDesc = item.description || '';
1384
+ if (!allGoalMap.has(label) || (!existingDesc && nextDesc)) {
1385
+ allGoalMap.set(label, nextDesc);
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ // Determine which are active in the current tier
1391
+ const activeTopicSet = new Set((tier.topics || []).concat(
1392
+ (tier.manifest?.topics || []).map(t => t.topic)
1393
+ ).filter(Boolean));
1394
+ const activeGoalSet = new Set((tier.goals || []).concat(
1395
+ (tier.manifest?.objectives || []).map(g => g.objective || g.topic)
1396
+ ).filter(Boolean));
1397
+
1398
+ // Render topics sidebar
1399
+ const topicItems = Array.from(allTopicMap.entries()).map(([name, desc]) => {
1400
+ const isActive = activeTopicSet.has(name);
1401
+ return `
1402
+ <div class="sidebar-item${isActive ? ' active-in-zone' : ''}" draggable="${isActive ? 'false' : 'true'}" data-sidebar-topic="${esc(name)}" data-description="${esc(desc)}" data-item-type="topic">
1403
+ <div style="display:flex;align-items:center;gap:0.4rem;">
1404
+ ${isActive ? '' : '<span class="material-symbols-outlined" style="color:#4B5563;font-size:1rem;cursor:grab;">drag_indicator</span>'}
1405
+ <span class="sidebar-item-name">${esc(name)}${isActive ? ' <span class="sidebar-item-active-label">(Active)</span>' : ''}</span>
1406
+ </div>
1407
+ </div>
1408
+ `;
1409
+ }).join('');
1410
+
1411
+ topicContainer.innerHTML = `
1412
+ <div class="sidebar-list-header">Topics</div>
1413
+ ${topicItems}
1414
+ <button class="sidebar-add-btn" data-add-type="topic">
1415
+ <span class="material-symbols-outlined" style="font-size:14px;">add</span> Add Topic
1416
+ </button>
1417
+ `;
1418
+
1419
+ // Render goals sidebar
1420
+ const goalItems = Array.from(allGoalMap.entries()).map(([name, desc]) => {
1421
+ const isActive = activeGoalSet.has(name);
1422
+ return `
1423
+ <div class="sidebar-item${isActive ? ' active-in-zone' : ''}" draggable="${isActive ? 'false' : 'true'}" data-sidebar-goal="${esc(name)}" data-description="${esc(desc)}" data-item-type="goal">
1424
+ <div style="display:flex;align-items:center;gap:0.4rem;">
1425
+ ${isActive ? '' : '<span class="material-symbols-outlined" style="color:#4B5563;font-size:1rem;cursor:grab;">drag_indicator</span>'}
1426
+ <span class="sidebar-item-name">${esc(name)}${isActive ? ' <span class="sidebar-item-active-label">(Active)</span>' : ''}</span>
1427
+ </div>
1428
+ </div>
1429
+ `;
1430
+ }).join('');
1431
+
1432
+ goalContainer.innerHTML = `
1433
+ <div class="sidebar-list-header">Goals &amp; Objectives</div>
1434
+ ${goalItems}
1435
+ <button class="sidebar-add-btn" data-add-type="goal">
1436
+ <span class="material-symbols-outlined" style="font-size:14px;">add</span> Add Goal
1437
+ </button>
1438
+ `;
1439
+ }
1440
+
1441
+ // A2A-61: Snapshot permissions payload from live DOM at mutation time.
1442
+ // This prevents polling/re-render races from dropping unsaved drag changes.
1443
+ function collectTierPayloadFromDom() {
1444
+ const toggles = document.querySelectorAll('#tool-toggles .toggle-switch input');
1445
+ const allowed_tools = Array.from(toggles).filter(t => t.checked).map(t => t.dataset.tool);
1446
+
1447
+ const topicCards = document.querySelectorAll('#active-topics-zone .active-item-card');
1448
+ // A2A-48: uses dataset.topic for BOTH topics and goals (NOT dataset.objective)
1449
+ // because parseTopicObjects() in dashboard.js only reads entry.topic.
1450
+ const topics = Array.from(topicCards).map(c => c.dataset.topic).filter(Boolean);
1451
+ const manifestTopics = Array.from(topicCards).map(c => ({
1452
+ topic: c.dataset.topic,
1453
+ description: c.dataset.description || ''
1454
+ })).filter(t => t.topic);
1455
+
1456
+ const goalCards = document.querySelectorAll('#active-goals-zone .active-item-card');
1457
+ const goals = Array.from(goalCards).map(c => c.dataset.topic).filter(Boolean);
1458
+ const manifestObjectives = Array.from(goalCards).map(c => ({
1459
+ topic: c.dataset.topic,
1460
+ description: c.dataset.description || ''
1461
+ })).filter(g => g.topic);
1462
+
1463
+ return {
1464
+ allowed_tools,
1465
+ topics,
1466
+ goals,
1467
+ manifest: {
1468
+ topics: manifestTopics,
1469
+ objectives: manifestObjectives
1470
+ }
1471
+ };
1472
+ }
1473
+
1474
+ // A2A-61: Build a tier view using live DOM state so sidebar active badges
1475
+ // stay accurate immediately after create/drop/delete (before round-trip save).
1476
+ function getTierSnapshotFromDom() {
1477
+ const tierId = state.activeTierId;
1478
+ const tier = (state.settings?.tiers || []).find(t => t.id === tierId);
1479
+ if (!tier) return null;
1480
+ const payload = collectTierPayloadFromDom();
1481
+ return {
1482
+ ...tier,
1483
+ allowed_tools: payload.allowed_tools,
1484
+ topics: payload.topics,
1485
+ goals: payload.goals,
1486
+ manifest: {
1487
+ ...(tier.manifest || {}),
1488
+ topics: payload.manifest.topics,
1489
+ objectives: payload.manifest.objectives
1490
+ }
1491
+ };
1492
+ }
1493
+
1494
+ function renderSidebarListsFromDom() {
1495
+ const snapshot = getTierSnapshotFromDom();
1496
+ if (snapshot) renderSidebarLists(snapshot);
1497
+ }
1498
+
1499
+ // A2A-61: Debounced auto-save with per-tier queueing.
1500
+ // Snapshot is captured at mutation time; timer only controls network cadence.
1501
+ let _autoSaveTimer = null;
1502
+ const _pendingTierSaves = new Map();
1503
+ let _tierSaveQueue = [];
1504
+ let _tierSaveInFlight = false;
1505
+
1506
+ function hasPendingTierSaves() {
1507
+ return Boolean(_autoSaveTimer) || _tierSaveInFlight || _tierSaveQueue.length > 0 || _pendingTierSaves.size > 0;
1508
+ }
1509
+
1510
+ function queueTierSave(tierId, payload) {
1511
+ if (!tierId || !payload) return;
1512
+ if (!_pendingTierSaves.has(tierId)) {
1513
+ _tierSaveQueue.push(tierId);
1514
+ }
1515
+ _pendingTierSaves.set(tierId, payload);
1516
+ }
1517
+
1518
+ async function flushTierSaveQueue() {
1519
+ if (_tierSaveInFlight) return;
1520
+ _tierSaveInFlight = true;
1521
+
1522
+ try {
1523
+ while (_tierSaveQueue.length > 0) {
1524
+ const tierId = _tierSaveQueue.shift();
1525
+ const body = _pendingTierSaves.get(tierId);
1526
+ _pendingTierSaves.delete(tierId);
1527
+ if (!tierId || !body) continue;
1528
+
1529
+ try {
1530
+ await request(`/settings/tiers/${encodeURIComponent(tierId)}`, {
1531
+ method: 'PUT',
1532
+ body: JSON.stringify(body)
1533
+ });
1534
+ showNotice('Saved');
1535
+
1536
+ // Refresh canonical settings after each successful save so future
1537
+ // renders and sidebar catalogs stay in sync with persisted state.
1538
+ const payload = await request('/settings');
1539
+ state.settings = payload;
1540
+ } catch (err) {
1541
+ showNotice(`Save failed: ${err.message}`);
1542
+ }
1543
+ }
1544
+ } finally {
1545
+ _tierSaveInFlight = false;
1546
+ if (_tierSaveQueue.length > 0) {
1547
+ flushTierSaveQueue().catch(() => {});
1548
+ }
1549
+ }
1550
+ }
1551
+
1552
+ function autoSaveTier(snapshotPayload = null) {
1553
+ const tierId = state.activeTierId;
1554
+ if (!tierId) return;
1555
+
1556
+ const payload = snapshotPayload || collectTierPayloadFromDom();
1557
+ queueTierSave(tierId, payload);
1558
+
1559
+ clearTimeout(_autoSaveTimer);
1560
+ _autoSaveTimer = setTimeout(() => {
1561
+ _autoSaveTimer = null;
1562
+ flushTierSaveQueue().catch(() => {});
1563
+ }, 250);
1564
+ }
1565
+
1566
+
1567
+ // A2A-50: Drop handler for active topic/goal zones. Routes items to the
1568
+ // CORRECT zone based on data.type (not the zone they were dropped on).
1569
+ // This fixes the bug where dragging a topic onto the goals zone would
1570
+ // incorrectly add it as a goal and vice versa.
1571
+ function handleZoneDrop(zone, e) {
1572
+ e.preventDefault();
1573
+ zone.classList.remove('drag-over');
1574
+
1575
+ let data;
1576
+ try { data = JSON.parse(e.dataTransfer.getData('application/json')); } catch { return; }
1577
+
1578
+ const name = String(data?.name || '').trim();
1579
+ if (!name) return;
1580
+
1581
+ // A2A-61: strict payload validation. Unknown explicit types are rejected
1582
+ // instead of silently defaulting into the goals column.
1583
+ let itemType = null;
1584
+ if (data.type === 'topic' || data.type === 'goal') {
1585
+ itemType = data.type;
1586
+ } else if (!data.type) {
1587
+ itemType = zone.id === 'active-topics-zone' ? 'topic' : 'goal';
1588
+ } else {
1589
+ return;
1590
+ }
1591
+
1592
+ const targetZoneId = itemType === 'topic' ? 'active-topics-zone' : 'active-goals-zone';
1593
+ const targetZone = document.getElementById(targetZoneId);
1594
+ if (!targetZone) return;
1595
+
1596
+ const accentClass = itemType === 'topic' ? 'active-item-card--teal' : 'active-item-card--yellow';
1597
+ const typeLabel = itemType === 'topic' ? 'Topic' : 'Goal';
1598
+ const removeAttr = itemType === 'topic' ? 'data-remove-topic' : 'data-remove-goal';
1599
+
1600
+ // Check if already in target zone
1601
+ const existing = targetZone.querySelectorAll('.active-item-card');
1602
+ for (const card of existing) {
1603
+ if (card.dataset.topic === name) return;
1604
+ }
1605
+
1606
+ // A2A-50: Shared helper builds the card HTML to avoid duplication with
1607
+ // the create-item-submit handler. Both paths now use buildItemCard().
1608
+ const card = buildItemCard(name, String(data.description || ''), accentClass, typeLabel, removeAttr);
1609
+ const placeholder = targetZone.querySelector('.drop-placeholder');
1610
+ if (placeholder) targetZone.insertBefore(card, placeholder);
1611
+ else targetZone.appendChild(card);
1612
+
1613
+ autoSaveTier();
1614
+ renderSidebarListsFromDom();
1615
+ }
1616
+
1617
+ // A2A-50: Shared helper to build an active-item card DOM element.
1618
+ // Used by both handleZoneDrop() and the create-item-submit handler
1619
+ // to avoid duplicating card HTML in two places.
1620
+ function buildItemCard(name, description, accentClass, typeLabel, removeAttr) {
1621
+ const card = document.createElement('div');
1622
+ card.className = `active-item-card ${accentClass}`;
1623
+ card.dataset.topic = name;
1624
+ card.dataset.description = description;
1625
+ card.innerHTML = `
1626
+ <div>
1627
+ <div class="item-name">${esc(name)}</div>
1628
+ <div class="item-type-label">${typeLabel}</div>
1629
+ </div>
1630
+ <button class="item-close-btn" ${removeAttr}="${esc(name)}">
1631
+ <span class="material-symbols-outlined" style="font-size:16px;">close</span>
1632
+ </button>
1633
+ `;
1634
+ return card;
1635
+ }
1636
+
1637
+ // A2A-50: Populates tier select dropdowns: #invite-tier (Invites tab),
1638
+ // #new-tier-copy-from (Settings tab), and #new-tier-dialog-copy-from
1639
+ // (new tier modal in Permissions tab).
1640
+ function populateInviteTierSelect() {
1641
+ const tiers = (state.settings?.tiers || []).slice().sort((a, b) => a.id.localeCompare(b.id));
1642
+ const newTierCopy = document.getElementById('new-tier-copy-from');
1643
+ const newTierDialogCopy = document.getElementById('new-tier-dialog-copy-from');
1644
+ const inviteTier = document.getElementById('invite-tier');
1645
+
1646
+ const optionsHtml = tiers.map(tier => {
1647
+ const emoji = TIER_EMOJIS[tier.id] || '\u{1F527}';
1648
+ return `<sl-option value="${esc(tier.id)}">${emoji} ${esc(tier.name || tier.id)}</sl-option>`;
1649
+ }).join('');
1650
+
1651
+ if (inviteTier) inviteTier.innerHTML = optionsHtml;
1652
+ if (newTierCopy) newTierCopy.innerHTML = `<sl-option value="">None</sl-option>${optionsHtml}`;
1653
+ // A2A-50: Also populate the Copy From select inside the new-tier-dialog modal
1654
+ if (newTierDialogCopy) newTierDialogCopy.innerHTML = `<sl-option value="">None</sl-option>${optionsHtml}`;
1655
+
1656
+ // A2A-48: Default invite tier to 'public'
1657
+ const defaultTier = tiers.find(t => t.id === 'public') ? 'public' : tiers[0]?.id;
1658
+ if (defaultTier && inviteTier) inviteTier.value = defaultTier;
1659
+ }
1660
+
1661
+ // A2A-41: contextual validation warnings for the currently selected tier.
1662
+ // Warns about empty tiers, dangerous tool grants, and inverted tier sizes.
1663
+ function renderTierWarnings(tier) {
1664
+ const container = document.getElementById('tier-warnings');
1665
+ if (!container) return;
1666
+ const warnings = [];
1667
+
1668
+ // A2A-41: use manifest OR flat topics (not both) to avoid double-counting.
1669
+ // Manifest is preferred when non-empty; flat array is the fallback.
1670
+ // NOTE: can't use || for this because empty arrays are truthy in JS.
1671
+ const mTopics = tier.manifest?.topics;
1672
+ const topicCount = (mTopics && mTopics.length > 0 ? mTopics : (tier.topics || [])).length;
1673
+ if (topicCount === 0) {
1674
+ warnings.push({ level: 'warn', text: "This tier has no topics \u2014 callers won't have conversation context." });
1675
+ }
1676
+
1677
+ if (tier.id === 'public' && (tier.allowed_tools || []).includes('Bash')) {
1678
+ warnings.push({ level: 'danger', text: 'Bash (full access) is granted to the public tier \u2014 any caller can execute commands.' });
1679
+ }
1680
+
1681
+ if (tier.id === 'family') {
1682
+ const allTiers = state.settings?.tiers || [];
1683
+ const friends = allTiers.find(t => t.id === 'friends');
1684
+ if (friends) {
1685
+ const mFam = tier.manifest?.topics;
1686
+ const familyOwn = (mFam && mFam.length > 0 ? mFam : (tier.topics || [])).length;
1687
+ const mFri = friends.manifest?.topics;
1688
+ const friendsOwn = (mFri && mFri.length > 0 ? mFri : (friends.topics || [])).length;
1689
+ if (familyOwn < friendsOwn) {
1690
+ warnings.push({ level: 'info', text: 'Family tier has fewer topics than Friends \u2014 usually Family is the most open tier.' });
1691
+ }
1692
+ }
1693
+ }
1694
+
1695
+ container.innerHTML = warnings.map(w =>
1696
+ `<div class="tier-warning ${w.level}">${esc(w.text)}</div>`
1697
+ ).join('');
1698
+ }
1699
+
1700
+ // A2A-41: merges topics/goals/tools from the selected tier and all lower tiers,
1701
+ // mirroring the backend's getTopicsForTier() inheritance. Used by the preview dialog.
1702
+ function getPreviewData(tierId) {
1703
+ const selectedIndex = TIER_ORDER.indexOf(tierId);
1704
+ const tiers = state.settings?.tiers || [];
1705
+ const merged = { topics: [], objectives: [], tools: new Set(), do_not_discuss: [], never_disclose: [] };
1706
+
1707
+ // A2A-41: for custom tiers not in TIER_ORDER, show only own data.
1708
+ // No inheritance is applied because custom tiers have no defined hierarchy.
1709
+ if (selectedIndex < 0) {
1710
+ const t = tiers.find(t => t.id === tierId);
1711
+ if (t) {
1712
+ (t.manifest?.topics || []).forEach(item => merged.topics.push({ ...item, source: tierId }));
1713
+ (t.manifest?.objectives || []).forEach(item => merged.objectives.push({ ...item, source: tierId }));
1714
+ (t.allowed_tools || []).forEach(tool => merged.tools.add(tool));
1715
+ }
1716
+ merged.never_disclose = state.settings?.manifest?.never_disclose || [];
1717
+ return merged;
1718
+ }
1719
+
1720
+ for (let i = 0; i <= selectedIndex; i++) {
1721
+ const t = tiers.find(t => t.id === TIER_ORDER[i]);
1722
+ if (!t) continue;
1723
+ (t.manifest?.topics || []).forEach(item => merged.topics.push({ ...item, source: TIER_ORDER[i] }));
1724
+ (t.manifest?.objectives || []).forEach(item => merged.objectives.push({ ...item, source: TIER_ORDER[i] }));
1725
+ (t.manifest?.do_not_discuss || []).forEach(item => {
1726
+ if (!merged.do_not_discuss.includes(item)) merged.do_not_discuss.push(item);
1727
+ });
1728
+ (t.allowed_tools || []).forEach(tool => merged.tools.add(tool));
1729
+ }
1730
+
1731
+ merged.never_disclose = state.settings?.manifest?.never_disclose || [];
1732
+ return merged;
1733
+ }
1734
+
1735
+ // A2A-48: opens the caller preview dialog showing the merged effective view
1736
+ // for the selected tier. Uses state.activeTierId instead of removed #tier-select.
1737
+ function openCallerPreview() {
1738
+ const tierId = state.activeTierId;
1739
+ const data = getPreviewData(tierId);
1740
+ const emoji = TIER_EMOJIS[tierId] || '\u{1F527}';
1741
+ const tierName = (state.settings?.tiers || []).find(t => t.id === tierId)?.name || tierId;
1742
+
1743
+ const dialog = document.getElementById('preview-dialog');
1744
+ dialog.label = `\u{1F441} Caller Preview \u2014 ${emoji} ${tierName}`;
1745
+
1746
+ const topicsList = data.topics.length > 0
1747
+ ? data.topics.map(t => `<li><strong>${esc(t.topic)}</strong>${t.description ? ` \u2014 ${esc(t.description)}` : ''}</li>`).join('')
1748
+ : '<li><em>None configured</em></li>';
1749
+
1750
+ const goalsList = data.objectives.length > 0
1751
+ ? data.objectives.map(g => `<li><strong>${esc(g.objective || g.topic)}</strong>${g.description ? ` \u2014 ${esc(g.description)}` : ''}</li>`).join('')
1752
+ : '<li><em>None configured</em></li>';
1753
+
1754
+ const toolsList = data.tools.size > 0
1755
+ ? Array.from(data.tools).map(t => `<li><strong>${esc(t)}</strong>${TOOL_DESCRIPTIONS[t] ? ` \u2014 ${esc(TOOL_DESCRIPTIONS[t])}` : ''}</li>`).join('')
1756
+ : '<li><em>None configured</em></li>';
1757
+
1758
+ const dndList = data.do_not_discuss.length > 0
1759
+ ? data.do_not_discuss.map(d => `<li>${esc(typeof d === 'string' ? d : d.topic || '')}</li>`).join('')
1760
+ : '<li><em>None configured</em></li>';
1761
+
1762
+ const neverList = data.never_disclose.length > 0
1763
+ ? data.never_disclose.map(n => `<li>${esc(n)}</li>`).join('')
1764
+ : '<li><em>None configured</em></li>';
1765
+
1766
+ document.getElementById('preview-content').innerHTML = `
1767
+ <h4>Topics this caller can discuss:</h4>
1768
+ <ul>${topicsList}</ul>
1769
+ <h4>Goals:</h4>
1770
+ <ul>${goalsList}</ul>
1771
+ <h4>Tools available:</h4>
1772
+ <ul>${toolsList}</ul>
1773
+ <h4>Will not discuss:</h4>
1774
+ <ul>${dndList}</ul>
1775
+ <h4>Never disclosed (any tier):</h4>
1776
+ <ul>${neverList}</ul>
1777
+ `;
1778
+
1779
+ dialog.show();
1780
+ }
1781
+
1782
+ // A2A-50: Shows the delete confirmation dialog for a topic/goal card.
1783
+ // Uses a unique marker attribute on the card so the confirm handler can
1784
+ // find and remove it. This avoids storing DOM references in closure state.
1785
+ let _deleteIdCounter = 0;
1786
+ function showDeleteConfirm(itemName, itemType, cardElement) {
1787
+ const dialog = document.getElementById('delete-confirm-dialog');
1788
+ if (!dialog) return;
1789
+ const label = itemType === 'topic' ? 'Topic' : 'Goal';
1790
+ const msgEl = document.getElementById('delete-confirm-message');
1791
+ if (msgEl) msgEl.textContent = `Remove ${label} "${itemName}" from this tier?`;
1792
+ // Tag the card with a unique ID so the confirm handler can find it
1793
+ const deleteId = `del-${++_deleteIdCounter}`;
1794
+ cardElement.setAttribute('data-delete-id', deleteId);
1795
+ dialog.dataset.deleteCardId = deleteId;
1796
+ dialog.show();
1797
+ }
1798
+
1799
+ // A2A-48: Binds all event handlers for the permissions panel. Replaces old
1800
+ // bindSettingsActions() — removes handlers for deleted elements (tier-form,
1801
+ // tier-select, copy-tier-btn, show-drag-columns, preview-caller-btn) and
1802
+ // adds handlers for tier cards, tool toggles, close buttons, and sidebar.
1803
+ function bindPermissionsActions() {
1804
+ const panel = document.getElementById('panel-permissions');
1805
+ if (!panel) return;
1806
+
1807
+ // A2A-48: Tier card click — switch active tier and re-render.
1808
+ // No autoSaveTier() here: switching tiers is a read operation, not a write.
1809
+ panel.addEventListener('click', (e) => {
1810
+ const card = e.target.closest('.tier-card[data-tier-id]');
1811
+ if (card) {
1812
+ state.activeTierId = card.dataset.tierId;
1813
+ renderPermissions();
1814
+ return;
1815
+ }
1816
+
1817
+ // A2A-50: Close button on active topic cards opens delete confirmation dialog
1818
+ const removeTopic = e.target.closest('[data-remove-topic]');
1819
+ if (removeTopic) {
1820
+ const card = removeTopic.closest('.active-item-card');
1821
+ if (card) {
1822
+ const itemName = card.dataset.topic || '';
1823
+ showDeleteConfirm(itemName, 'topic', card);
1824
+ }
1825
+ return;
1826
+ }
1827
+
1828
+ // A2A-50: Close button on active goal cards opens delete confirmation dialog
1829
+ const removeGoal = e.target.closest('[data-remove-goal]');
1830
+ if (removeGoal) {
1831
+ const card = removeGoal.closest('.active-item-card');
1832
+ if (card) {
1833
+ const itemName = card.dataset.topic || '';
1834
+ showDeleteConfirm(itemName, 'goal', card);
1835
+ }
1836
+ return;
1837
+ }
1838
+
1839
+ // A2A-50: "+ New Tier" button opens glass-styled sl-dialog instead of
1840
+ // scrolling to inline form. Keeps focus trap and accessibility from Shoelace.
1841
+ const newTierBtn = e.target.closest('#perm-new-tier-btn');
1842
+ if (newTierBtn) {
1843
+ const dialog = document.getElementById('new-tier-dialog');
1844
+ if (dialog) {
1845
+ const idInput = document.getElementById('new-tier-dialog-id');
1846
+ const nameInput = document.getElementById('new-tier-dialog-name');
1847
+ if (idInput) idInput.value = '';
1848
+ if (nameInput) nameInput.value = '';
1849
+ dialog.show();
1850
+ }
1851
+ return;
1852
+ }
1853
+
1854
+ // A2A-48: Sidebar "Add Topic" / "Add Goal" buttons open create dialog.
1855
+ // A2A-51: Also matches .col-header-add-btn for narrow viewports where sidebar is hidden.
1856
+ const addBtn = e.target.closest('[data-add-type]');
1857
+ if (addBtn) {
1858
+ const type = addBtn.dataset.addType;
1859
+ const dialog = document.getElementById('create-item-dialog');
1860
+ if (dialog) {
1861
+ dialog.label = `Create New ${type === 'topic' ? 'Topic' : 'Goal'}`;
1862
+ dialog.dataset.createType = type;
1863
+ const titleInput = document.getElementById('create-item-title');
1864
+ const descInput = document.getElementById('create-item-desc');
1865
+ if (titleInput) titleInput.value = '';
1866
+ if (descInput) descInput.value = '';
1867
+ dialog.show();
1868
+ }
1869
+ return;
1870
+ }
1871
+ });
1872
+
1873
+ // A2A-48: Drop zone listeners — bound ONCE here because the zone containers
1874
+ // (#active-topics-zone, #active-goals-zone) persist across renders. Only
1875
+ // their innerHTML is replaced by renderActiveTopics/renderActiveGoals.
1876
+ // Binding per-element would cause listener accumulation.
1877
+ // A2A-51: Uses dragenter/dragleave counter to prevent flickering when
1878
+ // cursor moves over child elements (cards, placeholder) inside the zone.
1879
+ const topicZone = document.getElementById('active-topics-zone');
1880
+ const goalZone = document.getElementById('active-goals-zone');
1881
+ [topicZone, goalZone].forEach(zone => {
1882
+ if (!zone) return;
1883
+ let dragCounter = 0;
1884
+ const clearZoneDragState = () => {
1885
+ dragCounter = 0;
1886
+ zone.classList.remove('drag-over');
1887
+ };
1888
+
1889
+ zone.addEventListener('dragenter', (e) => {
1890
+ e.preventDefault();
1891
+ dragCounter += 1;
1892
+ zone.classList.add('drag-over');
1893
+ });
1894
+ zone.addEventListener('dragover', (e) => { e.preventDefault(); });
1895
+ zone.addEventListener('dragleave', () => {
1896
+ dragCounter = Math.max(0, dragCounter - 1);
1897
+ if (dragCounter <= 0) zone.classList.remove('drag-over');
1898
+ });
1899
+ zone.addEventListener('drop', (e) => {
1900
+ clearZoneDragState();
1901
+ handleZoneDrop(zone, e);
1902
+ });
1903
+
1904
+ // A2A-61: Ensure highlight never gets stuck if drag ends outside zone.
1905
+ window.addEventListener('dragend', clearZoneDragState);
1906
+ });
1907
+
1908
+ // A2A-61: Delegated drag listeners on .perm-sidebar — survives innerHTML
1909
+ // rewrites in renderSidebarLists(). Replaces bindSidebarDrag() which bound
1910
+ // directly to elements destroyed on each render, causing the drag-drop
1911
+ // regression in A2A-41/48/50/51.
1912
+ const sidebar = document.querySelector('.perm-sidebar');
1913
+ if (sidebar) {
1914
+ sidebar.addEventListener('dragstart', (e) => {
1915
+ const item = e.target.closest('.sidebar-item[draggable="true"]');
1916
+ if (!item) return;
1917
+ const itemType = item.dataset.itemType || 'topic';
1918
+ const name = item.dataset.sidebarTopic || item.dataset.sidebarGoal || '';
1919
+ const desc = item.dataset.description || '';
1920
+ e.dataTransfer.effectAllowed = 'move';
1921
+ e.dataTransfer.setData('application/json', JSON.stringify({ name, description: desc, type: itemType }));
1922
+ item.classList.add('dragging');
1923
+ });
1924
+ sidebar.addEventListener('dragend', (e) => {
1925
+ const item = e.target.closest('.sidebar-item[draggable="true"]');
1926
+ if (item) item.classList.remove('dragging');
1927
+ });
1928
+ }
1929
+
1930
+ // A2A-48: Tool toggle change — auto-save and update card styling
1931
+ panel.addEventListener('change', (e) => {
1932
+ const toggle = e.target.closest('#tool-toggles .toggle-switch input');
1933
+ if (toggle) {
1934
+ const card = toggle.closest('.tool-toggle-card');
1935
+ if (card) {
1936
+ card.classList.toggle('enabled', toggle.checked);
1937
+ }
1938
+ autoSaveTier();
1939
+ return;
1940
+ }
1941
+ });
1942
+
1943
+ // A2A-50: Create Item dialog — submit handler. Uses shared buildItemCard()
1944
+ // helper to avoid duplicating card HTML with handleZoneDrop().
1945
+ document.getElementById('create-item-submit')?.addEventListener('click', () => {
1946
+ const dialog = document.getElementById('create-item-dialog');
1947
+ const titleInput = document.getElementById('create-item-title');
1948
+ const descInput = document.getElementById('create-item-desc');
1949
+ if (!dialog || !titleInput) return;
1950
+
1951
+ const title = titleInput.value.trim();
1952
+ if (!title) { titleInput.focus(); return; }
1953
+ const desc = descInput?.value?.trim() || '';
1954
+ const type = dialog.dataset.createType || 'topic';
1955
+
1956
+ const zoneId = type === 'topic' ? 'active-topics-zone' : 'active-goals-zone';
1957
+ const zone = document.getElementById(zoneId);
1958
+ if (!zone) return;
1959
+
1960
+ const accentClass = type === 'topic' ? 'active-item-card--teal' : 'active-item-card--yellow';
1961
+ const typeLabel = type === 'topic' ? 'Topic' : 'Goal';
1962
+ const removeAttr = type === 'topic' ? 'data-remove-topic' : 'data-remove-goal';
1963
+
1964
+ // A2A-61: Match drop-path dedupe behavior for create flow.
1965
+ const existing = zone.querySelectorAll('.active-item-card');
1966
+ for (const existingCard of existing) {
1967
+ if (existingCard.dataset.topic === title) {
1968
+ dialog.hide();
1969
+ showNotice(`Already active: ${title}`);
1970
+ return;
1971
+ }
1972
+ }
1973
+
1974
+ const card = buildItemCard(title, desc, accentClass, typeLabel, removeAttr);
1975
+ const placeholder = zone.querySelector('.drop-placeholder');
1976
+ if (placeholder) zone.insertBefore(card, placeholder);
1977
+ else zone.appendChild(card);
1978
+
1979
+ dialog.hide();
1980
+ autoSaveTier();
1981
+ renderSidebarListsFromDom();
1982
+ });
1983
+
1984
+ // A2A-48: Create Item dialog — cancel handler
1985
+ document.getElementById('create-item-cancel')?.addEventListener('click', () => {
1986
+ document.getElementById('create-item-dialog')?.hide();
1987
+ });
1988
+
1989
+ // A2A-50: New Tier dialog — submit handler (replaces inline form handler).
1990
+ // Creates tier via POST and switches to it.
1991
+ document.getElementById('new-tier-dialog-submit')?.addEventListener('click', async () => {
1992
+ const tierId = document.getElementById('new-tier-dialog-id')?.value?.trim();
1993
+ const name = document.getElementById('new-tier-dialog-name')?.value?.trim();
1994
+ const copyFrom = document.getElementById('new-tier-dialog-copy-from')?.value;
1995
+ if (!tierId) {
1996
+ document.getElementById('new-tier-dialog-id')?.focus();
1997
+ return;
1998
+ }
1999
+ try {
2000
+ await request('/settings/tiers', {
2001
+ method: 'POST',
2002
+ body: JSON.stringify({
2003
+ id: tierId,
2004
+ name: name || tierId,
2005
+ copy_from: copyFrom || undefined
2006
+ })
2007
+ });
2008
+ showNotice(`Created tier "${tierId}"`);
2009
+ document.getElementById('new-tier-dialog')?.hide();
2010
+ await loadSettings();
2011
+ state.activeTierId = tierId;
2012
+ renderPermissions();
2013
+ } catch (err) {
2014
+ showNotice(err.message);
2015
+ }
2016
+ });
2017
+
2018
+ // A2A-50: New Tier dialog — cancel handler
2019
+ document.getElementById('new-tier-dialog-cancel')?.addEventListener('click', () => {
2020
+ document.getElementById('new-tier-dialog')?.hide();
2021
+ });
2022
+
2023
+ // A2A-50: Delete confirm dialog — confirm handler. Removes the card that
2024
+ // was stored in dataset and triggers auto-save.
2025
+ document.getElementById('delete-confirm-yes')?.addEventListener('click', () => {
2026
+ const dialog = document.getElementById('delete-confirm-dialog');
2027
+ if (!dialog) return;
2028
+ const cardId = dialog.dataset.deleteCardId;
2029
+ if (cardId) {
2030
+ const card = document.querySelector(`.active-item-card[data-delete-id="${cardId}"]`);
2031
+ if (card) {
2032
+ card.removeAttribute('data-delete-id');
2033
+ card.remove();
2034
+ }
2035
+ }
2036
+ dialog.hide();
2037
+ autoSaveTier();
2038
+ renderSidebarListsFromDom();
2039
+ });
2040
+
2041
+ // A2A-50: Delete confirm dialog — cancel handler
2042
+ document.getElementById('delete-confirm-no')?.addEventListener('click', () => {
2043
+ const dialog = document.getElementById('delete-confirm-dialog');
2044
+ if (dialog) {
2045
+ // Clean up the marker attribute from the card
2046
+ const cardId = dialog.dataset.deleteCardId;
2047
+ if (cardId) {
2048
+ const card = document.querySelector(`.active-item-card[data-delete-id="${cardId}"]`);
2049
+ if (card) card.removeAttribute('data-delete-id');
2050
+ }
2051
+ dialog.hide();
2052
+ }
2053
+ });
2054
+
2055
+ // Preview dialog close — unchanged
2056
+ document.getElementById('preview-close-btn')?.addEventListener('click', () => {
2057
+ document.getElementById('preview-dialog').hide();
2058
+ });
2059
+ }
2060
+
2061
+ // A2A-48: Load settings and render permissions. Replaces fillTierSelects() and
2062
+ // renderTierColumns() calls with populateInviteTierSelect() + renderPermissions().
2063
+ async function loadSettings() {
2064
+ const payload = await request('/settings');
2065
+ state.settings = payload;
2066
+ populateInviteTierSelect();
2067
+ renderPermissions();
2068
+ document.getElementById('defaults-expiration').value = payload.defaults?.expiration || '7d';
2069
+ document.getElementById('defaults-max-calls').value = payload.defaults?.maxCalls || 100;
2070
+ }
2071
+
2072
+ function renderCallbookStatus() {
2073
+ const el = document.getElementById('callbook-status');
2074
+ if (!el) return;
2075
+
2076
+ const s = state.dashboardStatus;
2077
+ if (!s) {
2078
+ el.textContent = 'Loading\u2026';
2079
+ return;
2080
+ }
2081
+
2082
+ const warnings = Array.isArray(s.warnings) ? s.warnings : [];
2083
+ const publicUrl = s.public_dashboard_url || '-';
2084
+ const enabled = Boolean(s.callbook && s.callbook.enabled);
2085
+ const deviceCount = s.callbook && Number.isFinite(s.callbook.device_count) ? s.callbook.device_count : 0;
2086
+ const invite = s.invite_host || null;
2087
+ const inviteSource = invite && invite.source ? invite.source : null;
2088
+ const inviteResolved = invite && invite.host ? invite.host : null;
2089
+ const ext = s.external_ip || null;
2090
+ const extAttempts = ext && Array.isArray(ext.attempts) ? ext.attempts : [];
2091
+ const extMeta = [];
2092
+ if (ext && ext.source) extMeta.push(ext.source);
2093
+ if (ext && ext.checked_at) extMeta.push(`checked ${fmtDate(ext.checked_at)}`);
2094
+ if (ext && ext.from_cache) extMeta.push('cache');
2095
+ if (ext && ext.stale) extMeta.push('stale');
2096
+ const extMetaText = extMeta.length ? ` <span class="mono">(${esc(extMeta.join(', '))})</span>` : '';
2097
+ const extErrorText = ext && ext.error ? esc(ext.error) : '';
2098
+ const extAttemptsHtml = extAttempts.length
2099
+ ? `<sl-details summary="External IP probe" style="margin-top:0.5rem;">
2100
+ <div class="mono" style="margin-top:0.35rem;">
2101
+ ${extAttempts.map(a => {
2102
+ const service = a && a.service ? String(a.service) : '-';
2103
+ const ok = Boolean(a && a.ok);
2104
+ const status = a && a.statusCode ? ` (${a.statusCode})` : '';
2105
+ const err = a && a.error ? ` (${a.error})` : '';
2106
+ return esc(`${service}: ${ok ? 'ok' + status : 'failed' + err}`);
2107
+ }).join('<br>')}
2108
+ </div>
2109
+ </sl-details>`
2110
+ : '';
2111
+
2112
+ el.innerHTML = `
2113
+ <div><strong>Public dashboard URL:</strong> <span class="mono">${esc(publicUrl)}</span></div>
2114
+ <div><strong>Invite host:</strong> <span class="mono">${esc(inviteResolved || '-')}</span>${inviteSource ? ` <span class="mono">(${esc(inviteSource)})</span>` : ''}</div>
2115
+ <div><strong>External IP (egress):</strong> <span class="mono">${esc((ext && ext.ip) ? ext.ip : '-')}</span>${extMetaText}</div>
2116
+ ${extErrorText ? `<div style="margin-top:0.35rem;"><strong>External IP error:</strong> <span class="mono">${extErrorText}</span></div>` : ''}
2117
+ ${extAttemptsHtml}
2118
+ <div><strong>Callbook session storage:</strong> ${enabled ? 'enabled' : 'disabled'}</div>
2119
+ <div><strong>Paired devices:</strong> ${deviceCount}</div>
2120
+ ${warnings.length ? `<div style="margin-top:0.5rem;"><strong>Warnings:</strong><br>${warnings.map(w => esc(w)).join('<br>')}</div>` : ''}
2121
+ `;
2122
+ }
2123
+
2124
+ function renderAutoUpdateStatus() {
2125
+ const el = document.getElementById('auto-update-status');
2126
+ const toggleBtn = document.getElementById('auto-update-toggle');
2127
+ if (!el) return;
2128
+
2129
+ const au = state.autoUpdate;
2130
+ if (!au) {
2131
+ el.textContent = 'Loading\u2026';
2132
+ if (toggleBtn) toggleBtn.disabled = true;
2133
+ return;
2134
+ }
2135
+
2136
+ const stateText = formatUpdaterState(au.state);
2137
+ const variant = badgeVariant(au.state);
2138
+ const enabled = Boolean(au.enabled);
2139
+ const intervalSec = Number.isFinite(au.interval_ms) ? Math.floor(au.interval_ms / 1000) : null;
2140
+
2141
+ el.innerHTML = `
2142
+ <div><strong>Status:</strong> <sl-badge variant="${variant}">${esc(stateText)}</sl-badge></div>
2143
+ <div><strong>Enabled:</strong> ${enabled ? 'yes' : 'no'}</div>
2144
+ <div><strong>Current version:</strong> <span class="mono">${esc(au.current_version || '-')}</span></div>
2145
+ <div><strong>Latest version:</strong> <span class="mono">${esc(au.latest_version || '-')}</span></div>
2146
+ <div><strong>Target version:</strong> <span class="mono">${esc(au.target_version || '-')}</span></div>
2147
+ <div><strong>Active calls:</strong> ${esc(String(au.active_calls || 0))}</div>
2148
+ <div><strong>Interval:</strong> ${intervalSec === null ? '-' : `${intervalSec}s`}</div>
2149
+ <div><strong>Last checked:</strong> ${esc(fmtDate(au.last_checked_at))}</div>
2150
+ <div><strong>Last success:</strong> ${esc(fmtDate(au.last_success_at))}</div>
2151
+ ${au.defer_reason ? `<div><strong>Deferred:</strong> ${esc(au.defer_reason)}</div>` : ''}
2152
+ ${au.last_error ? `<div><strong>Error:</strong> <span class="mono">${esc(au.last_error)}</span></div>` : ''}
2153
+ `;
2154
+
2155
+ if (toggleBtn) {
2156
+ toggleBtn.disabled = false;
2157
+ toggleBtn.textContent = enabled ? 'Disable auto-update' : 'Enable auto-update';
2158
+ }
2159
+ }
2160
+
2161
+ async function loadDashboardStatus(refreshIp = false) {
2162
+ const payload = await request(`/status${refreshIp ? '?refresh_ip=true' : ''}`);
2163
+ state.dashboardStatus = payload;
2164
+ state.autoUpdate = payload.auto_update || state.autoUpdate;
2165
+ renderCallbookStatus();
2166
+ renderAutoUpdateStatus();
2167
+ renderContacts();
2168
+ renderContactDetail();
2169
+ }
2170
+
2171
+ async function loadAutoUpdateStatus() {
2172
+ const payload = await request('/update/status');
2173
+ state.autoUpdate = payload.auto_update || null;
2174
+ renderAutoUpdateStatus();
2175
+ }
2176
+
2177
+ function renderCallbookDevices() {
2178
+ const tbody = document.querySelector('#callbook-devices-table tbody');
2179
+ if (!tbody) return;
2180
+ tbody.innerHTML = '';
2181
+
2182
+ const devices = Array.isArray(state.callbookDevices) ? state.callbookDevices : [];
2183
+ if (devices.length === 0) {
2184
+ const tr = document.createElement('tr');
2185
+ tr.innerHTML = '<td colspan="6">No devices found.</td>';
2186
+ tbody.appendChild(tr);
2187
+ return;
2188
+ }
2189
+
2190
+ devices.forEach(dev => {
2191
+ const tr = document.createElement('tr');
2192
+ const revoked = Boolean(dev.revoked_at);
2193
+ const sessions = dev.active_sessions ?? '-';
2194
+ tr.innerHTML = `
2195
+ <td>${esc(dev.label || dev.id || '-')}</td>
2196
+ <td>${esc(fmtDate(dev.created_at))}</td>
2197
+ <td>${esc(fmtDate(dev.last_used_at))}</td>
2198
+ <td>${esc(String(sessions))}</td>
2199
+ <td>${revoked ? esc(fmtDate(dev.revoked_at)) : '-'}</td>
2200
+ <td>
2201
+ <sl-button size="small" variant="danger" data-revoke="${esc(dev.id)}" ${revoked ? 'disabled' : ''}>Revoke</sl-button>
2202
+ </td>
2203
+ `;
2204
+ tbody.appendChild(tr);
2205
+ });
2206
+
2207
+ tbody.querySelectorAll('[data-revoke]').forEach(btn => {
2208
+ btn.addEventListener('click', async () => {
2209
+ const deviceId = btn.dataset.revoke;
2210
+ if (!deviceId) return;
2211
+ btn.disabled = true;
2212
+ try {
2213
+ await request(`/callbook/devices/${encodeURIComponent(deviceId)}/revoke`, { method: 'POST' });
2214
+ showNotice('Device revoked');
2215
+ await loadCallbookDevices();
2216
+ } catch (err) {
2217
+ showNotice(err.message);
2218
+ btn.disabled = false;
2219
+ }
2220
+ });
2221
+ });
2222
+ }
2223
+
2224
+ async function loadCallbookDevices() {
2225
+ const payload = await request('/callbook/devices?include_revoked=true');
2226
+ state.callbookDevices = payload.devices || [];
2227
+ renderCallbookDevices();
2228
+ }
2229
+
2230
+ function bindCallbookActions() {
2231
+ const form = document.getElementById('callbook-provision-form');
2232
+ if (!form) return;
2233
+
2234
+ const urlEl = document.getElementById('callbook-install-url');
2235
+ const labelEl = document.getElementById('callbook-label');
2236
+ const warningsEl = document.getElementById('callbook-warnings');
2237
+
2238
+ document.getElementById('callbook-logout')?.addEventListener('click', async () => {
2239
+ try {
2240
+ await request('/callbook/logout', { method: 'POST' });
2241
+ showNotice('Logged out (cookie cleared)');
2242
+ } catch (err) {
2243
+ showNotice(err.message);
2244
+ }
2245
+ });
2246
+
2247
+ document.getElementById('callbook-copy-url')?.addEventListener('click', async () => {
2248
+ const ok = await copyText(urlEl?.value || '');
2249
+ showNotice(ok ? 'Copied' : 'Copy failed');
2250
+ });
2251
+
2252
+ form.addEventListener('submit', async (e) => {
2253
+ e.preventDefault();
2254
+ if (warningsEl) warningsEl.textContent = '';
2255
+ if (urlEl) urlEl.value = '';
2256
+
2257
+ const body = {
2258
+ label: labelEl ? labelEl.value : 'Callbook Remote',
2259
+ ttl_hours: 24
2260
+ };
2261
+
2262
+ try {
2263
+ const result = await request('/callbook/provision', {
2264
+ method: 'POST',
2265
+ body: JSON.stringify(body)
2266
+ });
2267
+ if (urlEl) urlEl.value = result.install_url || '';
2268
+ const warnings = Array.isArray(result.warnings) ? result.warnings : [];
2269
+ const expiresAt = result.expires_at ? `Expires: ${fmtDate(result.expires_at)}` : '';
2270
+ if (warningsEl) {
2271
+ warningsEl.textContent = [expiresAt, ...warnings].filter(Boolean).join('\n');
2272
+ }
2273
+ showNotice('Install link created');
2274
+ } catch (err) {
2275
+ showNotice(err.message);
2276
+ }
2277
+ });
2278
+ }
2279
+
2280
+ function bindAutoUpdateActions() {
2281
+ document.getElementById('auto-update-check')?.addEventListener('click', async () => {
2282
+ try {
2283
+ await request('/update/check', { method: 'POST', body: JSON.stringify({}) });
2284
+ await loadAutoUpdateStatus();
2285
+ showNotice('Update check complete');
2286
+ } catch (err) {
2287
+ showNotice(err.message);
2288
+ }
2289
+ });
2290
+
2291
+ document.getElementById('auto-update-now')?.addEventListener('click', async () => {
2292
+ try {
2293
+ await request('/update/now', { method: 'POST', body: JSON.stringify({}) });
2294
+ await loadAutoUpdateStatus();
2295
+ showNotice('Update triggered');
2296
+ } catch (err) {
2297
+ showNotice(err.message);
2298
+ }
2299
+ });
2300
+
2301
+ document.getElementById('auto-update-toggle')?.addEventListener('click', async () => {
2302
+ const au = state.autoUpdate || {};
2303
+ const nextEnabled = !Boolean(au.enabled);
2304
+ try {
2305
+ await request('/update/config', {
2306
+ method: 'PUT',
2307
+ body: JSON.stringify({ enabled: nextEnabled })
2308
+ });
2309
+ await loadAutoUpdateStatus();
2310
+ showNotice(nextEnabled ? 'Auto-update enabled' : 'Auto-update disabled');
2311
+ } catch (err) {
2312
+ showNotice(err.message);
2313
+ }
2314
+ });
2315
+ }
2316
+
2317
+ function renderInvites() {
2318
+ const tbody = document.querySelector('#invites-table tbody');
2319
+ tbody.innerHTML = '';
2320
+ state.invites.forEach(invite => {
2321
+ const tr = document.createElement('tr');
2322
+ tr.innerHTML = `
2323
+ <td>${invite.id}</td>
2324
+ <td>${invite.name || '-'}</td>
2325
+ <td>${invite.tier || '-'}</td>
2326
+ <td>${invite.calls_made || 0}${invite.max_calls ? `/${invite.max_calls}` : ''}</td>
2327
+ <td>${fmtDate(invite.expires_at)}</td>
2328
+ <td><sl-badge variant="${invite.revoked ? 'danger' : 'success'}">${invite.revoked ? 'revoked' : 'active'}</sl-badge></td>
2329
+ <td><sl-button size="small" variant="danger" data-revoke="${invite.id}" ${invite.revoked ? 'disabled' : ''}>Revoke</sl-button></td>
2330
+ `;
2331
+ tbody.appendChild(tr);
2332
+ });
2333
+
2334
+ tbody.querySelectorAll('[data-revoke]').forEach(btn => {
2335
+ btn.addEventListener('click', async () => {
2336
+ const tokenId = btn.dataset.revoke;
2337
+ await request(`/invites/${encodeURIComponent(tokenId)}/revoke`, { method: 'POST' });
2338
+ showNotice(`Revoked ${tokenId}`);
2339
+ await loadInvites();
2340
+ });
2341
+ });
2342
+ }
2343
+
2344
+ async function loadInvites() {
2345
+ const payload = await request('/invites?include_revoked=true');
2346
+ state.invites = payload.invites || [];
2347
+ renderInvites();
2348
+ }
2349
+
2350
+ function bindInviteActions() {
2351
+ const cancelBtn = document.getElementById('generate-invite-cancel');
2352
+ const inviteDetails = document.getElementById('generate-invite-details');
2353
+ const inviteMessageWrap = document.getElementById('invite-message-wrap');
2354
+ const inviteMessage = document.getElementById('invite-message');
2355
+
2356
+ // Cancel button collapses the sl-details
2357
+ if (cancelBtn && inviteDetails) {
2358
+ cancelBtn.addEventListener('click', () => {
2359
+ inviteDetails.open = false;
2360
+ });
2361
+ }
2362
+
2363
+ document.getElementById('invite-form').addEventListener('submit', async (e) => {
2364
+ e.preventDefault();
2365
+ const body = {
2366
+ name: document.getElementById('invite-name').value,
2367
+ owner: document.getElementById('invite-owner').value,
2368
+ tier: document.getElementById('invite-tier').value,
2369
+ expires: document.getElementById('invite-expires').value,
2370
+ max_calls: Number.parseInt(document.getElementById('invite-max-calls').value, 10),
2371
+ notify: document.getElementById('invite-notify').value
2372
+ };
2373
+ const result = await request('/invites', {
2374
+ method: 'POST',
2375
+ body: JSON.stringify(body)
2376
+ });
2377
+ // Show the invite message textarea with the result
2378
+ if (inviteMessage) {
2379
+ inviteMessage.value = result.invite_message || result.invite_url;
2380
+ if (inviteMessageWrap) inviteMessageWrap.style.display = 'block';
2381
+ }
2382
+ // Collapse the details after successful creation
2383
+ if (inviteDetails) inviteDetails.open = false;
2384
+ if (result.warnings && result.warnings.length) {
2385
+ showNotice(result.warnings[0]);
2386
+ } else {
2387
+ showNotice('Invite created');
2388
+ }
2389
+ await loadInvites();
2390
+ });
2391
+ }
2392
+
2393
+ function bindLogFilterRefresh() {
2394
+ // Auto-refresh logs as filters change (debounced).
2395
+ let debounce = null;
2396
+ const schedule = () => {
2397
+ clearTimeout(debounce);
2398
+ debounce = setTimeout(() => loadLogs().catch(err => showNotice(err.message)), 250);
2399
+ };
2400
+ [
2401
+ 'logs-level',
2402
+ 'logs-component',
2403
+ 'logs-event',
2404
+ 'logs-trace',
2405
+ 'logs-conversation',
2406
+ 'logs-token',
2407
+ 'logs-search',
2408
+ 'logs-limit'
2409
+ ].forEach(id => {
2410
+ const el = document.getElementById(id);
2411
+ if (!el) return;
2412
+ // Shoelace components fire sl-input and sl-change events
2413
+ el.addEventListener('sl-input', schedule);
2414
+ el.addEventListener('sl-change', schedule);
2415
+ // Also listen for native events as fallback
2416
+ el.addEventListener('input', schedule);
2417
+ el.addEventListener('change', schedule);
2418
+ });
2419
+ }
2420
+
2421
+ // --- Smart tab polling ---
2422
+
2423
+ let pollTimer = null;
2424
+
2425
+ // A2A-47: Simply return tracked state instead of querying sl-tab-group
2426
+ function getActiveTab() {
2427
+ return state.activeTab || 'contacts';
2428
+ }
2429
+
2430
+ const tabLoaders = {
2431
+ contacts: loadContacts,
2432
+ calls: loadCalls,
2433
+ logs: () => { loadLogs(); loadLogStats(); },
2434
+ // A2A-48/A2A-61: Load fresh settings data when switching to Permissions,
2435
+ // but skip polling refresh while local drag/create/delete saves are pending.
2436
+ permissions: () => {
2437
+ if (hasPendingTierSaves()) return Promise.resolve();
2438
+ return loadSettings();
2439
+ },
2440
+ invites: loadInvites,
2441
+ health: loadHealth,
2442
+ // A2A-50: Settings tab loads dashboard status, auto-update, and callbook data
2443
+ settings: () => { loadDashboardStatus(); loadAutoUpdateStatus(); loadCallbookDevices(); },
2444
+ };
2445
+
2446
+ function startPolling() {
2447
+ stopPolling();
2448
+ pollTimer = setInterval(() => {
2449
+ const loader = tabLoaders[getActiveTab()];
2450
+ if (loader) loader().catch(() => {});
2451
+ }, 5000);
2452
+ }
2453
+
2454
+ function stopPolling() {
2455
+ if (pollTimer) { clearInterval(pollTimer); pollTimer = null; }
2456
+ }
2457
+
2458
+ function onTabSwitch(tabName) {
2459
+ const loader = tabLoaders[tabName];
2460
+ if (loader) {
2461
+ try { loader().catch(() => {}); } catch (_) {}
2462
+ }
2463
+ startPolling(); // reset the 5s timer
2464
+ }
2465
+
2466
+ // === Health Tab (A2A-42) ===
2467
+
2468
+ // A2A-42: Escape HTML entities for safe innerHTML rendering of step names/errors.
2469
+ function escapeHtml(s) {
2470
+ return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
2471
+ }
2472
+
2473
+ async function loadHealth() {
2474
+ try {
2475
+ const data = await request('/test-results');
2476
+ renderHealthLatest(data.latest);
2477
+ renderHealthHistory(data.history || []);
2478
+ } catch (err) {
2479
+ renderHealthLatest(null);
2480
+ renderHealthHistory([]);
2481
+ }
2482
+ }
2483
+
2484
+ function renderHealthLatest(latest) {
2485
+ const card = document.getElementById('health-latest');
2486
+ if (!card) return;
2487
+
2488
+ if (!latest) {
2489
+ card.innerHTML = '<p>No test results available. Run <code>node test/e2e/orchestrate.js --persist</code> to generate.</p>';
2490
+ return;
2491
+ }
2492
+
2493
+ const statusBadge = latest.status === 'passed'
2494
+ ? '<sl-badge variant="success">PASSED</sl-badge>'
2495
+ : '<sl-badge variant="danger">FAILED</sl-badge>';
2496
+
2497
+ const regression = latest.regression;
2498
+ let regressionHtml = '';
2499
+ if (regression && regression.detected) {
2500
+ regressionHtml = `<p><sl-badge variant="warning">REGRESSION</sl-badge> New failures: ${regression.newFailures.map(escapeHtml).join(', ')}</p>`;
2501
+ }
2502
+ if (regression && regression.fixedTests && regression.fixedTests.length > 0) {
2503
+ regressionHtml += `<p><sl-badge variant="success">FIXED</sl-badge> ${regression.fixedTests.map(escapeHtml).join(', ')}</p>`;
2504
+ }
2505
+
2506
+ const ts = latest.finishedAt ? new Date(latest.finishedAt).toLocaleString() : 'unknown';
2507
+ const summary = latest.summary || {};
2508
+
2509
+ card.innerHTML = `
2510
+ <div class="row">
2511
+ <strong>Latest Run</strong> ${statusBadge}
2512
+ </div>
2513
+ <p><strong>Duration:</strong> ${latest.duration || 0}ms &middot;
2514
+ <strong>Passed:</strong> ${summary.passed || 0} &middot;
2515
+ <strong>Failed:</strong> ${summary.failed || 0} &middot;
2516
+ <strong>Skipped:</strong> ${summary.skipped || 0} &middot;
2517
+ <strong>Time:</strong> ${ts}</p>
2518
+ ${regressionHtml}
2519
+ <details>
2520
+ <summary>Steps (${(latest.steps || []).length})</summary>
2521
+ <ul>
2522
+ ${(latest.steps || []).map(s => {
2523
+ const icon = s.status === 'pass' ? '&#x2705;' : s.status === 'fail' ? '&#x274C;' : '&#x23ED;';
2524
+ const err = s.error ? ` — <code>${escapeHtml(String(s.error).slice(0, 120))}</code>` : '';
2525
+ return `<li>${icon} ${escapeHtml(s.name)}${err}</li>`;
2526
+ }).join('')}
2527
+ </ul>
2528
+ </details>
2529
+ `;
2530
+ }
2531
+
2532
+ function renderHealthHistory(history) {
2533
+ const tbody = document.querySelector('#health-history-table tbody');
2534
+ if (!tbody) return;
2535
+
2536
+ if (!history || history.length === 0) {
2537
+ tbody.innerHTML = '<tr><td colspan="6">No history</td></tr>';
2538
+ return;
2539
+ }
2540
+
2541
+ tbody.innerHTML = history.map(r => {
2542
+ const badge = r.status === 'passed'
2543
+ ? '<sl-badge variant="success" size="small">PASS</sl-badge>'
2544
+ : '<sl-badge variant="danger" size="small">FAIL</sl-badge>';
2545
+ const summary = r.summary || {};
2546
+ const regression = r.regression;
2547
+ const regText = regression && regression.detected
2548
+ ? `<sl-badge variant="warning" size="small">${regression.newFailures.length} new</sl-badge>`
2549
+ : '-';
2550
+ const ts = r.finishedAt ? new Date(r.finishedAt).toLocaleString() : '-';
2551
+ return `<tr>
2552
+ <td>${badge}</td>
2553
+ <td>${r.duration || 0}ms</td>
2554
+ <td>${summary.passed || 0}</td>
2555
+ <td>${summary.failed || 0}</td>
2556
+ <td>${regText}</td>
2557
+ <td>${ts}</td>
2558
+ </tr>`;
2559
+ }).join('');
2560
+ }
2561
+
2562
+ // A2A-50: Binds handlers for the Settings panel (defaults form, new-tier form).
2563
+ // These were previously inside bindPermissionsActions() but are now in the
2564
+ // separate #panel-settings panel.
2565
+ function bindSettingsActions() {
2566
+ // Defaults form
2567
+ document.getElementById('defaults-form')?.addEventListener('submit', async (e) => {
2568
+ e.preventDefault();
2569
+ try {
2570
+ await request('/settings/defaults', {
2571
+ method: 'PUT',
2572
+ body: JSON.stringify({
2573
+ expiration: document.getElementById('defaults-expiration').value,
2574
+ maxCalls: Number.parseInt(document.getElementById('defaults-max-calls').value, 10) || 100
2575
+ })
2576
+ });
2577
+ showNotice('Saved defaults');
2578
+ await loadSettings();
2579
+ } catch (err) {
2580
+ showNotice(err.message);
2581
+ }
2582
+ });
2583
+
2584
+ // New Tier form (inline form in Settings tab, kept for non-modal access)
2585
+ document.getElementById('new-tier-form')?.addEventListener('submit', async (e) => {
2586
+ e.preventDefault();
2587
+ const tierId = document.getElementById('new-tier-id')?.value?.trim();
2588
+ const name = document.getElementById('new-tier-name')?.value?.trim();
2589
+ const copyFrom = document.getElementById('new-tier-copy-from')?.value;
2590
+ if (!tierId) return;
2591
+ try {
2592
+ await request('/settings/tiers', {
2593
+ method: 'POST',
2594
+ body: JSON.stringify({
2595
+ id: tierId,
2596
+ name: name || tierId,
2597
+ copy_from: copyFrom || undefined
2598
+ })
2599
+ });
2600
+ showNotice(`Created tier "${tierId}"`);
2601
+ document.getElementById('new-tier-form')?.reset();
2602
+ await loadSettings();
2603
+ state.activeTierId = tierId;
2604
+ renderPermissions();
2605
+ } catch (err) {
2606
+ showNotice(err.message);
2607
+ }
2608
+ });
2609
+ }
2610
+
2611
+ async function bootstrap() {
2612
+ bindTabs();
2613
+ bindContactsActions();
2614
+ // A2A-48: bindPermissionsActions() replaces old bindSettingsActions() +
2615
+ // bindItemListDelegation(). All tier/tool/topic/goal handlers are now
2616
+ // inside bindPermissionsActions() using event delegation on #panel-permissions.
2617
+ bindPermissionsActions();
2618
+ // A2A-50: Settings panel handlers (defaults, new-tier inline form)
2619
+ bindSettingsActions();
2620
+ bindCallbookActions();
2621
+ bindAutoUpdateActions();
2622
+ bindInviteActions();
2623
+ bindLogFilterRefresh();
2624
+
2625
+ try {
2626
+ await Promise.all([
2627
+ loadSettings(),
2628
+ loadDashboardStatus(),
2629
+ loadAutoUpdateStatus(),
2630
+ loadCallbookDevices(),
2631
+ loadContacts(),
2632
+ loadCalls(),
2633
+ loadInvites(),
2634
+ loadLogStats(),
2635
+ loadLogs()
2636
+ ]);
2637
+ showNotice('Dashboard loaded');
2638
+ connectRealtimeEvents();
2639
+ startPolling();
2640
+
2641
+ setInterval(() => {
2642
+ loadAutoUpdateStatus().catch(() => {});
2643
+ }, 10000);
2644
+ } catch (err) {
2645
+ showNotice(err.message);
2646
+ }
2647
+ }
2648
+
2649
+ bootstrap();
2650
+
2651
+ // A2A-100: Native app update events (only when running in Tauri)
2652
+ if (window.__TAURI__) {
2653
+ const { listen } = window.__TAURI__.event;
2654
+ const { invoke } = window.__TAURI__.core;
2655
+
2656
+ listen('update-status', (event) => {
2657
+ const status = event.payload;
2658
+ const banner = document.getElementById('update-banner');
2659
+ const text = document.getElementById('update-banner-text');
2660
+ if (status.available && status.version) {
2661
+ text.textContent = `Version ${status.version} is available.`;
2662
+ banner.style.display = 'flex';
2663
+ }
2664
+ });
2665
+
2666
+ document.getElementById('update-banner-install')?.addEventListener('click', async () => {
2667
+ const btn = document.getElementById('update-banner-install');
2668
+ btn.loading = true;
2669
+ btn.disabled = true;
2670
+ document.getElementById('update-banner-text').textContent = 'Downloading update...';
2671
+ try {
2672
+ await invoke('install_update');
2673
+ } catch (err) {
2674
+ document.getElementById('update-banner-text').textContent = 'Update failed: ' + (err || 'unknown error');
2675
+ btn.loading = false;
2676
+ btn.disabled = false;
2677
+ }
2678
+ });
2679
+
2680
+ document.getElementById('update-banner-dismiss')?.addEventListener('click', () => {
2681
+ document.getElementById('update-banner').style.display = 'none';
2682
+ });
2683
+ }