@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/benchmarks/benchmark.d.ts +14 -14
- package/dist/bin/cli.js +5 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/llama-server-optimize.js +0 -0
- package/dist/bin/policy.js +0 -0
- package/dist/cli/task.d.ts +1 -1
- package/dist/cli/task.d.ts.map +1 -1
- package/dist/cli/task.js +65 -0
- package/dist/cli/task.js.map +1 -1
- package/dist/coordination/service.d.ts +11 -0
- package/dist/coordination/service.d.ts.map +1 -1
- package/dist/coordination/service.js +34 -3
- package/dist/coordination/service.js.map +1 -1
- package/dist/dashboard/data-seeder.d.ts +33 -0
- package/dist/dashboard/data-seeder.d.ts.map +1 -0
- package/dist/dashboard/data-seeder.js +247 -0
- package/dist/dashboard/data-seeder.js.map +1 -0
- package/dist/dashboard/data-service.d.ts +109 -0
- package/dist/dashboard/data-service.d.ts.map +1 -1
- package/dist/dashboard/data-service.js +566 -27
- package/dist/dashboard/data-service.js.map +1 -1
- package/dist/dashboard/index.d.ts +4 -2
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +2 -1
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +17 -0
- package/dist/dashboard/server.js.map +1 -1
- package/dist/models/profile-loader.d.ts +1 -1
- package/dist/models/types.d.ts +45 -45
- package/dist/policies/schemas/policy.d.ts +5 -5
- package/dist/tasks/types.d.ts +22 -22
- package/dist/types/config.d.ts +170 -170
- package/dist/types/coordination.d.ts +22 -22
- package/dist/uap-droids-strict.d.ts +4 -4
- package/package.json +1 -1
- package/scripts/maintenance/verify-compliance.sh +12 -12
- package/templates/hooks/pre-compact.sh +14 -0
- package/templates/hooks/session-start.sh +1 -1
- package/dist/bin/tool-calls.d.ts +0 -3
- package/dist/bin/tool-calls.d.ts.map +0 -1
- package/dist/bin/tool-calls.js +0 -4
- package/dist/bin/tool-calls.js.map +0 -1
- package/dist/cli/completion-gates.d.ts +0 -51
- package/dist/cli/completion-gates.d.ts.map +0 -1
- package/dist/cli/completion-gates.js +0 -201
- package/dist/cli/completion-gates.js.map +0 -1
- package/dist/cli/rtk-validation.d.ts +0 -24
- package/dist/cli/rtk-validation.d.ts.map +0 -1
- package/dist/cli/rtk-validation.js +0 -138
- package/dist/cli/rtk-validation.js.map +0 -1
- package/dist/cli/uap.d.ts +0 -10
- package/dist/cli/uap.d.ts.map +0 -1
- package/dist/cli/uap.js +0 -457
- package/dist/cli/uap.js.map +0 -1
- package/dist/coordination/droid-validator.d.ts +0 -59
- package/dist/coordination/droid-validator.d.ts.map +0 -1
- package/dist/coordination/droid-validator.js +0 -142
- package/dist/coordination/droid-validator.js.map +0 -1
- package/dist/coordination/worktree-enforcer.d.ts +0 -22
- package/dist/coordination/worktree-enforcer.d.ts.map +0 -1
- package/dist/coordination/worktree-enforcer.js +0 -71
- package/dist/coordination/worktree-enforcer.js.map +0 -1
- package/dist/generators/template-loader.d.ts +0 -105
- package/dist/generators/template-loader.d.ts.map +0 -1
- package/dist/generators/template-loader.js +0 -291
- package/dist/generators/template-loader.js.map +0 -1
- package/dist/memory/active-context.d.ts +0 -65
- package/dist/memory/active-context.d.ts.map +0 -1
- package/dist/memory/active-context.js +0 -126
- package/dist/memory/active-context.js.map +0 -1
- package/dist/memory/agent-scoped-memory.d.ts +0 -67
- package/dist/memory/agent-scoped-memory.d.ts.map +0 -1
- package/dist/memory/agent-scoped-memory.js +0 -126
- package/dist/memory/agent-scoped-memory.js.map +0 -1
- package/dist/memory/dedup-detector.d.ts +0 -57
- package/dist/memory/dedup-detector.d.ts.map +0 -1
- package/dist/memory/dedup-detector.js +0 -107
- package/dist/memory/dedup-detector.js.map +0 -1
- package/dist/memory/dedup-memory.d.ts +0 -89
- package/dist/memory/dedup-memory.d.ts.map +0 -1
- package/dist/memory/dedup-memory.js +0 -173
- package/dist/memory/dedup-memory.js.map +0 -1
- package/dist/memory/generic-uap-patterns.d.ts +0 -7
- package/dist/memory/generic-uap-patterns.d.ts.map +0 -1
- package/dist/memory/generic-uap-patterns.js +0 -43
- package/dist/memory/generic-uap-patterns.js.map +0 -1
- package/dist/memory/merge-claude-md.d.ts +0 -45
- package/dist/memory/merge-claude-md.d.ts.map +0 -1
- package/dist/memory/merge-claude-md.js +0 -118
- package/dist/memory/merge-claude-md.js.map +0 -1
- package/dist/memory/multi-view-memory.d.ts +0 -134
- package/dist/memory/multi-view-memory.d.ts.map +0 -1
- package/dist/memory/multi-view-memory.js +0 -430
- package/dist/memory/multi-view-memory.js.map +0 -1
- package/dist/memory/patterns.d.ts +0 -37
- package/dist/memory/patterns.d.ts.map +0 -1
- package/dist/memory/patterns.js +0 -81
- package/dist/memory/patterns.js.map +0 -1
- package/dist/memory/semantic-edge-graph.d.ts +0 -86
- package/dist/memory/semantic-edge-graph.d.ts.map +0 -1
- package/dist/memory/semantic-edge-graph.js +0 -168
- package/dist/memory/semantic-edge-graph.js.map +0 -1
- package/dist/memory/semantic-retrieval.d.ts +0 -70
- package/dist/memory/semantic-retrieval.d.ts.map +0 -1
- package/dist/memory/semantic-retrieval.js +0 -112
- package/dist/memory/semantic-retrieval.js.map +0 -1
- package/dist/memory/short-term/factory.d.ts +0 -26
- package/dist/memory/short-term/factory.d.ts.map +0 -1
- package/dist/memory/short-term/factory.js +0 -28
- package/dist/memory/short-term/factory.js.map +0 -1
- package/dist/memory/short-term/indexeddb.d.ts +0 -25
- package/dist/memory/short-term/indexeddb.d.ts.map +0 -1
- package/dist/memory/short-term/indexeddb.js +0 -64
- package/dist/memory/short-term/indexeddb.js.map +0 -1
- package/dist/memory/smart-consolidator.d.ts +0 -53
- package/dist/memory/smart-consolidator.d.ts.map +0 -1
- package/dist/memory/smart-consolidator.js +0 -144
- package/dist/memory/smart-consolidator.js.map +0 -1
- package/dist/memory/view-memory.d.ts +0 -80
- package/dist/memory/view-memory.d.ts.map +0 -1
- package/dist/memory/view-memory.js +0 -130
- package/dist/memory/view-memory.js.map +0 -1
- package/dist/memory/wrapped-memory.d.ts +0 -77
- package/dist/memory/wrapped-memory.d.ts.map +0 -1
- package/dist/memory/wrapped-memory.js +0 -127
- package/dist/memory/wrapped-memory.js.map +0 -1
- package/dist/models/api-client.d.ts +0 -46
- package/dist/models/api-client.d.ts.map +0 -1
- package/dist/models/api-client.js +0 -182
- package/dist/models/api-client.js.map +0 -1
- package/dist/utils/config-manager.d.ts +0 -30
- package/dist/utils/config-manager.d.ts.map +0 -1
- package/dist/utils/config-manager.js +0 -41
- package/dist/utils/config-manager.js.map +0 -1
- package/dist/utils/fetch-with-retry.d.ts +0 -5
- package/dist/utils/fetch-with-retry.d.ts.map +0 -1
- package/dist/utils/fetch-with-retry.js +0 -61
- package/dist/utils/fetch-with-retry.js.map +0 -1
- package/dist/utils/file-discovery.d.ts +0 -38
- package/dist/utils/file-discovery.d.ts.map +0 -1
- package/dist/utils/file-discovery.js +0 -100
- package/dist/utils/file-discovery.js.map +0 -1
- package/dist/utils/validate-json.d.ts +0 -51
- package/dist/utils/validate-json.d.ts.map +0 -1
- package/dist/utils/validate-json.js +0 -94
- package/dist/utils/validate-json.js.map +0 -1
- 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
|
|
115
|
+
memory,
|
|
29
116
|
models: getModelData(cwd),
|
|
30
|
-
tasks
|
|
31
|
-
coordination
|
|
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
|
|
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:
|
|
197
|
-
contextBytes:
|
|
198
|
-
savingsPercent:
|
|
199
|
-
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:
|
|
227
|
-
contextBytes:
|
|
228
|
-
savingsPercent:
|
|
229
|
-
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 = {
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
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
|
}
|