@miller-tech/uap 2.21.3 → 2.36.4

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 (328) hide show
  1. package/README.md +22 -10
  2. package/config/model-profiles/qwen38-coder390.json +46 -0
  3. package/config/model-profiles/qwen38.json +3 -2
  4. package/config/model-registry.json +110 -0
  5. package/dist/.tsbuildinfo +1 -1
  6. package/dist/benchmarks/paired/types.d.ts +2 -2
  7. package/dist/bin/cli.js +315 -20
  8. package/dist/bin/cli.js.map +1 -1
  9. package/dist/cli/attention.d.ts +6 -0
  10. package/dist/cli/attention.d.ts.map +1 -0
  11. package/dist/cli/attention.js +115 -0
  12. package/dist/cli/attention.js.map +1 -0
  13. package/dist/cli/bench-report.d.ts +82 -0
  14. package/dist/cli/bench-report.d.ts.map +1 -0
  15. package/dist/cli/bench-report.js +237 -0
  16. package/dist/cli/bench-report.js.map +1 -0
  17. package/dist/cli/clients.d.ts +6 -0
  18. package/dist/cli/clients.d.ts.map +1 -0
  19. package/dist/cli/clients.js +142 -0
  20. package/dist/cli/clients.js.map +1 -0
  21. package/dist/cli/dashboard-grants.d.ts +26 -0
  22. package/dist/cli/dashboard-grants.d.ts.map +1 -0
  23. package/dist/cli/dashboard-grants.js +69 -0
  24. package/dist/cli/dashboard-grants.js.map +1 -0
  25. package/dist/cli/dashboard-pace.d.ts +31 -0
  26. package/dist/cli/dashboard-pace.d.ts.map +1 -0
  27. package/dist/cli/dashboard-pace.js +42 -0
  28. package/dist/cli/dashboard-pace.js.map +1 -0
  29. package/dist/cli/dashboard-session.d.ts +25 -0
  30. package/dist/cli/dashboard-session.d.ts.map +1 -0
  31. package/dist/cli/dashboard-session.js +814 -0
  32. package/dist/cli/dashboard-session.js.map +1 -0
  33. package/dist/cli/dashboard-types.d.ts +18 -0
  34. package/dist/cli/dashboard-types.d.ts.map +1 -0
  35. package/dist/cli/dashboard-types.js +9 -0
  36. package/dist/cli/dashboard-types.js.map +1 -0
  37. package/dist/cli/dashboard.d.ts +2 -18
  38. package/dist/cli/dashboard.d.ts.map +1 -1
  39. package/dist/cli/dashboard.js +43 -793
  40. package/dist/cli/dashboard.js.map +1 -1
  41. package/dist/cli/deliver.d.ts +35 -0
  42. package/dist/cli/deliver.d.ts.map +1 -1
  43. package/dist/cli/deliver.js +93 -9
  44. package/dist/cli/deliver.js.map +1 -1
  45. package/dist/cli/diff.d.ts +85 -0
  46. package/dist/cli/diff.d.ts.map +1 -0
  47. package/dist/cli/diff.js +293 -0
  48. package/dist/cli/diff.js.map +1 -0
  49. package/dist/cli/eta.d.ts +35 -0
  50. package/dist/cli/eta.d.ts.map +1 -0
  51. package/dist/cli/eta.js +101 -0
  52. package/dist/cli/eta.js.map +1 -0
  53. package/dist/cli/handsfree.d.ts +7 -0
  54. package/dist/cli/handsfree.d.ts.map +1 -1
  55. package/dist/cli/handsfree.js +27 -0
  56. package/dist/cli/handsfree.js.map +1 -1
  57. package/dist/cli/hooks.d.ts.map +1 -1
  58. package/dist/cli/hooks.js +126 -5
  59. package/dist/cli/hooks.js.map +1 -1
  60. package/dist/cli/loops.d.ts +24 -5
  61. package/dist/cli/loops.d.ts.map +1 -1
  62. package/dist/cli/loops.js +92 -12
  63. package/dist/cli/loops.js.map +1 -1
  64. package/dist/cli/models.d.ts +62 -0
  65. package/dist/cli/models.d.ts.map +1 -0
  66. package/dist/cli/models.js +567 -0
  67. package/dist/cli/models.js.map +1 -0
  68. package/dist/cli/plan.d.ts +9 -0
  69. package/dist/cli/plan.d.ts.map +1 -1
  70. package/dist/cli/plan.js +57 -4
  71. package/dist/cli/plan.js.map +1 -1
  72. package/dist/cli/proxy-relay.d.ts +55 -0
  73. package/dist/cli/proxy-relay.d.ts.map +1 -0
  74. package/dist/cli/proxy-relay.js +116 -0
  75. package/dist/cli/proxy-relay.js.map +1 -0
  76. package/dist/cli/proxy.d.ts +4 -0
  77. package/dist/cli/proxy.d.ts.map +1 -1
  78. package/dist/cli/proxy.js +26 -1
  79. package/dist/cli/proxy.js.map +1 -1
  80. package/dist/cli/sandbox.d.ts +101 -4
  81. package/dist/cli/sandbox.d.ts.map +1 -1
  82. package/dist/cli/sandbox.js +393 -44
  83. package/dist/cli/sandbox.js.map +1 -1
  84. package/dist/cli/self.d.ts +13 -0
  85. package/dist/cli/self.d.ts.map +1 -0
  86. package/dist/cli/self.js +94 -0
  87. package/dist/cli/self.js.map +1 -0
  88. package/dist/cli/systemd-services.d.ts +2 -8
  89. package/dist/cli/systemd-services.d.ts.map +1 -1
  90. package/dist/cli/systemd-services.js +5 -20
  91. package/dist/cli/systemd-services.js.map +1 -1
  92. package/dist/cli/task.js +1 -1
  93. package/dist/cli/task.js.map +1 -1
  94. package/dist/cli/wizard-config.d.ts.map +1 -1
  95. package/dist/cli/wizard-config.js +31 -2
  96. package/dist/cli/wizard-config.js.map +1 -1
  97. package/dist/config/settings-registry.d.ts +1 -1
  98. package/dist/config/settings-registry.d.ts.map +1 -1
  99. package/dist/config/settings-registry.js +108 -0
  100. package/dist/config/settings-registry.js.map +1 -1
  101. package/dist/coordination/capability-router.js +2 -2
  102. package/dist/coordination/capability-router.js.map +1 -1
  103. package/dist/coordination/database.d.ts +13 -0
  104. package/dist/coordination/database.d.ts.map +1 -1
  105. package/dist/coordination/database.js +63 -7
  106. package/dist/coordination/database.js.map +1 -1
  107. package/dist/coordination/eta-inject.d.ts +32 -0
  108. package/dist/coordination/eta-inject.d.ts.map +1 -0
  109. package/dist/coordination/eta-inject.js +64 -0
  110. package/dist/coordination/eta-inject.js.map +1 -0
  111. package/dist/coordination/reactor.d.ts.map +1 -1
  112. package/dist/coordination/reactor.js +14 -0
  113. package/dist/coordination/reactor.js.map +1 -1
  114. package/dist/coordination/service.d.ts +18 -8
  115. package/dist/coordination/service.d.ts.map +1 -1
  116. package/dist/coordination/service.js +38 -28
  117. package/dist/coordination/service.js.map +1 -1
  118. package/dist/dashboard/client-registry.d.ts +163 -0
  119. package/dist/dashboard/client-registry.d.ts.map +1 -0
  120. package/dist/dashboard/client-registry.js +549 -0
  121. package/dist/dashboard/client-registry.js.map +1 -0
  122. package/dist/dashboard/controls.d.ts.map +1 -1
  123. package/dist/dashboard/controls.js +33 -4
  124. package/dist/dashboard/controls.js.map +1 -1
  125. package/dist/dashboard/data-seeder.d.ts +10 -7
  126. package/dist/dashboard/data-seeder.d.ts.map +1 -1
  127. package/dist/dashboard/data-seeder.js +31 -75
  128. package/dist/dashboard/data-seeder.js.map +1 -1
  129. package/dist/dashboard/data-service.d.ts +9 -0
  130. package/dist/dashboard/data-service.d.ts.map +1 -1
  131. package/dist/dashboard/data-service.js +32 -3
  132. package/dist/dashboard/data-service.js.map +1 -1
  133. package/dist/dashboard/placement-routes.d.ts +119 -0
  134. package/dist/dashboard/placement-routes.d.ts.map +1 -0
  135. package/dist/dashboard/placement-routes.js +331 -0
  136. package/dist/dashboard/placement-routes.js.map +1 -0
  137. package/dist/dashboard/server.d.ts.map +1 -1
  138. package/dist/dashboard/server.js +408 -2
  139. package/dist/dashboard/server.js.map +1 -1
  140. package/dist/delivery/attention.d.ts +130 -0
  141. package/dist/delivery/attention.d.ts.map +1 -0
  142. package/dist/delivery/attention.js +341 -0
  143. package/dist/delivery/attention.js.map +1 -0
  144. package/dist/delivery/decompose.d.ts +18 -0
  145. package/dist/delivery/decompose.d.ts.map +1 -1
  146. package/dist/delivery/decompose.js +22 -1
  147. package/dist/delivery/decompose.js.map +1 -1
  148. package/dist/delivery/eta-estimate.d.ts +111 -0
  149. package/dist/delivery/eta-estimate.d.ts.map +1 -0
  150. package/dist/delivery/eta-estimate.js +171 -0
  151. package/dist/delivery/eta-estimate.js.map +1 -0
  152. package/dist/delivery/eta.d.ts +23 -0
  153. package/dist/delivery/eta.d.ts.map +1 -0
  154. package/dist/delivery/eta.js +59 -0
  155. package/dist/delivery/eta.js.map +1 -0
  156. package/dist/delivery/orchestrated-mission.d.ts +10 -0
  157. package/dist/delivery/orchestrated-mission.d.ts.map +1 -1
  158. package/dist/delivery/orchestrated-mission.js +15 -1
  159. package/dist/delivery/orchestrated-mission.js.map +1 -1
  160. package/dist/delivery/plan-check.d.ts +7 -1
  161. package/dist/delivery/plan-check.d.ts.map +1 -1
  162. package/dist/delivery/plan-check.js +23 -2
  163. package/dist/delivery/plan-check.js.map +1 -1
  164. package/dist/delivery/run-state.d.ts +1 -1
  165. package/dist/delivery/run-state.d.ts.map +1 -1
  166. package/dist/delivery/run-state.js +8 -1
  167. package/dist/delivery/run-state.js.map +1 -1
  168. package/dist/delivery/task-orchestrator.d.ts +8 -3
  169. package/dist/delivery/task-orchestrator.d.ts.map +1 -1
  170. package/dist/delivery/task-orchestrator.js +75 -43
  171. package/dist/delivery/task-orchestrator.js.map +1 -1
  172. package/dist/delivery/task-sync.d.ts +21 -0
  173. package/dist/delivery/task-sync.d.ts.map +1 -1
  174. package/dist/delivery/task-sync.js +44 -8
  175. package/dist/delivery/task-sync.js.map +1 -1
  176. package/dist/delivery/task-workspace.d.ts +3 -3
  177. package/dist/delivery/task-workspace.js +3 -3
  178. package/dist/generators/self-card.d.ts +65 -0
  179. package/dist/generators/self-card.d.ts.map +1 -0
  180. package/dist/generators/self-card.js +256 -0
  181. package/dist/generators/self-card.js.map +1 -0
  182. package/dist/inference/probe.js +1 -1
  183. package/dist/inference/probe.js.map +1 -1
  184. package/dist/memory/prepopulate.d.ts.map +1 -1
  185. package/dist/memory/prepopulate.js +2 -0
  186. package/dist/memory/prepopulate.js.map +1 -1
  187. package/dist/models/analytics.d.ts +32 -0
  188. package/dist/models/analytics.d.ts.map +1 -1
  189. package/dist/models/analytics.js +68 -5
  190. package/dist/models/analytics.js.map +1 -1
  191. package/dist/models/plan-readability.d.ts +55 -0
  192. package/dist/models/plan-readability.d.ts.map +1 -0
  193. package/dist/models/plan-readability.js +339 -0
  194. package/dist/models/plan-readability.js.map +1 -0
  195. package/dist/placement/admission.d.ts +59 -0
  196. package/dist/placement/admission.d.ts.map +1 -0
  197. package/dist/placement/admission.js +197 -0
  198. package/dist/placement/admission.js.map +1 -0
  199. package/dist/placement/auto.d.ts +44 -0
  200. package/dist/placement/auto.d.ts.map +1 -0
  201. package/dist/placement/auto.js +212 -0
  202. package/dist/placement/auto.js.map +1 -0
  203. package/dist/placement/enforce.d.ts +128 -0
  204. package/dist/placement/enforce.d.ts.map +1 -0
  205. package/dist/placement/enforce.js +655 -0
  206. package/dist/placement/enforce.js.map +1 -0
  207. package/dist/placement/idle.d.ts +50 -0
  208. package/dist/placement/idle.d.ts.map +1 -0
  209. package/dist/placement/idle.js +213 -0
  210. package/dist/placement/idle.js.map +1 -0
  211. package/dist/placement/ledger.d.ts +323 -0
  212. package/dist/placement/ledger.d.ts.map +1 -0
  213. package/dist/placement/ledger.js +263 -0
  214. package/dist/placement/ledger.js.map +1 -0
  215. package/dist/placement/preview.d.ts +51 -0
  216. package/dist/placement/preview.d.ts.map +1 -0
  217. package/dist/placement/preview.js +61 -0
  218. package/dist/placement/preview.js.map +1 -0
  219. package/dist/placement/probes.d.ts +47 -0
  220. package/dist/placement/probes.d.ts.map +1 -0
  221. package/dist/placement/probes.js +207 -0
  222. package/dist/placement/probes.js.map +1 -0
  223. package/dist/placement/proxy-env.d.ts +21 -0
  224. package/dist/placement/proxy-env.d.ts.map +1 -0
  225. package/dist/placement/proxy-env.js +42 -0
  226. package/dist/placement/proxy-env.js.map +1 -0
  227. package/dist/placement/registry.d.ts +1038 -0
  228. package/dist/placement/registry.d.ts.map +1 -0
  229. package/dist/placement/registry.js +274 -0
  230. package/dist/placement/registry.js.map +1 -0
  231. package/dist/policies/liveness.d.ts +6 -8
  232. package/dist/policies/liveness.d.ts.map +1 -1
  233. package/dist/policies/liveness.js +6 -25
  234. package/dist/policies/liveness.js.map +1 -1
  235. package/dist/policies/sandbox-anchor.d.ts +91 -0
  236. package/dist/policies/sandbox-anchor.d.ts.map +1 -0
  237. package/dist/policies/sandbox-anchor.js +187 -0
  238. package/dist/policies/sandbox-anchor.js.map +1 -0
  239. package/dist/policies/sandbox-grants.d.ts +131 -0
  240. package/dist/policies/sandbox-grants.d.ts.map +1 -0
  241. package/dist/policies/sandbox-grants.js +562 -0
  242. package/dist/policies/sandbox-grants.js.map +1 -0
  243. package/dist/state/manifest.d.ts +4 -2
  244. package/dist/state/manifest.d.ts.map +1 -1
  245. package/dist/state/manifest.js +32 -16
  246. package/dist/state/manifest.js.map +1 -1
  247. package/dist/utils/checkout-root.d.ts +2 -0
  248. package/dist/utils/checkout-root.d.ts.map +1 -0
  249. package/dist/utils/checkout-root.js +43 -0
  250. package/dist/utils/checkout-root.js.map +1 -0
  251. package/dist/utils/resolve-uap-bin.d.ts +5 -0
  252. package/dist/utils/resolve-uap-bin.d.ts.map +1 -0
  253. package/dist/utils/resolve-uap-bin.js +33 -0
  254. package/dist/utils/resolve-uap-bin.js.map +1 -0
  255. package/dist/utils/terminal-safe.d.ts +12 -0
  256. package/dist/utils/terminal-safe.d.ts.map +1 -0
  257. package/dist/utils/terminal-safe.js +53 -0
  258. package/dist/utils/terminal-safe.js.map +1 -0
  259. package/docs/ATTRIBUTION.md +1 -0
  260. package/docs/DASHBOARD_UPLIFT_SPEC.md +8 -8
  261. package/docs/INDEX.md +6 -5
  262. package/docs/architecture/OVERVIEW.md +15 -3
  263. package/docs/architecture/PROTOCOL.md +7 -1
  264. package/docs/architecture/adr/0006-operator-directed-model-placement.md +86 -0
  265. package/docs/architecture/adr/0007-auto-displacement-allowlist-as-standing-consent.md +79 -0
  266. package/docs/architecture/adr/0008-fleet-clients-link-out.md +92 -0
  267. package/docs/architecture/adr/0009-completion-triggered-dispatch-and-per-leaf-model-dials.md +71 -0
  268. package/docs/architecture/adr/0010-attention-queue-and-afk-auto-decider.md +159 -0
  269. package/docs/architecture/adr/0011-sandbox-grants-and-network-egress-boundary.md +219 -0
  270. package/docs/architecture/adr/0012-operator-legibility-surfaces.md +175 -0
  271. package/docs/getting-started/INSTALLATION.md +1 -1
  272. package/docs/guides/AUTOMATIC_FEATURES.md +8 -0
  273. package/docs/guides/LOCAL_MODELS.md +1 -1
  274. package/docs/guides/PROXY.md +2 -1
  275. package/docs/guides/SANDBOX.md +91 -4
  276. package/docs/integrations/MCP_ROUTER.md +1 -1
  277. package/docs/plans/model-performance-proof-options.md +176 -0
  278. package/docs/reference/API.md +1 -1
  279. package/docs/reference/CLI.md +169 -4
  280. package/docs/reference/COMPARISON.md +1 -1
  281. package/docs/reference/CONFIGURATION.md +12 -3
  282. package/docs/reference/CONFIGURATION_REFERENCE.md +269 -14
  283. package/docs/reference/DATABASE_SCHEMA.md +1 -1
  284. package/docs/reference/FEATURES.md +15 -3
  285. package/docs/reference/PATTERNS.md +1 -1
  286. package/docs/reference/PLATFORMS.md +14 -1
  287. package/docs/specs/operator-model-placement.md +1314 -0
  288. package/docs/specs/operator-model-placement.review.md +339 -0
  289. package/package.json +2 -2
  290. package/scripts/maintenance/publish-npm.sh +23 -11
  291. package/scripts/version-bump.sh +6 -51
  292. package/src/policies/enforcers/__pycache__/_common.cpython-311.pyc +0 -0
  293. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-311.pyc +0 -0
  294. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-311.pyc +0 -0
  295. package/src/policies/schemas/policies/mandatory-testing-deployment.md +2 -5
  296. package/templates/hooks/__pycache__/deliver_autoroute.cpython-311.pyc +0 -0
  297. package/templates/hooks/session-start.sh +44 -0
  298. package/tools/agents/UAP/cli.py +14 -3
  299. package/tools/agents/UAP/paths.py +50 -0
  300. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-311.pyc +0 -0
  301. package/tools/agents/scripts/anthropic_proxy.py +1420 -100
  302. package/tools/agents/scripts/toolcall_path_normalizer.py +46 -1
  303. package/tools/agents/tests/test_anthropic_proxy_streaming.py +29 -6
  304. package/tools/agents/tests/test_doubling_break.py +64 -5
  305. package/tools/agents/tests/test_error_loop_break.py +10 -4
  306. package/tools/agents/tests/test_error_loop_ignores_correctives.py +13 -2
  307. package/tools/agents/tests/test_lockstep_escalation.py +280 -9
  308. package/tools/agents/tests/test_path_containment.py +94 -0
  309. package/tools/agents/tests/test_proxy_unix_socket.py +162 -0
  310. package/tools/agents/tests/test_signature_quality.py +400 -0
  311. package/tools/agents/tests/test_uap_compliance.py +11 -1
  312. package/web/dash/core.js +1 -0
  313. package/web/dash/tab-clients.js +196 -0
  314. package/web/dash/tab-models.js +387 -8
  315. package/web/dash/tab-policies.js +87 -4
  316. package/web/dash/tabs.js +77 -73
  317. package/web/dashboard.html +6 -0
  318. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  319. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-312.pyc +0 -0
  320. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
  321. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  322. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  323. package/web/dash/tab-agents.js +0 -21
  324. package/web/dash/tab-deliver.js +0 -12
  325. package/web/dash/tab-memory.js +0 -12
  326. package/web/dash/tab-orchestration.js +0 -21
  327. package/web/dash/tab-overview.js +0 -21
  328. package/web/dash/tab-tasks.js +0 -21
@@ -0,0 +1,814 @@
1
+ /**
2
+ * The dashboard's SESSION operator surface, split from src/cli/dashboard.ts
3
+ * (that module was the repo's worst LOC offender and every new surface was
4
+ * growing it; the grants + pace sections moved out first, these follow the
5
+ * same pattern). Three views live here because they are one family:
6
+ *
7
+ * - showSessionDashboard — `uap dashboard session`: the operator's live
8
+ * one-glance state (version, git, tasks, memory, patterns, skills, droids,
9
+ * the Sandbox Grants section with its anchor verdict, the summary box).
10
+ * - showDashboard — the legacy one-shot "Real-Time Benchmark Dashboard"
11
+ * (direct read-only DB reads; kept for `uap dashboard benchmark`).
12
+ * - compactSessionSummary — the one-line terminal-title/brief summary other
13
+ * CLI surfaces print after their own work (task flows).
14
+ */
15
+ import chalk from 'chalk';
16
+ import ora from 'ora';
17
+ import { existsSync, statSync, readFileSync, readdirSync } from 'fs';
18
+ import { join } from 'path';
19
+ import { execSync } from 'child_process';
20
+ import Database from 'better-sqlite3';
21
+ import { progressBar, stackedBar, stackedBarLegend, box, sectionHeader, keyValue, divider, bulletList, } from './visualize.js';
22
+ import { getPolicyMemoryManager } from '../policies/policy-memory.js';
23
+ import { sandboxGrantsLines } from './dashboard-grants.js';
24
+ export async function showSessionDashboard(options) {
25
+ if (options.compact) {
26
+ compactSessionSummary();
27
+ return;
28
+ }
29
+ const spinner = ora('Loading session dashboard...').start();
30
+ try {
31
+ const cwd = process.cwd();
32
+ // ── Gather data from all sources ──
33
+ const memDbPath = join(cwd, 'agents/data/memory/short_term.db');
34
+ const coordDbPath = join(cwd, 'agents/data/coordination/coordination.db');
35
+ const taskDbPath = join(cwd, '.uap/tasks/tasks.db');
36
+ // Package version
37
+ let version = '?';
38
+ try {
39
+ const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8'));
40
+ version = pkg.version || '?';
41
+ }
42
+ catch {
43
+ /* ignore */
44
+ }
45
+ // Git info
46
+ let gitBranch = '?';
47
+ let gitDirty = 0;
48
+ let gitAhead = 0;
49
+ let lastCommit = '';
50
+ try {
51
+ gitBranch = execSync('git branch --show-current', {
52
+ encoding: 'utf-8',
53
+ cwd,
54
+ stdio: ['pipe', 'pipe', 'pipe'],
55
+ }).trim();
56
+ gitDirty = execSync('git status --porcelain', {
57
+ encoding: 'utf-8',
58
+ cwd,
59
+ stdio: ['pipe', 'pipe', 'pipe'],
60
+ })
61
+ .trim()
62
+ .split('\n')
63
+ .filter(Boolean).length;
64
+ gitAhead =
65
+ parseInt(execSync('git rev-list --count @{u}..HEAD 2>/dev/null || echo 0', {
66
+ encoding: 'utf-8',
67
+ cwd,
68
+ stdio: ['pipe', 'pipe', 'pipe'],
69
+ }).trim(), 10) || 0;
70
+ lastCommit = execSync('git log -1 --format="%h %s" 2>/dev/null', {
71
+ encoding: 'utf-8',
72
+ cwd,
73
+ stdio: ['pipe', 'pipe', 'pipe'],
74
+ }).trim();
75
+ }
76
+ catch {
77
+ /* ignore */
78
+ }
79
+ // Memory stats
80
+ let memEntries = 0;
81
+ let memSizeKB = 0;
82
+ let recentMemories = [];
83
+ if (existsSync(memDbPath)) {
84
+ try {
85
+ const memDb = new Database(memDbPath, { readonly: true });
86
+ memEntries = memDb.prepare('SELECT COUNT(*) as c FROM memories').get().c;
87
+ memSizeKB = Math.round(statSync(memDbPath).size / 1024);
88
+ recentMemories = memDb
89
+ .prepare("SELECT type, substr(content, 1, 80) as content FROM memories WHERE timestamp >= datetime('now', '-1 day') ORDER BY id DESC LIMIT 5")
90
+ .all();
91
+ memDb.close();
92
+ }
93
+ catch {
94
+ /* ignore */
95
+ }
96
+ }
97
+ // Session memories (open loops)
98
+ let sessionMemCount = 0;
99
+ let openLoops = [];
100
+ if (existsSync(memDbPath)) {
101
+ try {
102
+ const memDb = new Database(memDbPath, { readonly: true });
103
+ const tables = memDb
104
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='session_memories'")
105
+ .all();
106
+ if (tables.length > 0) {
107
+ sessionMemCount = memDb.prepare('SELECT COUNT(*) as c FROM session_memories').get().c;
108
+ openLoops = memDb
109
+ .prepare("SELECT content FROM session_memories WHERE type IN ('action','goal','decision') AND importance >= 7 ORDER BY id DESC LIMIT 5")
110
+ .all();
111
+ }
112
+ memDb.close();
113
+ }
114
+ catch {
115
+ /* ignore */
116
+ }
117
+ }
118
+ // Coordination stats
119
+ let activeAgents = 0;
120
+ let activeClaims = 0;
121
+ let pendingDeploys = 0;
122
+ if (existsSync(coordDbPath)) {
123
+ try {
124
+ const coordDb = new Database(coordDbPath, { readonly: true });
125
+ activeAgents = coordDb
126
+ .prepare("SELECT COUNT(*) as c FROM agent_registry WHERE status='active'")
127
+ .get().c;
128
+ activeClaims = coordDb.prepare('SELECT COUNT(*) as c FROM work_claims').get().c;
129
+ pendingDeploys = coordDb
130
+ .prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='pending'")
131
+ .get().c;
132
+ coordDb.close();
133
+ }
134
+ catch {
135
+ /* ignore */
136
+ }
137
+ }
138
+ // Task stats
139
+ let taskTotal = 0;
140
+ let taskOpen = 0;
141
+ let taskProgress = 0;
142
+ let taskBlocked = 0;
143
+ let taskDone = 0;
144
+ let activeTasks = [];
145
+ if (existsSync(taskDbPath)) {
146
+ try {
147
+ const taskDb = new Database(taskDbPath, { readonly: true });
148
+ taskTotal = taskDb.prepare('SELECT COUNT(*) as c FROM tasks').get().c;
149
+ taskOpen = taskDb.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='open'").get().c;
150
+ taskProgress = taskDb.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='in_progress'").get().c;
151
+ taskBlocked = taskDb.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='blocked'").get().c;
152
+ taskDone = taskDb
153
+ .prepare("SELECT COUNT(*) as c FROM tasks WHERE status='done' OR status='wont_do'")
154
+ .get().c;
155
+ activeTasks = taskDb
156
+ .prepare("SELECT id, title, priority FROM tasks WHERE status='in_progress' ORDER BY priority ASC LIMIT 5")
157
+ .all();
158
+ taskDb.close();
159
+ }
160
+ catch {
161
+ /* ignore */
162
+ }
163
+ }
164
+ // Qdrant status
165
+ let qdrantStatus = 'Stopped';
166
+ let qdrantUptime = '';
167
+ try {
168
+ const dockerOut = execSync('docker ps --filter name=qdrant --format "{{.Status}}"', {
169
+ encoding: 'utf-8',
170
+ stdio: ['pipe', 'pipe', 'pipe'],
171
+ }).trim();
172
+ if (dockerOut) {
173
+ qdrantStatus = 'Running';
174
+ qdrantUptime = dockerOut;
175
+ }
176
+ }
177
+ catch {
178
+ /* ignore */
179
+ }
180
+ // Patterns
181
+ let patternCount = 0;
182
+ let patternNames = [];
183
+ const patternIndexPath = join(cwd, '.factory/patterns/index.json');
184
+ if (existsSync(patternIndexPath)) {
185
+ try {
186
+ const patternData = JSON.parse(readFileSync(patternIndexPath, 'utf-8'));
187
+ const patterns = patternData.patterns || [];
188
+ patternCount = patterns.length;
189
+ patternNames = patterns
190
+ .slice(0, 5)
191
+ .map((p) => p.title || p.name || p.abbreviation || (p.id != null ? `P${p.id}` : '?'));
192
+ }
193
+ catch {
194
+ /* ignore */
195
+ }
196
+ }
197
+ // Skills
198
+ let skillNames = [];
199
+ const skillDirs = [join(cwd, '.claude/skills'), join(cwd, '.factory/skills')];
200
+ for (const dir of skillDirs) {
201
+ if (existsSync(dir)) {
202
+ try {
203
+ const entries = readdirSync(dir, { withFileTypes: true });
204
+ for (const entry of entries) {
205
+ if (entry.isDirectory() && existsSync(join(dir, entry.name, 'SKILL.md'))) {
206
+ skillNames.push(entry.name);
207
+ }
208
+ }
209
+ }
210
+ catch {
211
+ /* ignore */
212
+ }
213
+ }
214
+ }
215
+ // Droids
216
+ let droidNames = [];
217
+ const droidDir = join(cwd, '.factory/droids');
218
+ if (existsSync(droidDir)) {
219
+ try {
220
+ const entries = readdirSync(droidDir).filter((f) => f.endsWith('.md') && !f.startsWith('test-droid-'));
221
+ droidNames = entries.map((f) => f.replace('.md', ''));
222
+ }
223
+ catch {
224
+ /* ignore */
225
+ }
226
+ }
227
+ // Worktrees
228
+ let worktreeCount = 0;
229
+ const worktreeDir = join(cwd, '.worktrees');
230
+ if (existsSync(worktreeDir)) {
231
+ try {
232
+ worktreeCount = readdirSync(worktreeDir, { withFileTypes: true }).filter((e) => e.isDirectory()).length;
233
+ }
234
+ catch {
235
+ /* ignore */
236
+ }
237
+ }
238
+ spinner.stop();
239
+ // ── Render ──
240
+ console.log('');
241
+ console.log(chalk.bold.cyan(' UAP Session Dashboard'));
242
+ console.log(divider(62));
243
+ console.log('');
244
+ // System info
245
+ console.log(sectionHeader('System'));
246
+ console.log('');
247
+ for (const line of keyValue([
248
+ ['Version', `v${version}`],
249
+ [
250
+ 'Branch',
251
+ `${gitBranch}${gitDirty > 0 ? chalk.yellow(` (${gitDirty} uncommitted)`) : chalk.green(' (clean)')}`,
252
+ ],
253
+ ['Ahead', gitAhead > 0 ? chalk.yellow(`${gitAhead} commits`) : chalk.dim('up to date')],
254
+ ['Last Commit', lastCommit.length > 55 ? lastCommit.slice(0, 55) + '...' : lastCommit],
255
+ ]))
256
+ console.log(line);
257
+ console.log('');
258
+ // Task progress
259
+ console.log(sectionHeader('Tasks'));
260
+ console.log('');
261
+ if (taskTotal > 0) {
262
+ const pct = Math.round((taskDone / taskTotal) * 100);
263
+ console.log(` ${progressBar(taskDone, taskTotal, 30, {
264
+ label: 'Progress',
265
+ filled: pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.white,
266
+ })}`);
267
+ console.log('');
268
+ const segments = [
269
+ { value: taskDone, color: chalk.green, label: 'Done' },
270
+ { value: taskProgress, color: chalk.cyan, label: 'Active' },
271
+ { value: taskOpen, color: chalk.white, label: 'Open' },
272
+ { value: taskBlocked, color: chalk.red, label: 'Blocked' },
273
+ ];
274
+ console.log(` ${stackedBar(segments, taskTotal, 40)}`);
275
+ console.log(` ${stackedBarLegend(segments)}`);
276
+ console.log('');
277
+ if (activeTasks.length > 0) {
278
+ console.log(chalk.dim(' Active:'));
279
+ for (const t of activeTasks) {
280
+ console.log(` ${chalk.cyan(t.id)} P${t.priority} ${t.title.slice(0, 45)}${t.title.length > 45 ? '...' : ''}`);
281
+ }
282
+ console.log('');
283
+ }
284
+ }
285
+ else {
286
+ console.log(chalk.dim(' No tasks tracked. Create with: uap task create --title "..."'));
287
+ console.log('');
288
+ }
289
+ // Memory layers
290
+ console.log(sectionHeader('Memory System'));
291
+ console.log('');
292
+ const memItems = [
293
+ {
294
+ text: `L1 Working Memory: ${chalk.bold(memEntries + ' entries')} ${chalk.dim(`(${memSizeKB} KB)`)}`,
295
+ status: existsSync(memDbPath) ? 'ok' : 'warn',
296
+ },
297
+ {
298
+ text: `L2 Session Memory: ${chalk.bold(sessionMemCount + ' entries')} ${openLoops.length > 0 ? chalk.yellow(`(${openLoops.length} open loops)`) : ''}`,
299
+ status: sessionMemCount > 0 ? 'ok' : 'info',
300
+ },
301
+ {
302
+ text: `L3 Semantic Memory: ${qdrantStatus === 'Running' ? chalk.green('Qdrant ' + qdrantUptime) : chalk.dim('Qdrant stopped')}`,
303
+ status: qdrantStatus === 'Running' ? 'ok' : 'warn',
304
+ },
305
+ {
306
+ text: `L4 Knowledge Graph: ${existsSync(memDbPath) ? chalk.green('Active') : chalk.dim('Not initialized')}`,
307
+ status: existsSync(memDbPath) ? 'ok' : 'warn',
308
+ },
309
+ ];
310
+ for (const line of bulletList(memItems))
311
+ console.log(line);
312
+ if (recentMemories.length > 0) {
313
+ console.log('');
314
+ console.log(chalk.dim(' Recent memories (24h):'));
315
+ for (const mem of recentMemories.slice(0, 3)) {
316
+ console.log(` ${chalk.dim((mem.type || 'unknown') + ':')} ${mem.content || ''}`);
317
+ }
318
+ }
319
+ console.log('');
320
+ // Coordination
321
+ console.log(sectionHeader('Coordination'));
322
+ console.log('');
323
+ for (const line of keyValue([
324
+ ['Active Agents', activeAgents > 0 ? chalk.green(String(activeAgents)) : chalk.dim('0')],
325
+ ['Resource Claims', activeClaims > 0 ? chalk.yellow(String(activeClaims)) : chalk.dim('0')],
326
+ [
327
+ 'Pending Deploys',
328
+ pendingDeploys > 0 ? chalk.yellow(String(pendingDeploys)) : chalk.dim('0'),
329
+ ],
330
+ ['Worktrees', worktreeCount > 0 ? chalk.yellow(String(worktreeCount)) : chalk.dim('0')],
331
+ ]))
332
+ console.log(line);
333
+ console.log('');
334
+ // Patterns & Skills
335
+ console.log(sectionHeader('Intelligence'));
336
+ console.log('');
337
+ if (patternCount > 0) {
338
+ console.log(` ${chalk.blue('Patterns')} ${chalk.bold(String(patternCount))} loaded`);
339
+ if (patternNames.length > 0) {
340
+ for (const name of patternNames) {
341
+ console.log(` ${chalk.dim('-')} ${name}`);
342
+ }
343
+ if (patternCount > 5)
344
+ console.log(chalk.dim(` ... and ${patternCount - 5} more`));
345
+ }
346
+ }
347
+ else {
348
+ console.log(chalk.dim(' Patterns: None loaded'));
349
+ }
350
+ console.log('');
351
+ if (skillNames.length > 0) {
352
+ console.log(` ${chalk.green('Skills')} ${chalk.bold(String(skillNames.length))} available`);
353
+ for (const name of skillNames) {
354
+ console.log(` ${chalk.dim('-')} ${name}`);
355
+ }
356
+ }
357
+ else {
358
+ console.log(chalk.dim(' Skills: None configured'));
359
+ }
360
+ console.log('');
361
+ if (droidNames.length > 0) {
362
+ console.log(` ${chalk.magenta('Droids')} ${chalk.bold(String(droidNames.length))} registered`);
363
+ for (const name of droidNames.slice(0, 5)) {
364
+ console.log(` ${chalk.dim('-')} ${name}`);
365
+ }
366
+ if (droidNames.length > 5)
367
+ console.log(chalk.dim(` ... and ${droidNames.length - 5} more`));
368
+ }
369
+ else {
370
+ console.log(chalk.dim(' Droids: None registered'));
371
+ }
372
+ console.log('');
373
+ // Policies (DB-driven)
374
+ console.log(sectionHeader('Policies'));
375
+ console.log('');
376
+ try {
377
+ const policyMgr = getPolicyMemoryManager();
378
+ const allPolicies = await policyMgr.getAllPolicies();
379
+ if (allPolicies.length > 0) {
380
+ for (const policy of allPolicies) {
381
+ const stageLabel = policy.enforcementStage || 'pre-exec';
382
+ const levelColor = policy.level === 'REQUIRED'
383
+ ? chalk.red
384
+ : policy.level === 'RECOMMENDED'
385
+ ? chalk.yellow
386
+ : chalk.dim;
387
+ console.log(` ${levelColor(policy.level.padEnd(14))} ${policy.name.slice(0, 30).padEnd(30)} ${chalk.dim(stageLabel)}`);
388
+ }
389
+ }
390
+ else {
391
+ console.log(chalk.dim(' No policies configured. Add with: uap policy add -f <file>'));
392
+ }
393
+ }
394
+ catch {
395
+ console.log(chalk.dim(' Policy DB not initialized'));
396
+ }
397
+ console.log('');
398
+ // Open loops
399
+ if (openLoops.length > 0) {
400
+ console.log(sectionHeader('Open Loops'));
401
+ console.log('');
402
+ for (const loop of openLoops) {
403
+ console.log(` ${chalk.yellow('>')} ${loop.content.slice(0, 70)}${loop.content.length > 70 ? '...' : ''}`);
404
+ }
405
+ console.log('');
406
+ }
407
+ // Summary box
408
+ const summaryLines = [
409
+ `v${version} on ${chalk.cyan(gitBranch)}${gitDirty > 0 ? chalk.yellow(' *') : ''}`,
410
+ `${chalk.bold(String(taskTotal))} tasks (${taskDone} done, ${taskProgress} active, ${taskBlocked} blocked)`,
411
+ `${chalk.bold(String(memEntries))} memories | Qdrant: ${qdrantStatus === 'Running' ? chalk.green('ON') : chalk.dim('OFF')}`,
412
+ `${patternCount} patterns | ${skillNames.length} skills | ${droidNames.length} droids`,
413
+ ];
414
+ for (const line of box('UAP Session', summaryLines, { borderColor: chalk.cyan })) {
415
+ console.log(` ${line}`);
416
+ }
417
+ console.log('');
418
+ // Sandbox grants (the ADR 0011-named surface): what extra
419
+ // writable holes are open, for which sessions, and whether the consent
420
+ // ledger verifies against its out-of-checkout trust anchor. Grants are
421
+ // SESSION authority, so this renders in the session view (the review
422
+ // caught an accidental placement in the models view). ⚠ lines are
423
+ // warnings — color them red.
424
+ const grantsSection = sandboxGrantsLines(process.cwd());
425
+ for (const l of grantsSection.lines)
426
+ console.log(l.trimStart().startsWith('⚠') ? chalk.red(l) : l);
427
+ console.log('');
428
+ }
429
+ catch (error) {
430
+ spinner.fail('Failed to load session dashboard');
431
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)));
432
+ }
433
+ }
434
+ export function compactSessionSummary() {
435
+ const cwd = process.cwd();
436
+ const memDbPath = join(cwd, 'agents/data/memory/short_term.db');
437
+ const coordDbPath = join(cwd, 'agents/data/coordination/coordination.db');
438
+ const taskDbPath = join(cwd, '.uap/tasks/tasks.db');
439
+ // Version
440
+ let version = '?';
441
+ try {
442
+ const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8'));
443
+ version = pkg.version || '?';
444
+ }
445
+ catch {
446
+ /* ignore */
447
+ }
448
+ // Git
449
+ let gitBranch = '?';
450
+ let gitDirty = 0;
451
+ try {
452
+ gitBranch = execSync('git branch --show-current', {
453
+ encoding: 'utf-8',
454
+ cwd,
455
+ stdio: ['pipe', 'pipe', 'pipe'],
456
+ }).trim();
457
+ gitDirty = execSync('git status --porcelain', {
458
+ encoding: 'utf-8',
459
+ cwd,
460
+ stdio: ['pipe', 'pipe', 'pipe'],
461
+ })
462
+ .trim()
463
+ .split('\n')
464
+ .filter(Boolean).length;
465
+ }
466
+ catch {
467
+ /* ignore */
468
+ }
469
+ // Tasks
470
+ let taskTotal = 0;
471
+ let taskDone = 0;
472
+ let taskProgress = 0;
473
+ let taskBlocked = 0;
474
+ let taskOpen = 0;
475
+ if (existsSync(taskDbPath)) {
476
+ try {
477
+ const db = new Database(taskDbPath, { readonly: true });
478
+ taskTotal = db.prepare('SELECT COUNT(*) as c FROM tasks').get().c;
479
+ taskDone = db
480
+ .prepare("SELECT COUNT(*) as c FROM tasks WHERE status='done' OR status='wont_do'")
481
+ .get().c;
482
+ taskProgress = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='in_progress'").get().c;
483
+ taskBlocked = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='blocked'").get().c;
484
+ taskOpen = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='open'").get().c;
485
+ db.close();
486
+ }
487
+ catch {
488
+ /* ignore */
489
+ }
490
+ }
491
+ // Memory
492
+ let memEntries = 0;
493
+ if (existsSync(memDbPath)) {
494
+ try {
495
+ const db = new Database(memDbPath, { readonly: true });
496
+ memEntries = db.prepare('SELECT COUNT(*) as c FROM memories').get().c;
497
+ db.close();
498
+ }
499
+ catch {
500
+ /* ignore */
501
+ }
502
+ }
503
+ // Agents
504
+ let activeAgents = 0;
505
+ if (existsSync(coordDbPath)) {
506
+ try {
507
+ const db = new Database(coordDbPath, { readonly: true });
508
+ activeAgents = db.prepare("SELECT COUNT(*) as c FROM agent_registry WHERE status='active'").get().c;
509
+ db.close();
510
+ }
511
+ catch {
512
+ /* ignore */
513
+ }
514
+ }
515
+ // Qdrant
516
+ let qdrantOn = false;
517
+ try {
518
+ const out = execSync('docker ps --filter name=qdrant --format "{{.Status}}"', {
519
+ encoding: 'utf-8',
520
+ stdio: ['pipe', 'pipe', 'pipe'],
521
+ }).trim();
522
+ qdrantOn = !!out;
523
+ }
524
+ catch {
525
+ /* ignore */
526
+ }
527
+ // Patterns, Skills, Droids counts
528
+ let patternCount = 0;
529
+ let skillCount = 0;
530
+ let droidCount = 0;
531
+ try {
532
+ const pi = join(cwd, '.factory/patterns/index.json');
533
+ if (existsSync(pi)) {
534
+ patternCount = JSON.parse(readFileSync(pi, 'utf-8')).patterns?.length || 0;
535
+ }
536
+ }
537
+ catch {
538
+ /* ignore */
539
+ }
540
+ for (const dir of [join(cwd, '.claude/skills'), join(cwd, '.factory/skills')]) {
541
+ if (existsSync(dir)) {
542
+ try {
543
+ skillCount += readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync(join(dir, e.name, 'SKILL.md'))).length;
544
+ }
545
+ catch {
546
+ /* ignore */
547
+ }
548
+ }
549
+ }
550
+ const dd = join(cwd, '.factory/droids');
551
+ if (existsSync(dd)) {
552
+ try {
553
+ droidCount = readdirSync(dd).filter((f) => f.endsWith('.md') && !f.startsWith('test-droid-')).length;
554
+ }
555
+ catch {
556
+ /* ignore */
557
+ }
558
+ }
559
+ // Build task progress bar
560
+ const barW = 20;
561
+ const pct = taskTotal > 0 ? Math.round((taskDone / taskTotal) * 100) : 0;
562
+ const filledW = taskTotal > 0 ? Math.round((taskDone / taskTotal) * barW) : 0;
563
+ const pctColor = pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.white;
564
+ const taskBar = chalk.green('\u2588'.repeat(filledW)) + chalk.dim('\u2591'.repeat(barW - filledW));
565
+ // Render compact box
566
+ const W = 58;
567
+ // Calculate header content width accurately (without ANSI codes)
568
+ const headerText = ` UAP v${version} on ${gitBranch}${gitDirty > 0 ? ' *' : ''}`;
569
+ const headerPad = Math.max(0, W - headerText.length);
570
+ console.log('');
571
+ console.log(chalk.cyan(` \u256D${'─'.repeat(W)}\u256E`));
572
+ console.log(chalk.cyan(` │`) +
573
+ chalk.bold.white(` UAP v${version}`) +
574
+ chalk.dim(` on ${gitBranch}${gitDirty > 0 ? ' *' : ''}`) +
575
+ ' '.repeat(headerPad) +
576
+ chalk.cyan(`│`));
577
+ console.log(chalk.cyan(` ├${'─'.repeat(W)}┤`));
578
+ // Task line
579
+ if (taskTotal > 0) {
580
+ const taskLine = ` ${taskBar} ${pctColor(pct + '%')} ${chalk.dim(`${taskDone}/${taskTotal}`)} ${chalk.green(taskDone + '\u2713')} ${chalk.cyan(taskProgress + '\u25D0')} ${taskBlocked > 0 ? chalk.red(taskBlocked + '\u2744') + ' ' : ''}${chalk.dim(taskOpen + '\u25CB')}`;
581
+ // Use raw length estimation for padding
582
+ const rawLen = ` ${'█'.repeat(barW)} ${pct}% ${taskDone}/${taskTotal} ${taskDone}✓ ${taskProgress}◐ ${taskBlocked > 0 ? taskBlocked + '❄ ' : ''}${taskOpen}○`;
583
+ console.log(chalk.cyan(` │`) + taskLine + ' '.repeat(Math.max(0, W - rawLen.length)) + chalk.cyan(`│`));
584
+ }
585
+ else {
586
+ console.log(chalk.cyan(` │`) + chalk.dim(' No tasks tracked') + ' '.repeat(W - 18) + chalk.cyan(`│`));
587
+ }
588
+ // Memory + Qdrant line
589
+ const memLine = ` Mem: ${memEntries} entries Qdrant: ${qdrantOn ? 'ON' : 'OFF'} Agents: ${activeAgents}`;
590
+ console.log(chalk.cyan(` │`) +
591
+ (qdrantOn ? chalk.white(memLine) : chalk.dim(memLine)) +
592
+ ' '.repeat(Math.max(0, W - memLine.length)) +
593
+ chalk.cyan(`│`));
594
+ // Intelligence line
595
+ const intLine = ` ${patternCount}P ${skillCount}S ${droidCount}D L1:ON L2:ON L3:${qdrantOn ? 'ON' : '?'} L4:ON`;
596
+ console.log(chalk.cyan(` │`) +
597
+ chalk.dim(intLine) +
598
+ ' '.repeat(Math.max(0, W - intLine.length)) +
599
+ chalk.cyan(`│`));
600
+ console.log(chalk.cyan(` \u2570${'─'.repeat(W)}\u256F`));
601
+ console.log('');
602
+ }
603
+ // ==================== Real-Time Benchmark Dashboard ====================
604
+ /**
605
+ * Show a real-time benchmark dashboard with memory, model routing,
606
+ * deploy batch, and pattern success stats from local databases.
607
+ *
608
+ * Uses simple ASCII formatting with no external dependencies beyond chalk.
609
+ */
610
+ export function showDashboard() {
611
+ const cwd = process.cwd();
612
+ // Database paths
613
+ const shortTermDbPath = join(cwd, 'agents/data/memory/short_term.db');
614
+ const coordDbPath = join(cwd, 'agents/data/coordination/coordination.db');
615
+ const modelDbPath = join(cwd, 'agents/data/memory/model_fingerprints.db');
616
+ const patternIndexPath = join(cwd, '.factory/patterns/index.json');
617
+ const W = 60;
618
+ const sep = '─'.repeat(W);
619
+ console.log('');
620
+ console.log(chalk.bold.cyan(' Real-Time Benchmark Dashboard'));
621
+ console.log(` ${sep}`);
622
+ console.log('');
623
+ // ── Memory Stats ──
624
+ console.log(chalk.bold(' Memory Stats'));
625
+ console.log(` ${chalk.dim('─'.repeat(W - 2))}`);
626
+ let shortTermCount = 0;
627
+ let sessionCount = 0;
628
+ let entityCount = 0;
629
+ let relationCount = 0;
630
+ let shortTermSizeKB = 0;
631
+ if (existsSync(shortTermDbPath)) {
632
+ try {
633
+ const db = new Database(shortTermDbPath, { readonly: true });
634
+ shortTermSizeKB = Math.round(statSync(shortTermDbPath).size / 1024);
635
+ shortTermCount = db.prepare('SELECT COUNT(*) as c FROM memories').get().c;
636
+ // Session memories
637
+ const hasSessions = db
638
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='session_memories'")
639
+ .all();
640
+ if (hasSessions.length > 0) {
641
+ sessionCount = db.prepare('SELECT COUNT(*) as c FROM session_memories').get().c;
642
+ }
643
+ // Knowledge graph entities
644
+ const hasEntities = db
645
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='entities'")
646
+ .all();
647
+ if (hasEntities.length > 0) {
648
+ entityCount = db.prepare('SELECT COUNT(*) as c FROM entities').get().c;
649
+ }
650
+ const hasRelations = db
651
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='relationships'")
652
+ .all();
653
+ if (hasRelations.length > 0) {
654
+ relationCount = db.prepare('SELECT COUNT(*) as c FROM relationships').get().c;
655
+ }
656
+ db.close();
657
+ }
658
+ catch {
659
+ /* ignore db errors */
660
+ }
661
+ }
662
+ const memRows = [
663
+ ['Short-term memories', String(shortTermCount)],
664
+ ['Session memories', String(sessionCount)],
665
+ ['KG entities', String(entityCount)],
666
+ ['KG relationships', String(relationCount)],
667
+ ['DB size', `${shortTermSizeKB} KB`],
668
+ ];
669
+ for (const [label, value] of memRows) {
670
+ console.log(` ${chalk.white(label.padEnd(25))} ${chalk.bold(value)}`);
671
+ }
672
+ console.log('');
673
+ // ── Model Routing Stats ──
674
+ console.log(chalk.bold(' Model Routing Stats'));
675
+ console.log(` ${chalk.dim('─'.repeat(W - 2))}`);
676
+ if (existsSync(modelDbPath)) {
677
+ try {
678
+ const db = new Database(modelDbPath, { readonly: true });
679
+ const tables = db
680
+ .prepare("SELECT name FROM sqlite_master WHERE type='table'")
681
+ .all();
682
+ const tableNames = tables.map((t) => t.name);
683
+ if (tableNames.includes('model_fingerprints')) {
684
+ const fpCount = db.prepare('SELECT COUNT(*) as c FROM model_fingerprints').get().c;
685
+ console.log(` ${chalk.white('Fingerprints'.padEnd(25))} ${chalk.bold(String(fpCount))}`);
686
+ // Try to get distinct models
687
+ try {
688
+ const models = db
689
+ .prepare('SELECT DISTINCT model_id FROM model_fingerprints ORDER BY model_id LIMIT 10')
690
+ .all();
691
+ if (models.length > 0) {
692
+ console.log(` ${chalk.white('Tracked models'.padEnd(25))} ${chalk.bold(String(models.length))}`);
693
+ for (const m of models.slice(0, 5)) {
694
+ console.log(` ${chalk.dim('-')} ${m.model_id}`);
695
+ }
696
+ if (models.length > 5)
697
+ console.log(chalk.dim(` ... and ${models.length - 5} more`));
698
+ }
699
+ }
700
+ catch {
701
+ /* schema variation */
702
+ }
703
+ }
704
+ if (tableNames.includes('routing_decisions')) {
705
+ const routeCount = db.prepare('SELECT COUNT(*) as c FROM routing_decisions').get().c;
706
+ console.log(` ${chalk.white('Routing decisions'.padEnd(25))} ${chalk.bold(String(routeCount))}`);
707
+ }
708
+ db.close();
709
+ }
710
+ catch {
711
+ console.log(chalk.dim(' Could not read model fingerprints DB'));
712
+ }
713
+ }
714
+ else {
715
+ console.log(chalk.dim(' No model fingerprints DB found'));
716
+ }
717
+ console.log('');
718
+ // ── Deploy Batch Stats ──
719
+ console.log(chalk.bold(' Deploy Batch Stats'));
720
+ console.log(` ${chalk.dim('─'.repeat(W - 2))}`);
721
+ let deployPending = 0;
722
+ let deployCompleted = 0;
723
+ let deployFailed = 0;
724
+ let batchCount = 0;
725
+ if (existsSync(coordDbPath)) {
726
+ try {
727
+ const db = new Database(coordDbPath, { readonly: true });
728
+ const hasDQ = db
729
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='deploy_queue'")
730
+ .all();
731
+ if (hasDQ.length > 0) {
732
+ deployPending = db.prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='pending'").get().c;
733
+ deployCompleted = db
734
+ .prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='completed' OR status='batched'")
735
+ .get().c;
736
+ deployFailed = db.prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='failed'").get().c;
737
+ }
738
+ const hasDB = db
739
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='deploy_batches'")
740
+ .all();
741
+ if (hasDB.length > 0) {
742
+ batchCount = db.prepare('SELECT COUNT(*) as c FROM deploy_batches').get()
743
+ .c;
744
+ }
745
+ db.close();
746
+ }
747
+ catch {
748
+ /* ignore */
749
+ }
750
+ }
751
+ const deployTotal = deployPending + deployCompleted + deployFailed;
752
+ const deployRows = [
753
+ ['Pending', String(deployPending)],
754
+ ['Completed', String(deployCompleted)],
755
+ ['Failed', String(deployFailed)],
756
+ ['Total actions', String(deployTotal)],
757
+ ['Batches', String(batchCount)],
758
+ ];
759
+ for (const [label, value] of deployRows) {
760
+ const color = label === 'Failed' && deployFailed > 0
761
+ ? chalk.red
762
+ : label === 'Pending' && deployPending > 0
763
+ ? chalk.yellow
764
+ : chalk.white;
765
+ console.log(` ${color(label.padEnd(25))} ${chalk.bold(value)}`);
766
+ }
767
+ console.log('');
768
+ // ── Pattern Success Rates ──
769
+ console.log(chalk.bold(' Pattern Success Rates'));
770
+ console.log(` ${chalk.dim('─'.repeat(W - 2))}`);
771
+ if (existsSync(patternIndexPath)) {
772
+ try {
773
+ const raw = readFileSync(patternIndexPath, 'utf-8');
774
+ const data = JSON.parse(raw);
775
+ const patterns = data.patterns || [];
776
+ const active = patterns.filter((p) => p.active !== false);
777
+ const withRates = active.filter((p) => typeof p.successRate === 'number');
778
+ console.log(` ${chalk.white('Total patterns'.padEnd(25))} ${chalk.bold(String(patterns.length))}`);
779
+ console.log(` ${chalk.white('Active'.padEnd(25))} ${chalk.bold(String(active.length))}`);
780
+ if (withRates.length > 0) {
781
+ const avgRate = withRates.reduce((sum, p) => sum + (p.successRate || 0), 0) / withRates.length;
782
+ console.log(` ${chalk.white('Avg success rate'.padEnd(25))} ${chalk.bold(avgRate.toFixed(1) + '%')}`);
783
+ console.log('');
784
+ // Show top patterns by success rate
785
+ const sorted = [...withRates].sort((a, b) => (b.successRate || 0) - (a.successRate || 0));
786
+ for (const p of sorted.slice(0, 8)) {
787
+ const name = p.title || p.name || p.abbreviation || (p.id != null ? `P${p.id}` : '?');
788
+ const rate = p.successRate || 0;
789
+ const barLen = Math.round(rate / 5);
790
+ const bar = chalk.green('\u2588'.repeat(barLen)) + chalk.dim('\u2591'.repeat(20 - barLen));
791
+ console.log(` ${name.slice(0, 22).padEnd(22)} ${bar} ${rate.toFixed(0)}%`);
792
+ }
793
+ if (sorted.length > 8) {
794
+ console.log(chalk.dim(` ... and ${sorted.length - 8} more`));
795
+ }
796
+ }
797
+ else {
798
+ console.log(chalk.dim(' No success rate data tracked'));
799
+ }
800
+ }
801
+ catch {
802
+ console.log(chalk.dim(' Could not parse patterns index'));
803
+ }
804
+ }
805
+ else {
806
+ console.log(chalk.dim(' No patterns index found'));
807
+ }
808
+ console.log('');
809
+ console.log(` ${sep}`);
810
+ console.log(chalk.dim(' Run: uap dashboard overview for full project dashboard'));
811
+ console.log(chalk.dim(' Run: uap dashboard session for session context'));
812
+ console.log('');
813
+ }
814
+ //# sourceMappingURL=dashboard-session.js.map