@miller-tech/uap 1.10.1 → 1.12.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 (149) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/benchmarks/benchmark.d.ts +14 -14
  3. package/dist/bin/cli.js +5 -0
  4. package/dist/bin/cli.js.map +1 -1
  5. package/dist/bin/llama-server-optimize.js +0 -0
  6. package/dist/bin/policy.js +0 -0
  7. package/dist/cli/task.d.ts +1 -1
  8. package/dist/cli/task.d.ts.map +1 -1
  9. package/dist/cli/task.js +65 -0
  10. package/dist/cli/task.js.map +1 -1
  11. package/dist/coordination/service.d.ts +11 -0
  12. package/dist/coordination/service.d.ts.map +1 -1
  13. package/dist/coordination/service.js +34 -3
  14. package/dist/coordination/service.js.map +1 -1
  15. package/dist/dashboard/data-seeder.d.ts +33 -0
  16. package/dist/dashboard/data-seeder.d.ts.map +1 -0
  17. package/dist/dashboard/data-seeder.js +247 -0
  18. package/dist/dashboard/data-seeder.js.map +1 -0
  19. package/dist/dashboard/data-service.d.ts +109 -0
  20. package/dist/dashboard/data-service.d.ts.map +1 -1
  21. package/dist/dashboard/data-service.js +566 -27
  22. package/dist/dashboard/data-service.js.map +1 -1
  23. package/dist/dashboard/index.d.ts +4 -2
  24. package/dist/dashboard/index.d.ts.map +1 -1
  25. package/dist/dashboard/index.js +2 -1
  26. package/dist/dashboard/index.js.map +1 -1
  27. package/dist/dashboard/server.d.ts.map +1 -1
  28. package/dist/dashboard/server.js +17 -0
  29. package/dist/dashboard/server.js.map +1 -1
  30. package/dist/models/profile-loader.d.ts +1 -1
  31. package/dist/models/types.d.ts +45 -45
  32. package/dist/policies/schemas/policy.d.ts +5 -5
  33. package/dist/tasks/types.d.ts +22 -22
  34. package/dist/types/config.d.ts +170 -170
  35. package/dist/types/coordination.d.ts +22 -22
  36. package/dist/uap-droids-strict.d.ts +4 -4
  37. package/package.json +1 -1
  38. package/scripts/maintenance/verify-compliance.sh +12 -12
  39. package/templates/hooks/pre-compact.sh +14 -0
  40. package/templates/hooks/session-start.sh +1 -1
  41. package/dist/bin/tool-calls.d.ts +0 -3
  42. package/dist/bin/tool-calls.d.ts.map +0 -1
  43. package/dist/bin/tool-calls.js +0 -4
  44. package/dist/bin/tool-calls.js.map +0 -1
  45. package/dist/cli/completion-gates.d.ts +0 -51
  46. package/dist/cli/completion-gates.d.ts.map +0 -1
  47. package/dist/cli/completion-gates.js +0 -201
  48. package/dist/cli/completion-gates.js.map +0 -1
  49. package/dist/cli/rtk-validation.d.ts +0 -24
  50. package/dist/cli/rtk-validation.d.ts.map +0 -1
  51. package/dist/cli/rtk-validation.js +0 -138
  52. package/dist/cli/rtk-validation.js.map +0 -1
  53. package/dist/cli/uap.d.ts +0 -10
  54. package/dist/cli/uap.d.ts.map +0 -1
  55. package/dist/cli/uap.js +0 -457
  56. package/dist/cli/uap.js.map +0 -1
  57. package/dist/coordination/droid-validator.d.ts +0 -59
  58. package/dist/coordination/droid-validator.d.ts.map +0 -1
  59. package/dist/coordination/droid-validator.js +0 -142
  60. package/dist/coordination/droid-validator.js.map +0 -1
  61. package/dist/coordination/worktree-enforcer.d.ts +0 -22
  62. package/dist/coordination/worktree-enforcer.d.ts.map +0 -1
  63. package/dist/coordination/worktree-enforcer.js +0 -71
  64. package/dist/coordination/worktree-enforcer.js.map +0 -1
  65. package/dist/generators/template-loader.d.ts +0 -105
  66. package/dist/generators/template-loader.d.ts.map +0 -1
  67. package/dist/generators/template-loader.js +0 -291
  68. package/dist/generators/template-loader.js.map +0 -1
  69. package/dist/memory/active-context.d.ts +0 -65
  70. package/dist/memory/active-context.d.ts.map +0 -1
  71. package/dist/memory/active-context.js +0 -126
  72. package/dist/memory/active-context.js.map +0 -1
  73. package/dist/memory/agent-scoped-memory.d.ts +0 -67
  74. package/dist/memory/agent-scoped-memory.d.ts.map +0 -1
  75. package/dist/memory/agent-scoped-memory.js +0 -126
  76. package/dist/memory/agent-scoped-memory.js.map +0 -1
  77. package/dist/memory/dedup-detector.d.ts +0 -57
  78. package/dist/memory/dedup-detector.d.ts.map +0 -1
  79. package/dist/memory/dedup-detector.js +0 -107
  80. package/dist/memory/dedup-detector.js.map +0 -1
  81. package/dist/memory/dedup-memory.d.ts +0 -89
  82. package/dist/memory/dedup-memory.d.ts.map +0 -1
  83. package/dist/memory/dedup-memory.js +0 -173
  84. package/dist/memory/dedup-memory.js.map +0 -1
  85. package/dist/memory/generic-uap-patterns.d.ts +0 -7
  86. package/dist/memory/generic-uap-patterns.d.ts.map +0 -1
  87. package/dist/memory/generic-uap-patterns.js +0 -43
  88. package/dist/memory/generic-uap-patterns.js.map +0 -1
  89. package/dist/memory/merge-claude-md.d.ts +0 -45
  90. package/dist/memory/merge-claude-md.d.ts.map +0 -1
  91. package/dist/memory/merge-claude-md.js +0 -118
  92. package/dist/memory/merge-claude-md.js.map +0 -1
  93. package/dist/memory/multi-view-memory.d.ts +0 -134
  94. package/dist/memory/multi-view-memory.d.ts.map +0 -1
  95. package/dist/memory/multi-view-memory.js +0 -430
  96. package/dist/memory/multi-view-memory.js.map +0 -1
  97. package/dist/memory/patterns.d.ts +0 -37
  98. package/dist/memory/patterns.d.ts.map +0 -1
  99. package/dist/memory/patterns.js +0 -81
  100. package/dist/memory/patterns.js.map +0 -1
  101. package/dist/memory/semantic-edge-graph.d.ts +0 -86
  102. package/dist/memory/semantic-edge-graph.d.ts.map +0 -1
  103. package/dist/memory/semantic-edge-graph.js +0 -168
  104. package/dist/memory/semantic-edge-graph.js.map +0 -1
  105. package/dist/memory/semantic-retrieval.d.ts +0 -70
  106. package/dist/memory/semantic-retrieval.d.ts.map +0 -1
  107. package/dist/memory/semantic-retrieval.js +0 -112
  108. package/dist/memory/semantic-retrieval.js.map +0 -1
  109. package/dist/memory/short-term/factory.d.ts +0 -26
  110. package/dist/memory/short-term/factory.d.ts.map +0 -1
  111. package/dist/memory/short-term/factory.js +0 -28
  112. package/dist/memory/short-term/factory.js.map +0 -1
  113. package/dist/memory/short-term/indexeddb.d.ts +0 -25
  114. package/dist/memory/short-term/indexeddb.d.ts.map +0 -1
  115. package/dist/memory/short-term/indexeddb.js +0 -64
  116. package/dist/memory/short-term/indexeddb.js.map +0 -1
  117. package/dist/memory/smart-consolidator.d.ts +0 -53
  118. package/dist/memory/smart-consolidator.d.ts.map +0 -1
  119. package/dist/memory/smart-consolidator.js +0 -144
  120. package/dist/memory/smart-consolidator.js.map +0 -1
  121. package/dist/memory/view-memory.d.ts +0 -80
  122. package/dist/memory/view-memory.d.ts.map +0 -1
  123. package/dist/memory/view-memory.js +0 -130
  124. package/dist/memory/view-memory.js.map +0 -1
  125. package/dist/memory/wrapped-memory.d.ts +0 -77
  126. package/dist/memory/wrapped-memory.d.ts.map +0 -1
  127. package/dist/memory/wrapped-memory.js +0 -127
  128. package/dist/memory/wrapped-memory.js.map +0 -1
  129. package/dist/models/api-client.d.ts +0 -46
  130. package/dist/models/api-client.d.ts.map +0 -1
  131. package/dist/models/api-client.js +0 -182
  132. package/dist/models/api-client.js.map +0 -1
  133. package/dist/utils/config-manager.d.ts +0 -30
  134. package/dist/utils/config-manager.d.ts.map +0 -1
  135. package/dist/utils/config-manager.js +0 -41
  136. package/dist/utils/config-manager.js.map +0 -1
  137. package/dist/utils/fetch-with-retry.d.ts +0 -5
  138. package/dist/utils/fetch-with-retry.d.ts.map +0 -1
  139. package/dist/utils/fetch-with-retry.js +0 -61
  140. package/dist/utils/fetch-with-retry.js.map +0 -1
  141. package/dist/utils/file-discovery.d.ts +0 -38
  142. package/dist/utils/file-discovery.d.ts.map +0 -1
  143. package/dist/utils/file-discovery.js +0 -100
  144. package/dist/utils/file-discovery.js.map +0 -1
  145. package/dist/utils/validate-json.d.ts +0 -51
  146. package/dist/utils/validate-json.d.ts.map +0 -1
  147. package/dist/utils/validate-json.js +0 -94
  148. package/dist/utils/validate-json.js.map +0 -1
  149. package/tools/agents/scripts/__pycache__/tool_call_wrapper.cpython-313.pyc +0 -0
@@ -4,12 +4,13 @@
4
4
  * Extracts structured data from all UAP subsystems for consumption
5
5
  * by both the CLI dashboard and the web overlay.
6
6
  */
7
- import { existsSync, readFileSync, statSync } from 'fs';
7
+ import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
8
8
  import { loadUapConfig } from '../utils/config-loader.js';
9
9
  import { join } from 'path';
10
10
  import { execSync } from 'child_process';
11
11
  import Database from 'better-sqlite3';
12
12
  import { globalSessionStats } from '../mcp-router/session-stats.js';
13
+ import { getSessionSnapshot } from '../telemetry/session-telemetry.js';
13
14
  import { getPerformanceMonitor } from '../utils/performance-monitor.js';
14
15
  const SUBPROCESS_CACHE_TTL = 30_000; // 30 seconds
15
16
  let cachedGitData = null;
@@ -17,19 +18,108 @@ let cachedQdrantStatus = null;
17
18
  // ── DB Connection Pool for memory database (prevents opening/closing on every refresh) ──
18
19
  const MEMORY_DB_CACHE_TTL = 5_000; // 5 seconds
19
20
  let cachedMemoryDb = null;
21
+ function getTelemetryDb(cwd) {
22
+ const dbPath = join(cwd, 'agents', 'data', 'memory', 'telemetry.db');
23
+ const db = new Database(dbPath);
24
+ db.exec(`
25
+ CREATE TABLE IF NOT EXISTS time_series (
26
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
27
+ timestamp TEXT NOT NULL,
28
+ data TEXT NOT NULL
29
+ )
30
+ `);
31
+ return db;
32
+ }
33
+ function persistTimeSeriesPoint(cwd, point) {
34
+ try {
35
+ const db = getTelemetryDb(cwd);
36
+ db.prepare('INSERT INTO time_series (timestamp, data) VALUES (?, ?)').run(point.timestamp, JSON.stringify(point));
37
+ // Keep only the last 500 points
38
+ db.prepare('DELETE FROM time_series WHERE id NOT IN (SELECT id FROM time_series ORDER BY id DESC LIMIT 500)').run();
39
+ db.close();
40
+ }
41
+ catch {
42
+ /* ignore */
43
+ }
44
+ }
45
+ function getTimeSeriesFromDb(cwd) {
46
+ try {
47
+ const db = getTelemetryDb(cwd);
48
+ const rows = db
49
+ .prepare('SELECT data FROM time_series ORDER BY id DESC LIMIT 120')
50
+ .all();
51
+ db.close();
52
+ return rows
53
+ .reverse()
54
+ .map((r) => {
55
+ try {
56
+ return JSON.parse(r.data);
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ })
62
+ .filter((p) => p !== null);
63
+ }
64
+ catch {
65
+ return [];
66
+ }
67
+ }
68
+ export function getTimeSeriesHistory(cwd) {
69
+ return getTimeSeriesFromDb(cwd);
70
+ }
71
+ export function pushTimeSeriesPoint(cwd, point) {
72
+ persistTimeSeriesPoint(cwd, point);
73
+ }
20
74
  // ── Data Gathering ──
21
75
  export async function getDashboardData() {
22
76
  const cwd = process.cwd();
77
+ const tasks = getTaskData(cwd);
78
+ const coordination = getCoordData(cwd);
79
+ const memory = getMemoryData(cwd);
80
+ const compliance = getComplianceData(cwd);
81
+ const deployBuckets = getDeployBucketData(cwd);
82
+ // Persist time-series point
83
+ const tsPoint = {
84
+ timestamp: new Date().toISOString(),
85
+ tasks: {
86
+ total: tasks.total,
87
+ done: tasks.done,
88
+ inProgress: tasks.inProgress,
89
+ blocked: tasks.blocked,
90
+ open: tasks.open,
91
+ },
92
+ coordination: {
93
+ activeAgents: coordination.activeAgents,
94
+ totalAgents: coordination.totalAgents,
95
+ completedAgents: coordination.completedAgents,
96
+ patternHits: coordination.patternHits,
97
+ activeWorktrees: coordination.activeWorktrees,
98
+ },
99
+ deployBuckets,
100
+ compression: memory.compression,
101
+ memoryHitsMisses: memory.hitsMisses || { hits: 0, misses: 0, hitRate: 'N/A' },
102
+ compliance: {
103
+ totalChecks: compliance.totalChecks,
104
+ totalBlocks: compliance.totalBlocks,
105
+ blockRate: compliance.blockRate,
106
+ },
107
+ };
108
+ pushTimeSeriesPoint(cwd, tsPoint);
23
109
  return {
24
110
  timestamp: new Date().toISOString(),
25
111
  system: getSystemData(cwd),
26
112
  policies: getPolicyData(cwd),
113
+ policyFiles: getPolicyFiles(cwd),
27
114
  auditTrail: getAuditData(cwd),
28
- memory: getMemoryData(cwd),
115
+ memory,
29
116
  models: getModelData(cwd),
30
- tasks: getTaskData(cwd),
31
- coordination: getCoordData(cwd),
117
+ tasks,
118
+ coordination,
32
119
  performance: getPerformanceData(),
120
+ timeSeries: getTimeSeriesHistory(cwd),
121
+ compliance,
122
+ deployBuckets,
33
123
  };
34
124
  }
35
125
  function getSystemData(cwd) {
@@ -100,6 +190,60 @@ function getPolicyData(cwd) {
100
190
  return [];
101
191
  }
102
192
  }
193
+ /**
194
+ * Read policy .md files from the policies/ directory.
195
+ * Returns metadata about each file (excluding README.md).
196
+ */
197
+ export function getPolicyFiles(cwd) {
198
+ const policiesDir = join(cwd, 'policies');
199
+ if (!existsSync(policiesDir))
200
+ return [];
201
+ try {
202
+ const files = readdirSync(policiesDir).filter((f) => f.endsWith('.md') && f.toLowerCase() !== 'readme.md');
203
+ return files.map((f) => {
204
+ const nameWithoutExt = f.replace(/\.md$/, '');
205
+ // Convert kebab-case to Title Case
206
+ const name = nameWithoutExt
207
+ .split('-')
208
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
209
+ .join(' ');
210
+ // Derive category from filename patterns
211
+ let category = 'general';
212
+ if (nameWithoutExt.includes('iac') || nameWithoutExt.includes('pipeline')) {
213
+ category = 'infrastructure';
214
+ }
215
+ else if (nameWithoutExt.includes('worktree') || nameWithoutExt.includes('file')) {
216
+ category = 'workflow';
217
+ }
218
+ else if (nameWithoutExt.includes('gate') ||
219
+ nameWithoutExt.includes('completion') ||
220
+ nameWithoutExt.includes('mandatory')) {
221
+ category = 'quality';
222
+ }
223
+ else if (nameWithoutExt.includes('semver') || nameWithoutExt.includes('version')) {
224
+ category = 'versioning';
225
+ }
226
+ else if (nameWithoutExt.includes('image') || nameWithoutExt.includes('asset')) {
227
+ category = 'assets';
228
+ }
229
+ else if (nameWithoutExt.includes('kubectl') || nameWithoutExt.includes('backport')) {
230
+ category = 'operations';
231
+ }
232
+ else if (nameWithoutExt.includes('backup')) {
233
+ category = 'safety';
234
+ }
235
+ return {
236
+ filename: f,
237
+ name,
238
+ category,
239
+ path: join(policiesDir, f),
240
+ };
241
+ });
242
+ }
243
+ catch {
244
+ return [];
245
+ }
246
+ }
103
247
  function getAuditData(cwd) {
104
248
  const dbPath = join(cwd, 'agents', 'data', 'memory', 'policies.db');
105
249
  if (!existsSync(dbPath))
@@ -137,6 +281,7 @@ function getMemoryData(cwd) {
137
281
  let l2Entries = 0;
138
282
  let l4Entities = 0;
139
283
  let l4Relationships = 0;
284
+ const recentQueries = [];
140
285
  if (existsSync(memDbPath)) {
141
286
  try {
142
287
  l1SizeKB = Math.round(statSync(memDbPath).size / 1024);
@@ -155,6 +300,23 @@ function getMemoryData(cwd) {
155
300
  .all();
156
301
  if (hasMem.length > 0) {
157
302
  l1Entries = db.prepare('SELECT COUNT(*) as c FROM memories').get().c;
303
+ // Recent queries from memories table (last 10)
304
+ try {
305
+ const memRows = db
306
+ .prepare(`SELECT type, substr(content, 1, 80) as snippet, timestamp
307
+ FROM memories ORDER BY id DESC LIMIT 10`)
308
+ .all();
309
+ for (const row of memRows) {
310
+ recentQueries.push({
311
+ query: row.snippet || '',
312
+ type: row.type || 'memory',
313
+ timestamp: row.timestamp || '',
314
+ });
315
+ }
316
+ }
317
+ catch {
318
+ /* table might not have expected columns */
319
+ }
158
320
  }
159
321
  const hasSess = db
160
322
  .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='session_memories'")
@@ -180,12 +342,72 @@ function getMemoryData(cwd) {
180
342
  /* ignore */
181
343
  }
182
344
  }
183
- // Get compression stats first (needed for both cached and fresh paths)
345
+ // Get compression stats from session stats
184
346
  const stats = globalSessionStats.getSummary();
347
+ // Try real compression ratio from model_analytics.db
348
+ let compressionRaw = stats.totalRawBytes;
349
+ let compressionCtx = stats.totalContextBytes;
350
+ let compressionSavings = stats.savingsPercent;
351
+ const compressionCalls = stats.totalCalls;
352
+ const analyticsDbPath = join(cwd, 'agents', 'data', 'memory', 'model_analytics.db');
353
+ if (existsSync(analyticsDbPath) && compressionRaw === 0) {
354
+ try {
355
+ const aDb = new Database(analyticsDbPath, { readonly: true });
356
+ const hasTable = aDb
357
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='task_outcomes'")
358
+ .all();
359
+ if (hasTable.length > 0) {
360
+ const row = aDb
361
+ .prepare('SELECT SUM(tokensIn) as ti, SUM(tokensOut) as to2 FROM task_outcomes')
362
+ .get();
363
+ if (row && row.ti && row.to2 && row.ti + row.to2 > 0) {
364
+ compressionRaw = row.ti + row.to2;
365
+ compressionCtx = row.to2;
366
+ const ratio = row.to2 / (row.ti + row.to2);
367
+ compressionSavings = ((1 - ratio) * 100).toFixed(1) + '%';
368
+ }
369
+ }
370
+ aDb.close();
371
+ }
372
+ catch {
373
+ /* ignore */
374
+ }
375
+ }
185
376
  // Use TTL cache for Qdrant status (Docker doesn't change faster than 30s)
186
377
  let l3Status = 'Stopped';
187
378
  let l3Uptime = '';
188
379
  const now = Date.now();
380
+ // Memory hits/misses: derive from memory types (observations=hits, thoughts=misses)
381
+ // or from session telemetry if available
382
+ const snapshot = getSessionSnapshot();
383
+ let memHits = snapshot?.memoryHits ?? 0;
384
+ let memMisses = snapshot?.memoryMisses ?? 0;
385
+ if (memHits === 0 && memMisses === 0 && existsSync(memDbPath)) {
386
+ try {
387
+ const mdb = cachedMemoryDb?.db || new Database(memDbPath, { readonly: true });
388
+ const typeCounts = mdb
389
+ .prepare('SELECT type, COUNT(*) as c FROM memories GROUP BY type')
390
+ .all();
391
+ for (const tc of typeCounts) {
392
+ if (tc.type === 'observation' || tc.type === 'action')
393
+ memHits += tc.c;
394
+ else if (tc.type === 'thought')
395
+ memMisses += tc.c;
396
+ }
397
+ // Session memories are all hits (successfully stored context)
398
+ const sesCount = mdb.prepare('SELECT COUNT(*) as c FROM session_memories').get();
399
+ memHits += sesCount.c;
400
+ }
401
+ catch {
402
+ /* ignore */
403
+ }
404
+ }
405
+ const memTotal = memHits + memMisses;
406
+ const hitsMisses = {
407
+ hits: memHits,
408
+ misses: memMisses,
409
+ hitRate: memTotal > 0 ? `${Math.round((memHits / memTotal) * 100)}%` : 'N/A',
410
+ };
189
411
  if (cachedQdrantStatus && cachedQdrantStatus.expiresAt > now) {
190
412
  return {
191
413
  l1: { entries: l1Entries, sizeKB: l1SizeKB },
@@ -193,11 +415,13 @@ function getMemoryData(cwd) {
193
415
  l3: { status: cachedQdrantStatus.data.status, uptime: cachedQdrantStatus.data.uptime },
194
416
  l4: { entities: l4Entities, relationships: l4Relationships },
195
417
  compression: {
196
- rawBytes: stats.totalRawBytes,
197
- contextBytes: stats.totalContextBytes,
198
- savingsPercent: stats.savingsPercent,
199
- totalCalls: stats.totalCalls,
418
+ rawBytes: compressionRaw,
419
+ contextBytes: compressionCtx,
420
+ savingsPercent: compressionSavings,
421
+ totalCalls: compressionCalls,
200
422
  },
423
+ hitsMisses,
424
+ recentQueries,
201
425
  };
202
426
  }
203
427
  try {
@@ -223,11 +447,13 @@ function getMemoryData(cwd) {
223
447
  l3: { status: l3Status, uptime: l3Uptime },
224
448
  l4: { entities: l4Entities, relationships: l4Relationships },
225
449
  compression: {
226
- rawBytes: stats.totalRawBytes,
227
- contextBytes: stats.totalContextBytes,
228
- savingsPercent: stats.savingsPercent,
229
- totalCalls: stats.totalCalls,
450
+ rawBytes: compressionRaw,
451
+ contextBytes: compressionCtx,
452
+ savingsPercent: compressionSavings,
453
+ totalCalls: compressionCalls,
230
454
  },
455
+ hitsMisses,
456
+ recentQueries,
231
457
  };
232
458
  }
233
459
  function getModelData(cwd) {
@@ -281,7 +507,7 @@ function getModelData(cwd) {
281
507
  }
282
508
  function getTaskData(cwd) {
283
509
  const taskDbPath = join(cwd, '.uap/tasks/tasks.db');
284
- const result = { total: 0, done: 0, inProgress: 0, blocked: 0, open: 0 };
510
+ const result = { total: 0, done: 0, inProgress: 0, blocked: 0, open: 0, items: [] };
285
511
  if (existsSync(taskDbPath)) {
286
512
  try {
287
513
  const db = new Database(taskDbPath, { readonly: true });
@@ -292,6 +518,31 @@ function getTaskData(cwd) {
292
518
  result.inProgress = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='in_progress'").get().c;
293
519
  result.blocked = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='blocked'").get().c;
294
520
  result.open = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='open'").get().c;
521
+ // Fetch individual task items for kanban board (most recent 50)
522
+ const rows = db
523
+ .prepare(`SELECT id, title, type, status, priority, assignee, updated_at
524
+ FROM tasks
525
+ WHERE status NOT IN ('done', 'wont_do')
526
+ ORDER BY priority ASC, updated_at DESC
527
+ LIMIT 50`)
528
+ .all();
529
+ // Also fetch recent done/wont_do (last 10)
530
+ const doneRows = db
531
+ .prepare(`SELECT id, title, type, status, priority, assignee, updated_at
532
+ FROM tasks
533
+ WHERE status IN ('done', 'wont_do')
534
+ ORDER BY updated_at DESC
535
+ LIMIT 10`)
536
+ .all();
537
+ result.items = [...rows, ...doneRows].map((r) => ({
538
+ id: r.id,
539
+ title: r.title,
540
+ type: r.type,
541
+ status: r.status,
542
+ priority: r.priority,
543
+ assignee: r.assignee,
544
+ updatedAt: r.updated_at,
545
+ }));
295
546
  db.close();
296
547
  }
297
548
  catch {
@@ -302,33 +553,321 @@ function getTaskData(cwd) {
302
553
  }
303
554
  function getCoordData(cwd) {
304
555
  const coordDbPath = join(cwd, 'agents/data/coordination/coordination.db');
305
- const result = { activeAgents: 0, activeClaims: 0, pendingDeploys: 0 };
556
+ const result = {
557
+ activeAgents: 0,
558
+ activeClaims: 0,
559
+ pendingDeploys: 0,
560
+ totalAgents: 0,
561
+ completedAgents: 0,
562
+ patternHits: 0,
563
+ patternSuccesses: 0,
564
+ activeWorktrees: 0,
565
+ agents: [],
566
+ skillsPerAgent: {},
567
+ patternsPerAgent: {},
568
+ };
306
569
  if (existsSync(coordDbPath)) {
307
570
  try {
308
571
  const db = new Database(coordDbPath, { readonly: true });
309
- const hasAgents = db
310
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='agent_registry'")
311
- .all();
312
- if (hasAgents.length > 0) {
313
- result.activeAgents = db.prepare("SELECT COUNT(*) as c FROM agent_registry WHERE status='active'").get().c;
572
+ // Active agents
573
+ try {
574
+ const hasAgents = db
575
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='agent_registry'")
576
+ .all();
577
+ if (hasAgents.length > 0) {
578
+ result.activeAgents = db.prepare("SELECT COUNT(*) as c FROM agent_registry WHERE status='active'").get().c;
579
+ result.totalAgents = db.prepare('SELECT COUNT(*) as c FROM agent_registry').get().c;
580
+ result.completedAgents = db
581
+ .prepare("SELECT COUNT(*) as c FROM agent_registry WHERE status='completed'")
582
+ .get().c;
583
+ // Agent list
584
+ try {
585
+ const agentRows = db
586
+ .prepare('SELECT id, name, status, started_at FROM agent_registry ORDER BY started_at DESC LIMIT 20')
587
+ .all();
588
+ result.agents = agentRows.map((a) => ({
589
+ id: a.id,
590
+ name: a.name,
591
+ status: a.status,
592
+ startedAt: a.started_at,
593
+ }));
594
+ }
595
+ catch {
596
+ /* ignore */
597
+ }
598
+ }
314
599
  }
315
- const hasClaims = db
316
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='work_claims'")
317
- .all();
318
- if (hasClaims.length > 0) {
319
- result.activeClaims = db.prepare("SELECT COUNT(*) as c FROM work_claims WHERE status='active'").get().c;
600
+ catch {
601
+ /* ignore */
602
+ }
603
+ // Work claims - NO status column, use COUNT(*)
604
+ try {
605
+ const hasClaims = db
606
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='work_claims'")
607
+ .all();
608
+ if (hasClaims.length > 0) {
609
+ result.activeClaims = db.prepare('SELECT COUNT(*) as c FROM work_claims').get().c;
610
+ }
320
611
  }
612
+ catch {
613
+ /* ignore */
614
+ }
615
+ // Deploy queue
616
+ try {
617
+ const hasDQ = db
618
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='deploy_queue'")
619
+ .all();
620
+ if (hasDQ.length > 0) {
621
+ result.pendingDeploys = db.prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='pending'").get().c;
622
+ }
623
+ }
624
+ catch {
625
+ /* ignore */
626
+ }
627
+ // Pattern outcomes
628
+ try {
629
+ const hasPO = db
630
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_outcomes'")
631
+ .all();
632
+ if (hasPO.length > 0) {
633
+ const poRow = db
634
+ .prepare('SELECT SUM(uses) as u, SUM(successes) as s FROM pattern_outcomes')
635
+ .get();
636
+ result.patternHits = poRow?.u || 0;
637
+ result.patternSuccesses = poRow?.s || 0;
638
+ // Patterns per agent - group by pattern_id
639
+ try {
640
+ const patternRows = db
641
+ .prepare('SELECT pattern_id, task_category, uses FROM pattern_outcomes ORDER BY uses DESC')
642
+ .all();
643
+ // Group patterns by agent (use agent list if available, otherwise use 'all')
644
+ const agentIds = result.agents.length > 0 ? result.agents.map((a) => a.id) : ['all'];
645
+ for (const agentId of agentIds) {
646
+ result.patternsPerAgent[agentId] = patternRows.map((p) => ({
647
+ id: p.pattern_id,
648
+ category: p.task_category,
649
+ uses: p.uses,
650
+ }));
651
+ }
652
+ }
653
+ catch {
654
+ /* ignore */
655
+ }
656
+ }
657
+ }
658
+ catch {
659
+ /* ignore */
660
+ }
661
+ db.close();
662
+ }
663
+ catch {
664
+ /* ignore */
665
+ }
666
+ }
667
+ // Worktree count from worktree registry
668
+ try {
669
+ const wtDbPath = join(cwd, '.uap/worktree_registry.db');
670
+ if (existsSync(wtDbPath)) {
671
+ const wtDb = new Database(wtDbPath, { readonly: true });
672
+ try {
673
+ const hasTable = wtDb
674
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='worktrees'")
675
+ .all();
676
+ if (hasTable.length > 0) {
677
+ result.activeWorktrees = wtDb.prepare("SELECT COUNT(*) as c FROM worktrees WHERE status='active'").get().c;
678
+ }
679
+ }
680
+ catch {
681
+ /* ignore */
682
+ }
683
+ wtDb.close();
684
+ }
685
+ }
686
+ catch {
687
+ /* ignore */
688
+ }
689
+ // Skills per agent: read from .claude/skills/ directory (shared by all agents)
690
+ try {
691
+ const skillsDir = join(cwd, '.claude', 'skills');
692
+ if (existsSync(skillsDir)) {
693
+ const skillDirs = readdirSync(skillsDir).filter((d) => {
694
+ try {
695
+ return statSync(join(skillsDir, d)).isDirectory();
696
+ }
697
+ catch {
698
+ return false;
699
+ }
700
+ });
701
+ const agentIds = result.agents.length > 0 ? result.agents.map((a) => a.id) : ['all'];
702
+ for (const agentId of agentIds) {
703
+ result.skillsPerAgent[agentId] = skillDirs;
704
+ }
705
+ }
706
+ }
707
+ catch {
708
+ /* ignore */
709
+ }
710
+ return result;
711
+ }
712
+ export function getDeployBucketData(cwd) {
713
+ const coordDbPath = join(cwd, 'agents/data/coordination/coordination.db');
714
+ const summary = {
715
+ totalActions: 0,
716
+ queued: 0,
717
+ batched: 0,
718
+ executing: 0,
719
+ done: 0,
720
+ failed: 0,
721
+ batchCount: 0,
722
+ savedOps: 0,
723
+ };
724
+ if (!existsSync(coordDbPath))
725
+ return summary;
726
+ try {
727
+ const db = new Database(coordDbPath, { readonly: true });
728
+ try {
321
729
  const hasDQ = db
322
730
  .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='deploy_queue'")
323
731
  .all();
324
732
  if (hasDQ.length > 0) {
325
- result.pendingDeploys = db.prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='pending'").get().c;
733
+ // Use status mapping: 'completed' counts as done
734
+ const rows = db
735
+ .prepare(`SELECT status, COUNT(*) as c FROM deploy_queue GROUP BY status`)
736
+ .all();
737
+ for (const row of rows) {
738
+ summary.totalActions += row.c;
739
+ switch (row.status) {
740
+ case 'pending':
741
+ summary.queued += row.c;
742
+ break;
743
+ case 'batched':
744
+ summary.batched += row.c;
745
+ break;
746
+ case 'executing':
747
+ summary.executing += row.c;
748
+ break;
749
+ case 'completed':
750
+ summary.done += row.c;
751
+ break;
752
+ case 'failed':
753
+ summary.failed += row.c;
754
+ break;
755
+ }
756
+ }
757
+ }
758
+ }
759
+ catch {
760
+ /* ignore */
761
+ }
762
+ try {
763
+ const hasDB = db
764
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='deploy_batches'")
765
+ .all();
766
+ if (hasDB.length > 0) {
767
+ summary.batchCount = db.prepare('SELECT COUNT(*) as c FROM deploy_batches').get().c;
326
768
  }
327
- db.close();
328
769
  }
329
770
  catch {
330
771
  /* ignore */
331
772
  }
773
+ db.close();
774
+ }
775
+ catch {
776
+ /* ignore */
777
+ }
778
+ // Calculate saved ops (batched actions that were squashed)
779
+ if (summary.batchCount > 0 && summary.totalActions > summary.batchCount) {
780
+ summary.savedOps = summary.totalActions - summary.batchCount;
781
+ }
782
+ return summary;
783
+ }
784
+ // ── Compliance Data ──
785
+ function categorizeMechanism(policyId, policyName, operation) {
786
+ const combined = `${policyId} ${policyName} ${operation}`.toLowerCase();
787
+ if (combined.includes('worktree'))
788
+ return 'Worktree Gate';
789
+ if (combined.includes('build'))
790
+ return 'Build Gate';
791
+ if (combined.includes('test'))
792
+ return 'Test Gate';
793
+ if (combined.includes('schema'))
794
+ return 'Schema Diff Gate';
795
+ if (combined.includes('backup'))
796
+ return 'File Backup';
797
+ if (combined.includes('version'))
798
+ return 'Version Gate';
799
+ if (combined.includes('lint'))
800
+ return 'Lint Gate';
801
+ if (combined.includes('deploy'))
802
+ return 'Deploy Gate';
803
+ if (combined.includes('security') || combined.includes('secret'))
804
+ return 'Security Gate';
805
+ return 'Policy Enforcement';
806
+ }
807
+ function getComplianceData(cwd) {
808
+ const dbPath = join(cwd, 'agents', 'data', 'memory', 'policies.db');
809
+ const result = {
810
+ totalChecks: 0,
811
+ totalBlocks: 0,
812
+ blockRate: '0%',
813
+ recentFailures: [],
814
+ failuresByMechanism: {},
815
+ };
816
+ if (!existsSync(dbPath))
817
+ return result;
818
+ try {
819
+ const db = new Database(dbPath, { readonly: true });
820
+ const hasTable = db
821
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='policy_executions'")
822
+ .all();
823
+ if (hasTable.length === 0) {
824
+ db.close();
825
+ return result;
826
+ }
827
+ result.totalChecks = db.prepare('SELECT COUNT(*) as c FROM policy_executions').get().c;
828
+ result.totalBlocks = db.prepare('SELECT COUNT(*) as c FROM policy_executions WHERE allowed = 0').get().c;
829
+ result.blockRate =
830
+ result.totalChecks > 0
831
+ ? `${Math.round((result.totalBlocks / result.totalChecks) * 100)}%`
832
+ : '0%';
833
+ const hasPolicies = db
834
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='policies'")
835
+ .all();
836
+ let failureRows;
837
+ if (hasPolicies.length > 0) {
838
+ failureRows = db
839
+ .prepare(`SELECT pe.policyId, pe.operation, pe.reason, pe.executedAt, COALESCE(p.name, pe.policyId) as policyName
840
+ FROM policy_executions pe LEFT JOIN policies p ON pe.policyId = p.id
841
+ WHERE pe.allowed = 0 ORDER BY pe.executedAt DESC LIMIT 50`)
842
+ .all();
843
+ }
844
+ else {
845
+ failureRows = db
846
+ .prepare(`SELECT policyId, operation, reason, executedAt, policyId as policyName
847
+ FROM policy_executions WHERE allowed = 0 ORDER BY executedAt DESC LIMIT 50`)
848
+ .all();
849
+ }
850
+ const mechanismCounts = {};
851
+ result.recentFailures = failureRows.map((r) => {
852
+ const pid = r.policyId || '';
853
+ const pname = r.policyName || pid;
854
+ const op = r.operation || 'unknown';
855
+ const mech = categorizeMechanism(pid, pname, op);
856
+ mechanismCounts[mech] = (mechanismCounts[mech] || 0) + 1;
857
+ return {
858
+ policyId: pid,
859
+ policyName: pname,
860
+ operation: op,
861
+ reason: r.reason || '',
862
+ executedAt: r.executedAt || '',
863
+ defeatedMechanism: mech,
864
+ };
865
+ });
866
+ result.failuresByMechanism = mechanismCounts;
867
+ db.close();
868
+ }
869
+ catch {
870
+ /* ignore */
332
871
  }
333
872
  return result;
334
873
  }