@dollhousemcp/mcp-server 2.0.0-rc.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/CHANGELOG.md +54 -17
  2. package/README.github.md +2 -2
  3. package/README.md +2 -2
  4. package/README.md.backup +284 -224
  5. package/README.npm.md +2 -2
  6. package/dist/cache/LRUCache.d.ts +3 -0
  7. package/dist/cache/LRUCache.d.ts.map +1 -1
  8. package/dist/cache/LRUCache.js +36 -26
  9. package/dist/config/env.d.ts +14 -4
  10. package/dist/config/env.d.ts.map +1 -1
  11. package/dist/config/env.js +20 -6
  12. package/dist/di/Container.d.ts +21 -0
  13. package/dist/di/Container.d.ts.map +1 -1
  14. package/dist/di/Container.js +250 -53
  15. package/dist/elements/BaseElement.d.ts.map +1 -1
  16. package/dist/elements/BaseElement.js +5 -10
  17. package/dist/elements/base/BaseElementManager.d.ts +22 -0
  18. package/dist/elements/base/BaseElementManager.d.ts.map +1 -1
  19. package/dist/elements/base/BaseElementManager.js +47 -7
  20. package/dist/elements/memories/Memory.d.ts +1 -0
  21. package/dist/elements/memories/Memory.d.ts.map +1 -1
  22. package/dist/elements/memories/Memory.js +12 -8
  23. package/dist/elements/memories/MemoryManager.d.ts.map +1 -1
  24. package/dist/elements/memories/MemoryManager.js +23 -42
  25. package/dist/elements/memories/MemorySearchIndex.js +2 -2
  26. package/dist/generated/version.d.ts +2 -2
  27. package/dist/generated/version.d.ts.map +1 -1
  28. package/dist/generated/version.js +3 -3
  29. package/dist/handlers/EnhancedIndexHandler.js +6 -6
  30. package/dist/handlers/element-crud/editElement.d.ts.map +1 -1
  31. package/dist/handlers/element-crud/editElement.js +39 -18
  32. package/dist/handlers/element-crud/helpers.d.ts.map +1 -1
  33. package/dist/handlers/element-crud/helpers.js +30 -3
  34. package/dist/handlers/element-crud/listElements.d.ts +2 -0
  35. package/dist/handlers/element-crud/listElements.d.ts.map +1 -1
  36. package/dist/handlers/element-crud/listElements.js +3 -1
  37. package/dist/handlers/mcp-aql/Gatekeeper.d.ts.map +1 -1
  38. package/dist/handlers/mcp-aql/Gatekeeper.js +23 -17
  39. package/dist/handlers/mcp-aql/GatekeeperTypes.d.ts +4 -0
  40. package/dist/handlers/mcp-aql/GatekeeperTypes.d.ts.map +1 -1
  41. package/dist/handlers/mcp-aql/GatekeeperTypes.js +1 -1
  42. package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts +33 -0
  43. package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts.map +1 -1
  44. package/dist/handlers/mcp-aql/MCPAQLHandler.js +232 -23
  45. package/dist/handlers/mcp-aql/OperationRouter.d.ts.map +1 -1
  46. package/dist/handlers/mcp-aql/OperationRouter.js +13 -1
  47. package/dist/handlers/mcp-aql/OperationSchema.d.ts +7 -0
  48. package/dist/handlers/mcp-aql/OperationSchema.d.ts.map +1 -1
  49. package/dist/handlers/mcp-aql/OperationSchema.js +52 -1
  50. package/dist/handlers/mcp-aql/UnifiedEndpoint.d.ts.map +1 -1
  51. package/dist/handlers/mcp-aql/UnifiedEndpoint.js +4 -3
  52. package/dist/handlers/mcp-aql/evaluatePermission.d.ts +53 -0
  53. package/dist/handlers/mcp-aql/evaluatePermission.d.ts.map +1 -0
  54. package/dist/handlers/mcp-aql/evaluatePermission.js +132 -0
  55. package/dist/handlers/mcp-aql/policies/ElementPolicies.d.ts +17 -0
  56. package/dist/handlers/mcp-aql/policies/ElementPolicies.d.ts.map +1 -1
  57. package/dist/handlers/mcp-aql/policies/ElementPolicies.js +91 -1
  58. package/dist/handlers/mcp-aql/policies/ToolClassification.d.ts +5 -3
  59. package/dist/handlers/mcp-aql/policies/ToolClassification.d.ts.map +1 -1
  60. package/dist/handlers/mcp-aql/policies/ToolClassification.js +56 -2
  61. package/dist/handlers/mcp-aql/types.d.ts +22 -0
  62. package/dist/handlers/mcp-aql/types.d.ts.map +1 -1
  63. package/dist/handlers/mcp-aql/types.js +47 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +3 -3
  66. package/dist/logging/LogHooks.js +11 -11
  67. package/dist/logging/LogManager.d.ts +0 -2
  68. package/dist/logging/LogManager.d.ts.map +1 -1
  69. package/dist/logging/LogManager.js +1 -3
  70. package/dist/logging/sinks/MemoryLogSink.d.ts +2 -0
  71. package/dist/logging/sinks/MemoryLogSink.d.ts.map +1 -1
  72. package/dist/logging/sinks/MemoryLogSink.js +12 -3
  73. package/dist/logging/types.d.ts +0 -2
  74. package/dist/logging/types.d.ts.map +1 -1
  75. package/dist/logging/types.js +1 -1
  76. package/dist/metrics/GatekeeperMetricsTracker.d.ts +32 -0
  77. package/dist/metrics/GatekeeperMetricsTracker.d.ts.map +1 -0
  78. package/dist/metrics/GatekeeperMetricsTracker.js +42 -0
  79. package/dist/metrics/MetricsManager.d.ts +47 -0
  80. package/dist/metrics/MetricsManager.d.ts.map +1 -0
  81. package/dist/metrics/MetricsManager.js +232 -0
  82. package/dist/metrics/OperationMetricsTracker.d.ts +32 -0
  83. package/dist/metrics/OperationMetricsTracker.d.ts.map +1 -0
  84. package/dist/metrics/OperationMetricsTracker.js +53 -0
  85. package/dist/metrics/collectors/DefaultElementProviderCollector.d.ts +27 -0
  86. package/dist/metrics/collectors/DefaultElementProviderCollector.d.ts.map +1 -0
  87. package/dist/metrics/collectors/DefaultElementProviderCollector.js +69 -0
  88. package/dist/metrics/collectors/FileLockManagerCollector.d.ts +16 -0
  89. package/dist/metrics/collectors/FileLockManagerCollector.d.ts.map +1 -0
  90. package/dist/metrics/collectors/FileLockManagerCollector.js +51 -0
  91. package/dist/metrics/collectors/GatekeeperMetricsCollector.d.ts +16 -0
  92. package/dist/metrics/collectors/GatekeeperMetricsCollector.d.ts.map +1 -0
  93. package/dist/metrics/collectors/GatekeeperMetricsCollector.js +76 -0
  94. package/dist/metrics/collectors/LRUCacheCollector.d.ts +18 -0
  95. package/dist/metrics/collectors/LRUCacheCollector.d.ts.map +1 -0
  96. package/dist/metrics/collectors/LRUCacheCollector.js +95 -0
  97. package/dist/metrics/collectors/OperationMetricsCollector.d.ts +16 -0
  98. package/dist/metrics/collectors/OperationMetricsCollector.d.ts.map +1 -0
  99. package/dist/metrics/collectors/OperationMetricsCollector.js +80 -0
  100. package/dist/metrics/collectors/OperationalTelemetryCollector.d.ts +17 -0
  101. package/dist/metrics/collectors/OperationalTelemetryCollector.d.ts.map +1 -0
  102. package/dist/metrics/collectors/OperationalTelemetryCollector.js +26 -0
  103. package/dist/metrics/collectors/PerformanceMonitorCollector.d.ts +14 -0
  104. package/dist/metrics/collectors/PerformanceMonitorCollector.d.ts.map +1 -0
  105. package/dist/metrics/collectors/PerformanceMonitorCollector.js +141 -0
  106. package/dist/metrics/collectors/SecurityMonitorCollector.d.ts +21 -0
  107. package/dist/metrics/collectors/SecurityMonitorCollector.d.ts.map +1 -0
  108. package/dist/metrics/collectors/SecurityMonitorCollector.js +56 -0
  109. package/dist/metrics/collectors/SecurityTelemetryCollector.d.ts +15 -0
  110. package/dist/metrics/collectors/SecurityTelemetryCollector.d.ts.map +1 -0
  111. package/dist/metrics/collectors/SecurityTelemetryCollector.js +112 -0
  112. package/dist/metrics/collectors/TriggerMetricsTrackerCollector.d.ts +16 -0
  113. package/dist/metrics/collectors/TriggerMetricsTrackerCollector.d.ts.map +1 -0
  114. package/dist/metrics/collectors/TriggerMetricsTrackerCollector.js +26 -0
  115. package/dist/metrics/collectors/index.d.ts +11 -0
  116. package/dist/metrics/collectors/index.d.ts.map +1 -0
  117. package/dist/metrics/collectors/index.js +11 -0
  118. package/dist/metrics/sinks/MemoryMetricsSink.d.ts +22 -0
  119. package/dist/metrics/sinks/MemoryMetricsSink.d.ts.map +1 -0
  120. package/dist/metrics/sinks/MemoryMetricsSink.js +121 -0
  121. package/dist/metrics/types.d.ts +98 -0
  122. package/dist/metrics/types.d.ts.map +1 -0
  123. package/dist/metrics/types.js +24 -0
  124. package/dist/portfolio/DefaultElementProvider.d.ts.map +1 -1
  125. package/dist/portfolio/DefaultElementProvider.js +1 -7
  126. package/dist/portfolio/EnhancedIndexManager.d.ts.map +1 -1
  127. package/dist/portfolio/EnhancedIndexManager.js +18 -18
  128. package/dist/portfolio/NLPScoringManager.d.ts.map +1 -1
  129. package/dist/portfolio/NLPScoringManager.js +5 -9
  130. package/dist/portfolio/PortfolioIndexManager.js +2 -2
  131. package/dist/portfolio/RelationshipManager.js +2 -2
  132. package/dist/portfolio/VerbTriggerManager.d.ts.map +1 -1
  133. package/dist/portfolio/VerbTriggerManager.js +5 -19
  134. package/dist/portfolio/config/IndexConfig.d.ts.map +1 -1
  135. package/dist/portfolio/config/IndexConfig.js +1 -12
  136. package/dist/portfolio/enhanced-index/ElementDefinitionBuilder.d.ts.map +1 -1
  137. package/dist/portfolio/enhanced-index/ElementDefinitionBuilder.js +3 -15
  138. package/dist/portfolio/enhanced-index/SemanticRelationshipService.d.ts.map +1 -1
  139. package/dist/portfolio/enhanced-index/SemanticRelationshipService.js +2 -16
  140. package/dist/portfolio/types/RelationshipTypes.d.ts.map +1 -1
  141. package/dist/portfolio/types/RelationshipTypes.js +3 -17
  142. package/dist/security/audit/config/suppressions.d.ts.map +1 -1
  143. package/dist/security/audit/config/suppressions.js +36 -8
  144. package/dist/security/constants.d.ts.map +1 -1
  145. package/dist/security/constants.js +10 -6
  146. package/dist/security/fileLockManager.d.ts.map +1 -1
  147. package/dist/security/fileLockManager.js +8 -6
  148. package/dist/security/secureYamlParser.d.ts.map +1 -1
  149. package/dist/security/secureYamlParser.js +1 -13
  150. package/dist/security/securityMonitor.d.ts +2 -1
  151. package/dist/security/securityMonitor.d.ts.map +1 -1
  152. package/dist/security/securityMonitor.js +14 -3
  153. package/dist/security/telemetry/SecurityTelemetry.d.ts +16 -0
  154. package/dist/security/telemetry/SecurityTelemetry.d.ts.map +1 -1
  155. package/dist/security/telemetry/SecurityTelemetry.js +30 -2
  156. package/dist/security/tokenManager.d.ts +3 -0
  157. package/dist/security/tokenManager.d.ts.map +1 -1
  158. package/dist/security/tokenManager.js +13 -5
  159. package/dist/security/validation/BackgroundValidator.d.ts.map +1 -1
  160. package/dist/security/validation/BackgroundValidator.js +7 -7
  161. package/dist/server/startup.d.ts.map +1 -1
  162. package/dist/server/startup.js +8 -24
  163. package/dist/server/tools/MCPAQLTools.js +8 -1
  164. package/dist/services/ActivationStore.d.ts.map +1 -1
  165. package/dist/services/ActivationStore.js +9 -3
  166. package/dist/services/FileWatchService.d.ts +1 -0
  167. package/dist/services/FileWatchService.d.ts.map +1 -1
  168. package/dist/services/FileWatchService.js +83 -48
  169. package/dist/services/MetadataService.d.ts.map +1 -1
  170. package/dist/services/MetadataService.js +7 -2
  171. package/dist/services/PolicyExportService.d.ts.map +1 -1
  172. package/dist/services/PolicyExportService.js +8 -1
  173. package/dist/services/query/ElementQueryService.d.ts.map +1 -1
  174. package/dist/services/query/ElementQueryService.js +1 -41
  175. package/dist/services/query/PaginationService.d.ts.map +1 -1
  176. package/dist/services/query/PaginationService.js +1 -14
  177. package/dist/services/query/SortService.d.ts.map +1 -1
  178. package/dist/services/query/SortService.js +1 -6
  179. package/dist/services/validation/ValidationService.d.ts.map +1 -1
  180. package/dist/services/validation/ValidationService.js +3 -8
  181. package/dist/storage/ElementStorageLayer.d.ts.map +1 -1
  182. package/dist/storage/ElementStorageLayer.js +5 -2
  183. package/dist/storage/MemoryStorageLayer.d.ts.map +1 -1
  184. package/dist/storage/MemoryStorageLayer.js +5 -2
  185. package/dist/telemetry/OperationalTelemetry.js +2 -2
  186. package/dist/utils/EventDeduplicator.d.ts +44 -0
  187. package/dist/utils/EventDeduplicator.d.ts.map +1 -0
  188. package/dist/utils/EventDeduplicator.js +93 -0
  189. package/dist/utils/FileLock.d.ts.map +1 -1
  190. package/dist/utils/FileLock.js +1 -9
  191. package/dist/utils/PerformanceMonitor.d.ts.map +1 -1
  192. package/dist/utils/PerformanceMonitor.js +5 -5
  193. package/dist/utils/SlidingWindowRateLimiter.d.ts +13 -0
  194. package/dist/utils/SlidingWindowRateLimiter.d.ts.map +1 -0
  195. package/dist/utils/SlidingWindowRateLimiter.js +23 -0
  196. package/dist/web/console/IngestRoutes.d.ts +84 -0
  197. package/dist/web/console/IngestRoutes.d.ts.map +1 -0
  198. package/dist/web/console/IngestRoutes.js +252 -0
  199. package/dist/web/console/LeaderElection.d.ts +89 -0
  200. package/dist/web/console/LeaderElection.d.ts.map +1 -0
  201. package/dist/web/console/LeaderElection.js +205 -0
  202. package/dist/web/console/LeaderForwardingSink.d.ts +61 -0
  203. package/dist/web/console/LeaderForwardingSink.d.ts.map +1 -0
  204. package/dist/web/console/LeaderForwardingSink.js +197 -0
  205. package/dist/web/console/SessionNames.d.ts +46 -0
  206. package/dist/web/console/SessionNames.d.ts.map +1 -0
  207. package/dist/web/console/SessionNames.js +257 -0
  208. package/dist/web/console/UnifiedConsole.d.ts +64 -0
  209. package/dist/web/console/UnifiedConsole.d.ts.map +1 -0
  210. package/dist/web/console/UnifiedConsole.js +119 -0
  211. package/dist/web/contentPipeline.d.ts +58 -0
  212. package/dist/web/contentPipeline.d.ts.map +1 -0
  213. package/dist/web/contentPipeline.js +112 -0
  214. package/dist/web/portDiscovery.d.ts +58 -0
  215. package/dist/web/portDiscovery.d.ts.map +1 -0
  216. package/dist/web/portDiscovery.js +143 -0
  217. package/dist/web/public/app.js +148 -60
  218. package/dist/web/public/logs.js +638 -0
  219. package/dist/web/public/metrics.js +682 -0
  220. package/dist/web/public/permissions.js +394 -0
  221. package/dist/web/public/sessions.js +369 -0
  222. package/dist/web/routes/healthRoutes.d.ts +16 -0
  223. package/dist/web/routes/healthRoutes.d.ts.map +1 -0
  224. package/dist/web/routes/healthRoutes.js +29 -0
  225. package/dist/web/routes/logRoutes.d.ts +18 -0
  226. package/dist/web/routes/logRoutes.d.ts.map +1 -0
  227. package/dist/web/routes/logRoutes.js +126 -0
  228. package/dist/web/routes/metricsRoutes.d.ts +17 -0
  229. package/dist/web/routes/metricsRoutes.d.ts.map +1 -0
  230. package/dist/web/routes/metricsRoutes.js +90 -0
  231. package/dist/web/routes/permissionRoutes.d.ts +16 -0
  232. package/dist/web/routes/permissionRoutes.d.ts.map +1 -0
  233. package/dist/web/routes/permissionRoutes.js +133 -0
  234. package/dist/web/routes.d.ts.map +1 -1
  235. package/dist/web/routes.js +309 -339
  236. package/dist/web/server.d.ts +21 -1
  237. package/dist/web/server.d.ts.map +1 -1
  238. package/dist/web/server.js +42 -4
  239. package/dist/web/sinks/WebSSELogSink.d.ts +15 -0
  240. package/dist/web/sinks/WebSSELogSink.d.ts.map +1 -0
  241. package/dist/web/sinks/WebSSELogSink.js +22 -0
  242. package/dist/web/sinks/WebSSEMetricsSink.d.ts +16 -0
  243. package/dist/web/sinks/WebSSEMetricsSink.d.ts.map +1 -0
  244. package/dist/web/sinks/WebSSEMetricsSink.js +23 -0
  245. package/package.json +2 -2
  246. package/server.json +2 -2
  247. package/dist/constants/version.d.ts +0 -3
  248. package/dist/constants/version.d.ts.map +0 -1
  249. package/dist/constants/version.js +0 -4
  250. package/dist/logging/sinks/SSELogSink.d.ts +0 -35
  251. package/dist/logging/sinks/SSELogSink.d.ts.map +0 -1
  252. package/dist/logging/sinks/SSELogSink.js +0 -181
  253. package/dist/logging/viewer/viewerHtml.d.ts +0 -8
  254. package/dist/logging/viewer/viewerHtml.d.ts.map +0 -1
  255. package/dist/logging/viewer/viewerHtml.js +0 -204
  256. package/dist/web/public/public/app.js +0 -1878
@@ -0,0 +1,394 @@
1
+ /**
2
+ * permissions.js — Live Permission Dashboard for DollhouseMCP (auto-dollhouse#5)
3
+ *
4
+ * Shows active gatekeeper policies, live permission decision feed, and system status.
5
+ * Polls /api/permissions/status for policy state and recent decisions.
6
+ * Follows Todd's metrics.js patterns (polling, card layout, lazy init).
7
+ */
8
+
9
+ (function () {
10
+ 'use strict';
11
+
12
+ // ── State ──────────────────────────────────────────────────
13
+
14
+ let pollTimer = null;
15
+ const POLL_INTERVAL_MS = 3000;
16
+ let initialized = false;
17
+ let lastDecisionId = null;
18
+
19
+ // ── Public API ─────────────────────────────────────────────
20
+
21
+ window.DollhouseConsole = window.DollhouseConsole || {};
22
+ window.DollhouseConsole.permissions = {
23
+ init: initPermissions,
24
+ destroy: destroyPermissions,
25
+ refresh: function () { poll(); },
26
+ };
27
+
28
+ // Hook into tab switching — Todd's app.js lazyInitTab only knows logs/metrics,
29
+ // so we self-register by listening for tab clicks on 'permissions'.
30
+ document.addEventListener('DOMContentLoaded', function () {
31
+ const tabs = document.getElementById('console-tabs');
32
+ if (tabs) {
33
+ tabs.addEventListener('click', function (e) {
34
+ const btn = e.target.closest('.console-tab');
35
+ if (btn && btn.dataset.tab === 'permissions') {
36
+ var dc = window.DollhouseConsole;
37
+ if (dc && dc.permissions) {
38
+ if (!initialized) dc.permissions.init();
39
+ else if (dc.permissions.refresh) dc.permissions.refresh();
40
+ }
41
+ }
42
+ });
43
+ }
44
+ });
45
+
46
+ // ── Initialization ─────────────────────────────────────────
47
+
48
+ function initPermissions() {
49
+ if (initialized) return;
50
+ initialized = true;
51
+
52
+ const root = document.getElementById('permissions-dashboard-root');
53
+ if (!root) return;
54
+
55
+ root.innerHTML = buildDashboardHTML();
56
+ attachCardToggles();
57
+ poll(); // immediate first fetch
58
+ pollTimer = setInterval(poll, POLL_INTERVAL_MS);
59
+ }
60
+
61
+ function destroyPermissions() {
62
+ if (pollTimer) {
63
+ clearInterval(pollTimer);
64
+ pollTimer = null;
65
+ }
66
+ initialized = false;
67
+ }
68
+
69
+ // ── Polling ────────────────────────────────────────────────
70
+
71
+ async function poll() {
72
+ try {
73
+ const res = await fetch('/api/permissions/status');
74
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
75
+ const data = await res.json();
76
+ render(data);
77
+ } catch (err) {
78
+ renderError(err.message);
79
+ }
80
+ }
81
+
82
+ // ── Rendering ──────────────────────────────────────────────
83
+
84
+ function render(data) {
85
+ renderStatusBar(data);
86
+ renderSummaryStats(data);
87
+ renderPolicySources(data);
88
+ renderDenyPatterns(data);
89
+ renderAllowPatterns(data);
90
+ renderConfirmPatterns(data);
91
+ renderLiveFeed(data);
92
+ }
93
+
94
+ function renderError(message) {
95
+ console.warn('[Permissions Dashboard] API error:', message);
96
+ const dot = document.getElementById('perm-dot-server');
97
+ if (dot) {
98
+ dot.dataset.status = 'error';
99
+ dot.parentElement.querySelector('.perm-status-label').textContent = 'Server unreachable';
100
+ }
101
+ }
102
+
103
+ function renderStatusBar(data) {
104
+ const serverDot = document.getElementById('perm-dot-server');
105
+ const ensembleDot = document.getElementById('perm-dot-ensemble');
106
+ const hookDot = document.getElementById('perm-dot-hook');
107
+ const updated = document.getElementById('perm-last-updated');
108
+
109
+ if (serverDot) {
110
+ serverDot.dataset.status = 'active';
111
+ serverDot.parentElement.querySelector('.perm-status-label').textContent = 'Server';
112
+ }
113
+ if (ensembleDot) {
114
+ const hasElements = data.activeElementCount > 0;
115
+ ensembleDot.dataset.status = hasElements ? 'active' : 'inactive';
116
+ ensembleDot.parentElement.querySelector('.perm-status-label').textContent =
117
+ hasElements ? `${data.activeElementCount} elements` : 'No ensemble';
118
+ }
119
+ if (hookDot) {
120
+ const hasPatterns = (data.denyPatterns?.length || 0) + (data.allowPatterns?.length || 0) > 0;
121
+ hookDot.dataset.status = hasPatterns ? 'active' : 'inactive';
122
+ hookDot.parentElement.querySelector('.perm-status-label').textContent =
123
+ hasPatterns ? 'Policies active' : 'No policies';
124
+ }
125
+ if (updated) {
126
+ updated.textContent = new Date().toLocaleTimeString();
127
+ }
128
+ }
129
+
130
+ function renderSummaryStats(data) {
131
+ setText('perm-stat-deny-count', data.denyPatterns?.length || 0);
132
+ setText('perm-stat-allow-count', data.allowPatterns?.length || 0);
133
+ setText('perm-stat-confirm-count', data.confirmPatterns?.length || 0);
134
+ setText('perm-stat-decisions', data.recentDecisions?.length || 0);
135
+
136
+ // Decision breakdown
137
+ const decisions = data.recentDecisions || [];
138
+ const allowed = decisions.filter(d => d.decision === 'allow').length;
139
+ const denied = decisions.filter(d => d.decision === 'deny').length;
140
+ const asked = decisions.filter(d => d.decision === 'ask').length;
141
+ setText('perm-stat-allowed', allowed);
142
+ setText('perm-stat-denied', denied);
143
+ setText('perm-stat-asked', asked);
144
+ }
145
+
146
+ function renderPolicySources(data) {
147
+ const list = document.getElementById('perm-source-list');
148
+ if (!list) return;
149
+
150
+ const elements = data.elements || [];
151
+ if (elements.length === 0) {
152
+ list.innerHTML = '<li class="perm-pattern-empty">No active elements with policies</li>';
153
+ return;
154
+ }
155
+
156
+ list.innerHTML = elements.map(el => `
157
+ <li class="perm-source-item">
158
+ <span class="perm-source-type">${esc(el.type)}</span>
159
+ <span class="perm-source-name">${esc(el.element_name)}</span>
160
+ ${el.description ? `<span style="color:var(--ink-400);font-size:0.75rem;margin-left:auto">${esc(el.description)}</span>` : ''}
161
+ </li>
162
+ `).join('');
163
+ }
164
+
165
+ function renderDenyPatterns(data) {
166
+ renderPatternList('perm-deny-list', data.denyPatterns || [], 'deny');
167
+ }
168
+
169
+ function renderAllowPatterns(data) {
170
+ renderPatternList('perm-allow-list', data.allowPatterns || [], 'allow');
171
+ }
172
+
173
+ function renderConfirmPatterns(data) {
174
+ renderPatternList('perm-confirm-list', data.confirmPatterns || [], 'confirm');
175
+ }
176
+
177
+ function renderPatternList(elementId, patterns, type) {
178
+ const list = document.getElementById(elementId);
179
+ if (!list) return;
180
+
181
+ if (patterns.length === 0) {
182
+ list.innerHTML = `<li class="perm-pattern-empty">No ${type} patterns active</li>`;
183
+ return;
184
+ }
185
+
186
+ list.innerHTML = patterns.map(p => `
187
+ <li class="perm-pattern-item">
188
+ <span class="perm-pattern-badge perm-pattern-badge--${type}">${type}</span>
189
+ <span class="perm-pattern-text">${esc(p)}</span>
190
+ </li>
191
+ `).join('');
192
+ }
193
+
194
+ function renderLiveFeed(data) {
195
+ const feed = document.getElementById('perm-feed');
196
+ if (!feed) return;
197
+
198
+ const decisions = data.recentDecisions || [];
199
+ if (decisions.length === 0) {
200
+ feed.innerHTML = '<div class="perm-feed-empty">No permission decisions yet. Waiting for tool calls...</div>';
201
+ return;
202
+ }
203
+
204
+ // Check if new decisions arrived
205
+ const latestId = decisions[0]?.id;
206
+ if (latestId === lastDecisionId) return; // no change
207
+ lastDecisionId = latestId;
208
+
209
+ feed.innerHTML = decisions.map(d => {
210
+ const time = new Date(d.timestamp).toLocaleTimeString();
211
+ const toolDisplay = d.tool_name === 'Bash'
212
+ ? `Bash: ${esc(truncate(d.command || '', 60))}`
213
+ : esc(d.tool_name);
214
+
215
+ return `
216
+ <div class="perm-feed-row">
217
+ <span class="perm-feed-time">${time}</span>
218
+ <span class="perm-feed-decision perm-feed-decision--${d.decision}">${d.decision.toUpperCase()}</span>
219
+ <span class="perm-feed-tool" title="${esc(d.command || d.tool_name)}">${toolDisplay}</span>
220
+ <span class="perm-feed-reason" title="${esc(d.reason || '')}">${esc(d.reason || '')}</span>
221
+ </div>
222
+ `;
223
+ }).join('');
224
+ }
225
+
226
+ // ── Dashboard HTML ─────────────────────────────────────────
227
+
228
+ function buildDashboardHTML() {
229
+ return `
230
+ <div class="perm-status-bar">
231
+ <div class="perm-status-indicator">
232
+ <span class="perm-status-dot" id="perm-dot-server" data-status="inactive"></span>
233
+ <span class="perm-status-label">Connecting...</span>
234
+ </div>
235
+ <div class="perm-status-indicator">
236
+ <span class="perm-status-dot" id="perm-dot-ensemble" data-status="inactive"></span>
237
+ <span class="perm-status-label">Ensemble</span>
238
+ </div>
239
+ <div class="perm-status-indicator">
240
+ <span class="perm-status-dot" id="perm-dot-hook" data-status="inactive"></span>
241
+ <span class="perm-status-label">Policies</span>
242
+ </div>
243
+ <span class="perm-status-spacer"></span>
244
+ <span class="perm-status-updated">Updated: <span id="perm-last-updated">--:--:--</span></span>
245
+ </div>
246
+
247
+ <div class="perm-dashboard">
248
+
249
+ <!-- Summary Stats -->
250
+ <div class="perm-card perm-card--full" data-collapsed="false">
251
+ <div class="perm-card-header" role="button" tabindex="0" aria-expanded="true">
252
+ <h3 class="perm-card-title">Autonomy Overview</h3>
253
+ <span class="perm-card-toggle" aria-hidden="true">&#9662;</span>
254
+ </div>
255
+ <div class="perm-card-body">
256
+ <div class="perm-stat-grid">
257
+ <div class="perm-stat">
258
+ <div class="perm-stat-value perm-stat-value--deny" id="perm-stat-deny-count">0</div>
259
+ <div class="perm-stat-label">Deny Patterns</div>
260
+ </div>
261
+ <div class="perm-stat">
262
+ <div class="perm-stat-value perm-stat-value--allow" id="perm-stat-allow-count">0</div>
263
+ <div class="perm-stat-label">Allow Patterns</div>
264
+ </div>
265
+ <div class="perm-stat">
266
+ <div class="perm-stat-value perm-stat-value--ask" id="perm-stat-confirm-count">0</div>
267
+ <div class="perm-stat-label">Confirm Patterns</div>
268
+ </div>
269
+ <div class="perm-stat">
270
+ <div class="perm-stat-value" id="perm-stat-decisions">0</div>
271
+ <div class="perm-stat-label">Recent Decisions</div>
272
+ </div>
273
+ <div class="perm-stat">
274
+ <div class="perm-stat-value perm-stat-value--allow" id="perm-stat-allowed">0</div>
275
+ <div class="perm-stat-label">Allowed</div>
276
+ </div>
277
+ <div class="perm-stat">
278
+ <div class="perm-stat-value perm-stat-value--deny" id="perm-stat-denied">0</div>
279
+ <div class="perm-stat-label">Denied</div>
280
+ </div>
281
+ <div class="perm-stat">
282
+ <div class="perm-stat-value perm-stat-value--ask" id="perm-stat-asked">0</div>
283
+ <div class="perm-stat-label">Asked</div>
284
+ </div>
285
+ </div>
286
+ </div>
287
+ </div>
288
+
289
+ <!-- Policy Sources -->
290
+ <div class="perm-card" data-collapsed="false">
291
+ <div class="perm-card-header" role="button" tabindex="0" aria-expanded="true">
292
+ <h3 class="perm-card-title">Policy Sources</h3>
293
+ <span class="perm-card-toggle" aria-hidden="true">&#9662;</span>
294
+ </div>
295
+ <div class="perm-card-body">
296
+ <ul class="perm-source-list" id="perm-source-list">
297
+ <li class="perm-pattern-empty">Loading...</li>
298
+ </ul>
299
+ </div>
300
+ </div>
301
+
302
+ <!-- Deny Patterns -->
303
+ <div class="perm-card" data-collapsed="false">
304
+ <div class="perm-card-header" role="button" tabindex="0" aria-expanded="true">
305
+ <h3 class="perm-card-title">Deny Patterns</h3>
306
+ <span class="perm-card-toggle" aria-hidden="true">&#9662;</span>
307
+ </div>
308
+ <div class="perm-card-body">
309
+ <ul class="perm-pattern-list" id="perm-deny-list">
310
+ <li class="perm-pattern-empty">Loading...</li>
311
+ </ul>
312
+ </div>
313
+ </div>
314
+
315
+ <!-- Allow Patterns -->
316
+ <div class="perm-card" data-collapsed="true">
317
+ <div class="perm-card-header" role="button" tabindex="0" aria-expanded="true">
318
+ <h3 class="perm-card-title">Allow Patterns</h3>
319
+ <span class="perm-card-toggle" aria-hidden="true">&#9662;</span>
320
+ </div>
321
+ <div class="perm-card-body">
322
+ <ul class="perm-pattern-list" id="perm-allow-list">
323
+ <li class="perm-pattern-empty">Loading...</li>
324
+ </ul>
325
+ </div>
326
+ </div>
327
+
328
+ <!-- Confirm Patterns -->
329
+ <div class="perm-card" data-collapsed="false">
330
+ <div class="perm-card-header" role="button" tabindex="0" aria-expanded="true">
331
+ <h3 class="perm-card-title">Confirm Patterns (Requires Approval)</h3>
332
+ <span class="perm-card-toggle" aria-hidden="true">&#9662;</span>
333
+ </div>
334
+ <div class="perm-card-body">
335
+ <ul class="perm-pattern-list" id="perm-confirm-list">
336
+ <li class="perm-pattern-empty">Loading...</li>
337
+ </ul>
338
+ </div>
339
+ </div>
340
+
341
+ <!-- Live Decision Feed -->
342
+ <div class="perm-card perm-card--full" data-collapsed="false">
343
+ <div class="perm-card-header" role="button" tabindex="0" aria-expanded="true">
344
+ <h3 class="perm-card-title">Live Decision Feed</h3>
345
+ <span class="perm-card-toggle" aria-hidden="true">&#9662;</span>
346
+ </div>
347
+ <div class="perm-card-body">
348
+ <div class="perm-feed" id="perm-feed" role="log" aria-live="polite" aria-label="Permission decisions">
349
+ <div class="perm-feed-empty">No permission decisions yet. Waiting for tool calls...</div>
350
+ </div>
351
+ </div>
352
+ </div>
353
+
354
+ </div>
355
+ `;
356
+ }
357
+
358
+ // ── Helpers ────────────────────────────────────────────────
359
+
360
+ function attachCardToggles() {
361
+ document.querySelectorAll('.perm-card-header').forEach(header => {
362
+ const toggle = () => {
363
+ const card = header.parentElement;
364
+ const collapsed = card.dataset.collapsed === 'true';
365
+ card.dataset.collapsed = collapsed ? 'false' : 'true';
366
+ header.setAttribute('aria-expanded', collapsed ? 'true' : 'false');
367
+ };
368
+ header.addEventListener('click', toggle);
369
+ header.addEventListener('keydown', (e) => {
370
+ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); }
371
+ });
372
+ });
373
+ }
374
+
375
+ function setText(id, value) {
376
+ const el = document.getElementById(id);
377
+ if (el) el.textContent = String(value);
378
+ }
379
+
380
+ // dmcp-sec[DMCP-SEC-004] — Client-side JS: UnicodeValidator unavailable in browser.
381
+ // Using native String.normalize('NFC') which performs the same NFC normalization.
382
+ // All data comes from our own server API, not direct user input.
383
+ function esc(str) {
384
+ const normalized = String(str).normalize('NFC');
385
+ const div = document.createElement('div');
386
+ div.textContent = normalized;
387
+ return div.innerHTML;
388
+ }
389
+
390
+ function truncate(str, len) {
391
+ return str.length > len ? str.slice(0, len) + '...' : str;
392
+ }
393
+
394
+ })();