@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
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
- import { existsSync, statSync, readFileSync, readdirSync } from 'fs';
3
+ import { existsSync, statSync } from 'fs';
4
4
  import { join } from 'path';
5
5
  import { execSync } from 'child_process';
6
6
  import Database from 'better-sqlite3';
@@ -16,6 +16,8 @@ import { ModelRouter } from '../models/router.js';
16
16
  import { ModelPresets } from '../models/types.js';
17
17
  import { getModelAnalytics } from '../models/analytics.js';
18
18
  import { detectExecutionProfile } from '../models/execution-profiles.js';
19
+ import { computeMeasuredPace } from './dashboard-pace.js';
20
+ import { showDashboard, showSessionDashboard } from './dashboard-session.js';
19
21
  export async function dashboardCommand(action, options = {}) {
20
22
  switch (action) {
21
23
  case 'overview':
@@ -104,35 +106,40 @@ async function exportDashboard(options) {
104
106
  }
105
107
  async function showHistory(_options) {
106
108
  const cwd = process.cwd();
107
- const snapshotDbPath = join(cwd, 'agents', 'data', 'memory', 'session_snapshots.db');
108
- if (!existsSync(snapshotDbPath)) {
109
- console.log(chalk.dim('No session history found. Snapshots are saved automatically.'));
109
+ // History lives in telemetry.db's session_history table (written by the
110
+ // dashboard on each refresh). The old path read session_snapshots.db — a
111
+ // table nothing writes, so the command always reported "no history" (dash
112
+ // audit).
113
+ const telemetryDbPath = join(cwd, 'agents', 'data', 'memory', 'telemetry.db');
114
+ if (!existsSync(telemetryDbPath)) {
115
+ console.log(chalk.dim('No session history found. Start the dashboard to record sessions.'));
110
116
  return;
111
117
  }
112
118
  try {
113
- const db = new Database(snapshotDbPath, { readonly: true });
119
+ const db = new Database(telemetryDbPath, { readonly: true });
114
120
  const rows = db
115
- .prepare('SELECT id, timestamp, duration_ms, total_cost, tasks_completed, models_used FROM session_snapshots ORDER BY timestamp DESC LIMIT 20')
121
+ .prepare(`SELECT session_id, status, started_at, duration_ms, total_cost, task_count, model
122
+ FROM session_history ORDER BY started_at DESC LIMIT 20`)
116
123
  .all();
117
124
  db.close();
118
125
  if (rows.length === 0) {
119
- console.log(chalk.dim('No session snapshots recorded yet.'));
126
+ console.log(chalk.dim('No session history recorded yet.'));
120
127
  return;
121
128
  }
122
129
  console.log('');
123
130
  console.log(chalk.bold.cyan(' Session History'));
124
131
  console.log(divider(70));
125
132
  console.log('');
126
- console.log(` ${'Timestamp'.padEnd(22)} ${'Duration'.padEnd(12)} ${'Cost'.padEnd(10)} ${'Tasks'.padEnd(8)} Models`);
133
+ console.log(` ${'Started'.padEnd(22)} ${'Duration'.padEnd(12)} ${'Cost'.padEnd(10)} ${'Tasks'.padEnd(8)} ${'Status'.padEnd(8)} Model`);
127
134
  console.log(` ${'─'.repeat(22)} ${'─'.repeat(12)} ${'─'.repeat(10)} ${'─'.repeat(8)} ${'─'.repeat(16)}`);
128
135
  for (const row of rows) {
129
- const ts = (row.timestamp || '').slice(0, 19);
136
+ const ts = (row.started_at || '').slice(0, 19);
130
137
  const dur = row.duration_ms ? `${Math.round(row.duration_ms / 1000)}s` : '?';
131
138
  const cost = row.total_cost ? `$${row.total_cost.toFixed(4)}` : '$0';
132
- const tasks = String(row.tasks_completed || 0);
133
- const parsedModels = row.models_used ? JSON.parse(row.models_used) : null;
134
- const models = Array.isArray(parsedModels) ? parsedModels.join(', ') : '?';
135
- console.log(` ${ts.padEnd(22)} ${dur.padEnd(12)} ${cost.padEnd(10)} ${tasks.padEnd(8)} ${models}`);
139
+ const tasks = String(row.task_count || 0);
140
+ const status = String(row.status || '-');
141
+ const model = String(row.model || 'unknown');
142
+ console.log(` ${ts.padEnd(22)} ${dur.padEnd(12)} ${cost.padEnd(10)} ${tasks.padEnd(8)} ${status.padEnd(8)} ${model}`);
136
143
  }
137
144
  console.log('');
138
145
  }
@@ -840,406 +847,6 @@ async function showStatsDashboard(_options) {
840
847
  console.log('');
841
848
  }
842
849
  // ==================== Session Dashboard ====================
843
- async function showSessionDashboard(options) {
844
- if (options.compact) {
845
- compactSessionSummary();
846
- return;
847
- }
848
- const spinner = ora('Loading session dashboard...').start();
849
- try {
850
- const cwd = process.cwd();
851
- // ── Gather data from all sources ──
852
- const memDbPath = join(cwd, 'agents/data/memory/short_term.db');
853
- const coordDbPath = join(cwd, 'agents/data/coordination/coordination.db');
854
- const taskDbPath = join(cwd, '.uap/tasks/tasks.db');
855
- // Package version
856
- let version = '?';
857
- try {
858
- const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8'));
859
- version = pkg.version || '?';
860
- }
861
- catch {
862
- /* ignore */
863
- }
864
- // Git info
865
- let gitBranch = '?';
866
- let gitDirty = 0;
867
- let gitAhead = 0;
868
- let lastCommit = '';
869
- try {
870
- gitBranch = execSync('git branch --show-current', {
871
- encoding: 'utf-8',
872
- cwd,
873
- stdio: ['pipe', 'pipe', 'pipe'],
874
- }).trim();
875
- gitDirty = execSync('git status --porcelain', {
876
- encoding: 'utf-8',
877
- cwd,
878
- stdio: ['pipe', 'pipe', 'pipe'],
879
- })
880
- .trim()
881
- .split('\n')
882
- .filter(Boolean).length;
883
- gitAhead =
884
- parseInt(execSync('git rev-list --count @{u}..HEAD 2>/dev/null || echo 0', {
885
- encoding: 'utf-8',
886
- cwd,
887
- stdio: ['pipe', 'pipe', 'pipe'],
888
- }).trim(), 10) || 0;
889
- lastCommit = execSync('git log -1 --format="%h %s" 2>/dev/null', {
890
- encoding: 'utf-8',
891
- cwd,
892
- stdio: ['pipe', 'pipe', 'pipe'],
893
- }).trim();
894
- }
895
- catch {
896
- /* ignore */
897
- }
898
- // Memory stats
899
- let memEntries = 0;
900
- let memSizeKB = 0;
901
- let recentMemories = [];
902
- if (existsSync(memDbPath)) {
903
- try {
904
- const memDb = new Database(memDbPath, { readonly: true });
905
- memEntries = memDb.prepare('SELECT COUNT(*) as c FROM memories').get().c;
906
- memSizeKB = Math.round(statSync(memDbPath).size / 1024);
907
- recentMemories = memDb
908
- .prepare("SELECT type, substr(content, 1, 80) as content FROM memories WHERE timestamp >= datetime('now', '-1 day') ORDER BY id DESC LIMIT 5")
909
- .all();
910
- memDb.close();
911
- }
912
- catch {
913
- /* ignore */
914
- }
915
- }
916
- // Session memories (open loops)
917
- let sessionMemCount = 0;
918
- let openLoops = [];
919
- if (existsSync(memDbPath)) {
920
- try {
921
- const memDb = new Database(memDbPath, { readonly: true });
922
- const tables = memDb
923
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='session_memories'")
924
- .all();
925
- if (tables.length > 0) {
926
- sessionMemCount = memDb.prepare('SELECT COUNT(*) as c FROM session_memories').get().c;
927
- openLoops = memDb
928
- .prepare("SELECT content FROM session_memories WHERE type IN ('action','goal','decision') AND importance >= 7 ORDER BY id DESC LIMIT 5")
929
- .all();
930
- }
931
- memDb.close();
932
- }
933
- catch {
934
- /* ignore */
935
- }
936
- }
937
- // Coordination stats
938
- let activeAgents = 0;
939
- let activeClaims = 0;
940
- let pendingDeploys = 0;
941
- if (existsSync(coordDbPath)) {
942
- try {
943
- const coordDb = new Database(coordDbPath, { readonly: true });
944
- activeAgents = coordDb
945
- .prepare("SELECT COUNT(*) as c FROM agent_registry WHERE status='active'")
946
- .get().c;
947
- activeClaims = coordDb.prepare('SELECT COUNT(*) as c FROM work_claims').get().c;
948
- pendingDeploys = coordDb
949
- .prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='pending'")
950
- .get().c;
951
- coordDb.close();
952
- }
953
- catch {
954
- /* ignore */
955
- }
956
- }
957
- // Task stats
958
- let taskTotal = 0;
959
- let taskOpen = 0;
960
- let taskProgress = 0;
961
- let taskBlocked = 0;
962
- let taskDone = 0;
963
- let activeTasks = [];
964
- if (existsSync(taskDbPath)) {
965
- try {
966
- const taskDb = new Database(taskDbPath, { readonly: true });
967
- taskTotal = taskDb.prepare('SELECT COUNT(*) as c FROM tasks').get().c;
968
- taskOpen = taskDb.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='open'").get().c;
969
- taskProgress = taskDb.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='in_progress'").get().c;
970
- taskBlocked = taskDb.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='blocked'").get().c;
971
- taskDone = taskDb
972
- .prepare("SELECT COUNT(*) as c FROM tasks WHERE status='done' OR status='wont_do'")
973
- .get().c;
974
- activeTasks = taskDb
975
- .prepare("SELECT id, title, priority FROM tasks WHERE status='in_progress' ORDER BY priority ASC LIMIT 5")
976
- .all();
977
- taskDb.close();
978
- }
979
- catch {
980
- /* ignore */
981
- }
982
- }
983
- // Qdrant status
984
- let qdrantStatus = 'Stopped';
985
- let qdrantUptime = '';
986
- try {
987
- const dockerOut = execSync('docker ps --filter name=qdrant --format "{{.Status}}"', {
988
- encoding: 'utf-8',
989
- stdio: ['pipe', 'pipe', 'pipe'],
990
- }).trim();
991
- if (dockerOut) {
992
- qdrantStatus = 'Running';
993
- qdrantUptime = dockerOut;
994
- }
995
- }
996
- catch {
997
- /* ignore */
998
- }
999
- // Patterns
1000
- let patternCount = 0;
1001
- let patternNames = [];
1002
- const patternIndexPath = join(cwd, '.factory/patterns/index.json');
1003
- if (existsSync(patternIndexPath)) {
1004
- try {
1005
- const patternData = JSON.parse(readFileSync(patternIndexPath, 'utf-8'));
1006
- const patterns = patternData.patterns || [];
1007
- patternCount = patterns.length;
1008
- patternNames = patterns
1009
- .slice(0, 5)
1010
- .map((p) => p.title || p.name || p.abbreviation || (p.id != null ? `P${p.id}` : '?'));
1011
- }
1012
- catch {
1013
- /* ignore */
1014
- }
1015
- }
1016
- // Skills
1017
- let skillNames = [];
1018
- const skillDirs = [join(cwd, '.claude/skills'), join(cwd, '.factory/skills')];
1019
- for (const dir of skillDirs) {
1020
- if (existsSync(dir)) {
1021
- try {
1022
- const entries = readdirSync(dir, { withFileTypes: true });
1023
- for (const entry of entries) {
1024
- if (entry.isDirectory() && existsSync(join(dir, entry.name, 'SKILL.md'))) {
1025
- skillNames.push(entry.name);
1026
- }
1027
- }
1028
- }
1029
- catch {
1030
- /* ignore */
1031
- }
1032
- }
1033
- }
1034
- // Droids
1035
- let droidNames = [];
1036
- const droidDir = join(cwd, '.factory/droids');
1037
- if (existsSync(droidDir)) {
1038
- try {
1039
- const entries = readdirSync(droidDir).filter((f) => f.endsWith('.md') && !f.startsWith('test-droid-'));
1040
- droidNames = entries.map((f) => f.replace('.md', ''));
1041
- }
1042
- catch {
1043
- /* ignore */
1044
- }
1045
- }
1046
- // Worktrees
1047
- let worktreeCount = 0;
1048
- const worktreeDir = join(cwd, '.worktrees');
1049
- if (existsSync(worktreeDir)) {
1050
- try {
1051
- worktreeCount = readdirSync(worktreeDir, { withFileTypes: true }).filter((e) => e.isDirectory()).length;
1052
- }
1053
- catch {
1054
- /* ignore */
1055
- }
1056
- }
1057
- spinner.stop();
1058
- // ── Render ──
1059
- console.log('');
1060
- console.log(chalk.bold.cyan(' UAP Session Dashboard'));
1061
- console.log(divider(62));
1062
- console.log('');
1063
- // System info
1064
- console.log(sectionHeader('System'));
1065
- console.log('');
1066
- for (const line of keyValue([
1067
- ['Version', `v${version}`],
1068
- [
1069
- 'Branch',
1070
- `${gitBranch}${gitDirty > 0 ? chalk.yellow(` (${gitDirty} uncommitted)`) : chalk.green(' (clean)')}`,
1071
- ],
1072
- ['Ahead', gitAhead > 0 ? chalk.yellow(`${gitAhead} commits`) : chalk.dim('up to date')],
1073
- ['Last Commit', lastCommit.length > 55 ? lastCommit.slice(0, 55) + '...' : lastCommit],
1074
- ]))
1075
- console.log(line);
1076
- console.log('');
1077
- // Task progress
1078
- console.log(sectionHeader('Tasks'));
1079
- console.log('');
1080
- if (taskTotal > 0) {
1081
- const pct = Math.round((taskDone / taskTotal) * 100);
1082
- console.log(` ${progressBar(taskDone, taskTotal, 30, {
1083
- label: 'Progress',
1084
- filled: pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.white,
1085
- })}`);
1086
- console.log('');
1087
- const segments = [
1088
- { value: taskDone, color: chalk.green, label: 'Done' },
1089
- { value: taskProgress, color: chalk.cyan, label: 'Active' },
1090
- { value: taskOpen, color: chalk.white, label: 'Open' },
1091
- { value: taskBlocked, color: chalk.red, label: 'Blocked' },
1092
- ];
1093
- console.log(` ${stackedBar(segments, taskTotal, 40)}`);
1094
- console.log(` ${stackedBarLegend(segments)}`);
1095
- console.log('');
1096
- if (activeTasks.length > 0) {
1097
- console.log(chalk.dim(' Active:'));
1098
- for (const t of activeTasks) {
1099
- console.log(` ${chalk.cyan(t.id)} P${t.priority} ${t.title.slice(0, 45)}${t.title.length > 45 ? '...' : ''}`);
1100
- }
1101
- console.log('');
1102
- }
1103
- }
1104
- else {
1105
- console.log(chalk.dim(' No tasks tracked. Create with: uap task create --title "..."'));
1106
- console.log('');
1107
- }
1108
- // Memory layers
1109
- console.log(sectionHeader('Memory System'));
1110
- console.log('');
1111
- const memItems = [
1112
- {
1113
- text: `L1 Working Memory: ${chalk.bold(memEntries + ' entries')} ${chalk.dim(`(${memSizeKB} KB)`)}`,
1114
- status: existsSync(memDbPath) ? 'ok' : 'warn',
1115
- },
1116
- {
1117
- text: `L2 Session Memory: ${chalk.bold(sessionMemCount + ' entries')} ${openLoops.length > 0 ? chalk.yellow(`(${openLoops.length} open loops)`) : ''}`,
1118
- status: sessionMemCount > 0 ? 'ok' : 'info',
1119
- },
1120
- {
1121
- text: `L3 Semantic Memory: ${qdrantStatus === 'Running' ? chalk.green('Qdrant ' + qdrantUptime) : chalk.dim('Qdrant stopped')}`,
1122
- status: qdrantStatus === 'Running' ? 'ok' : 'warn',
1123
- },
1124
- {
1125
- text: `L4 Knowledge Graph: ${existsSync(memDbPath) ? chalk.green('Active') : chalk.dim('Not initialized')}`,
1126
- status: existsSync(memDbPath) ? 'ok' : 'warn',
1127
- },
1128
- ];
1129
- for (const line of bulletList(memItems))
1130
- console.log(line);
1131
- if (recentMemories.length > 0) {
1132
- console.log('');
1133
- console.log(chalk.dim(' Recent memories (24h):'));
1134
- for (const mem of recentMemories.slice(0, 3)) {
1135
- console.log(` ${chalk.dim((mem.type || 'unknown') + ':')} ${mem.content || ''}`);
1136
- }
1137
- }
1138
- console.log('');
1139
- // Coordination
1140
- console.log(sectionHeader('Coordination'));
1141
- console.log('');
1142
- for (const line of keyValue([
1143
- ['Active Agents', activeAgents > 0 ? chalk.green(String(activeAgents)) : chalk.dim('0')],
1144
- ['Resource Claims', activeClaims > 0 ? chalk.yellow(String(activeClaims)) : chalk.dim('0')],
1145
- [
1146
- 'Pending Deploys',
1147
- pendingDeploys > 0 ? chalk.yellow(String(pendingDeploys)) : chalk.dim('0'),
1148
- ],
1149
- ['Worktrees', worktreeCount > 0 ? chalk.yellow(String(worktreeCount)) : chalk.dim('0')],
1150
- ]))
1151
- console.log(line);
1152
- console.log('');
1153
- // Patterns & Skills
1154
- console.log(sectionHeader('Intelligence'));
1155
- console.log('');
1156
- if (patternCount > 0) {
1157
- console.log(` ${chalk.blue('Patterns')} ${chalk.bold(String(patternCount))} loaded`);
1158
- if (patternNames.length > 0) {
1159
- for (const name of patternNames) {
1160
- console.log(` ${chalk.dim('-')} ${name}`);
1161
- }
1162
- if (patternCount > 5)
1163
- console.log(chalk.dim(` ... and ${patternCount - 5} more`));
1164
- }
1165
- }
1166
- else {
1167
- console.log(chalk.dim(' Patterns: None loaded'));
1168
- }
1169
- console.log('');
1170
- if (skillNames.length > 0) {
1171
- console.log(` ${chalk.green('Skills')} ${chalk.bold(String(skillNames.length))} available`);
1172
- for (const name of skillNames) {
1173
- console.log(` ${chalk.dim('-')} ${name}`);
1174
- }
1175
- }
1176
- else {
1177
- console.log(chalk.dim(' Skills: None configured'));
1178
- }
1179
- console.log('');
1180
- if (droidNames.length > 0) {
1181
- console.log(` ${chalk.magenta('Droids')} ${chalk.bold(String(droidNames.length))} registered`);
1182
- for (const name of droidNames.slice(0, 5)) {
1183
- console.log(` ${chalk.dim('-')} ${name}`);
1184
- }
1185
- if (droidNames.length > 5)
1186
- console.log(chalk.dim(` ... and ${droidNames.length - 5} more`));
1187
- }
1188
- else {
1189
- console.log(chalk.dim(' Droids: None registered'));
1190
- }
1191
- console.log('');
1192
- // Policies (DB-driven)
1193
- console.log(sectionHeader('Policies'));
1194
- console.log('');
1195
- try {
1196
- const policyMgr = getPolicyMemoryManager();
1197
- const allPolicies = await policyMgr.getAllPolicies();
1198
- if (allPolicies.length > 0) {
1199
- for (const policy of allPolicies) {
1200
- const stageLabel = policy.enforcementStage || 'pre-exec';
1201
- const levelColor = policy.level === 'REQUIRED'
1202
- ? chalk.red
1203
- : policy.level === 'RECOMMENDED'
1204
- ? chalk.yellow
1205
- : chalk.dim;
1206
- console.log(` ${levelColor(policy.level.padEnd(14))} ${policy.name.slice(0, 30).padEnd(30)} ${chalk.dim(stageLabel)}`);
1207
- }
1208
- }
1209
- else {
1210
- console.log(chalk.dim(' No policies configured. Add with: uap policy add -f <file>'));
1211
- }
1212
- }
1213
- catch {
1214
- console.log(chalk.dim(' Policy DB not initialized'));
1215
- }
1216
- console.log('');
1217
- // Open loops
1218
- if (openLoops.length > 0) {
1219
- console.log(sectionHeader('Open Loops'));
1220
- console.log('');
1221
- for (const loop of openLoops) {
1222
- console.log(` ${chalk.yellow('>')} ${loop.content.slice(0, 70)}${loop.content.length > 70 ? '...' : ''}`);
1223
- }
1224
- console.log('');
1225
- }
1226
- // Summary box
1227
- const summaryLines = [
1228
- `v${version} on ${chalk.cyan(gitBranch)}${gitDirty > 0 ? chalk.yellow(' *') : ''}`,
1229
- `${chalk.bold(String(taskTotal))} tasks (${taskDone} done, ${taskProgress} active, ${taskBlocked} blocked)`,
1230
- `${chalk.bold(String(memEntries))} memories | Qdrant: ${qdrantStatus === 'Running' ? chalk.green('ON') : chalk.dim('OFF')}`,
1231
- `${patternCount} patterns | ${skillNames.length} skills | ${droidNames.length} droids`,
1232
- ];
1233
- for (const line of box('UAP Session', summaryLines, { borderColor: chalk.cyan })) {
1234
- console.log(` ${line}`);
1235
- }
1236
- console.log('');
1237
- }
1238
- catch (error) {
1239
- spinner.fail('Failed to load session dashboard');
1240
- console.error(chalk.red(error instanceof Error ? error.message : String(error)));
1241
- }
1242
- }
1243
850
  async function showPoliciesDashboard(options) {
1244
851
  const spinner = ora('Loading policies dashboard...').start();
1245
852
  try {
@@ -1443,6 +1050,29 @@ async function showModelsDashboard(options) {
1443
1050
  console.log(chalk.dim(' No model usage recorded this session'));
1444
1051
  console.log('');
1445
1052
  }
1053
+ // Measured pace: the agent's own EWMA estimates per
1054
+ // (model, taskType, complexity) key — the operator's legibility surface
1055
+ // for "how long does this model actually take here". Only keys with a
1056
+ // real estimate get a row; the honest caveat (spread, n, failure rate)
1057
+ // travels with the number.
1058
+ const pace = await computeMeasuredPace(analytics);
1059
+ if (pace.rows.length > 0) {
1060
+ console.log(sectionHeader('Measured Pace (ETA — from this project\'s own history)'));
1061
+ console.log('');
1062
+ console.log(` ${'Model'.padEnd(14)} ${'Key'.padEnd(22)} ${'Per task'.padEnd(10)} ${'Spread'.padEnd(16)} ${'N'.padEnd(8)} Success`);
1063
+ console.log(` ${'─'.repeat(14)} ${'─'.repeat(22)} ${'─'.repeat(10)} ${'─'.repeat(16)} ${'─'.repeat(8)} ${'─'.repeat(7)}`);
1064
+ for (const r of pace.rows) {
1065
+ console.log(` ${chalk.cyan(r.modelId.padEnd(14))} ${r.key.padEnd(22)}` +
1066
+ ` ${r.perTask.padEnd(10)} ${r.spread.padEnd(16)} ${r.n.padEnd(8)} ${r.successPct}`);
1067
+ }
1068
+ if (pace.more > 0) {
1069
+ console.log(chalk.dim(` (… and ${pace.more} more key(s) with history beyond the top 8 by attempts)`));
1070
+ }
1071
+ if (pace.thin > 0) {
1072
+ console.log(chalk.dim(` (+${pace.thin} more key(s) with history too thin to estimate — needs ≥3 successes)`));
1073
+ }
1074
+ console.log('');
1075
+ }
1446
1076
  // Execution profile for executor
1447
1077
  const executorModel = roles.executor || 'qwen35-a3b';
1448
1078
  const executorApiModel = ModelPresets[executorModel]?.apiModel || executorModel;
@@ -1513,384 +1143,4 @@ async function showModelsDashboard(options) {
1513
1143
  * Used after task completion and before context compaction to give
1514
1144
  * the agent (and user) a quick snapshot of UAP operational state.
1515
1145
  */
1516
- export function compactSessionSummary() {
1517
- const cwd = process.cwd();
1518
- const memDbPath = join(cwd, 'agents/data/memory/short_term.db');
1519
- const coordDbPath = join(cwd, 'agents/data/coordination/coordination.db');
1520
- const taskDbPath = join(cwd, '.uap/tasks/tasks.db');
1521
- // Version
1522
- let version = '?';
1523
- try {
1524
- const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8'));
1525
- version = pkg.version || '?';
1526
- }
1527
- catch {
1528
- /* ignore */
1529
- }
1530
- // Git
1531
- let gitBranch = '?';
1532
- let gitDirty = 0;
1533
- try {
1534
- gitBranch = execSync('git branch --show-current', {
1535
- encoding: 'utf-8',
1536
- cwd,
1537
- stdio: ['pipe', 'pipe', 'pipe'],
1538
- }).trim();
1539
- gitDirty = execSync('git status --porcelain', {
1540
- encoding: 'utf-8',
1541
- cwd,
1542
- stdio: ['pipe', 'pipe', 'pipe'],
1543
- })
1544
- .trim()
1545
- .split('\n')
1546
- .filter(Boolean).length;
1547
- }
1548
- catch {
1549
- /* ignore */
1550
- }
1551
- // Tasks
1552
- let taskTotal = 0;
1553
- let taskDone = 0;
1554
- let taskProgress = 0;
1555
- let taskBlocked = 0;
1556
- let taskOpen = 0;
1557
- if (existsSync(taskDbPath)) {
1558
- try {
1559
- const db = new Database(taskDbPath, { readonly: true });
1560
- taskTotal = db.prepare('SELECT COUNT(*) as c FROM tasks').get().c;
1561
- taskDone = db
1562
- .prepare("SELECT COUNT(*) as c FROM tasks WHERE status='done' OR status='wont_do'")
1563
- .get().c;
1564
- taskProgress = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='in_progress'").get().c;
1565
- taskBlocked = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='blocked'").get().c;
1566
- taskOpen = db.prepare("SELECT COUNT(*) as c FROM tasks WHERE status='open'").get().c;
1567
- db.close();
1568
- }
1569
- catch {
1570
- /* ignore */
1571
- }
1572
- }
1573
- // Memory
1574
- let memEntries = 0;
1575
- if (existsSync(memDbPath)) {
1576
- try {
1577
- const db = new Database(memDbPath, { readonly: true });
1578
- memEntries = db.prepare('SELECT COUNT(*) as c FROM memories').get().c;
1579
- db.close();
1580
- }
1581
- catch {
1582
- /* ignore */
1583
- }
1584
- }
1585
- // Agents
1586
- let activeAgents = 0;
1587
- if (existsSync(coordDbPath)) {
1588
- try {
1589
- const db = new Database(coordDbPath, { readonly: true });
1590
- activeAgents = db.prepare("SELECT COUNT(*) as c FROM agent_registry WHERE status='active'").get().c;
1591
- db.close();
1592
- }
1593
- catch {
1594
- /* ignore */
1595
- }
1596
- }
1597
- // Qdrant
1598
- let qdrantOn = false;
1599
- try {
1600
- const out = execSync('docker ps --filter name=qdrant --format "{{.Status}}"', {
1601
- encoding: 'utf-8',
1602
- stdio: ['pipe', 'pipe', 'pipe'],
1603
- }).trim();
1604
- qdrantOn = !!out;
1605
- }
1606
- catch {
1607
- /* ignore */
1608
- }
1609
- // Patterns, Skills, Droids counts
1610
- let patternCount = 0;
1611
- let skillCount = 0;
1612
- let droidCount = 0;
1613
- try {
1614
- const pi = join(cwd, '.factory/patterns/index.json');
1615
- if (existsSync(pi)) {
1616
- patternCount = JSON.parse(readFileSync(pi, 'utf-8')).patterns?.length || 0;
1617
- }
1618
- }
1619
- catch {
1620
- /* ignore */
1621
- }
1622
- for (const dir of [join(cwd, '.claude/skills'), join(cwd, '.factory/skills')]) {
1623
- if (existsSync(dir)) {
1624
- try {
1625
- skillCount += readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync(join(dir, e.name, 'SKILL.md'))).length;
1626
- }
1627
- catch {
1628
- /* ignore */
1629
- }
1630
- }
1631
- }
1632
- const dd = join(cwd, '.factory/droids');
1633
- if (existsSync(dd)) {
1634
- try {
1635
- droidCount = readdirSync(dd).filter((f) => f.endsWith('.md') && !f.startsWith('test-droid-')).length;
1636
- }
1637
- catch {
1638
- /* ignore */
1639
- }
1640
- }
1641
- // Build task progress bar
1642
- const barW = 20;
1643
- const pct = taskTotal > 0 ? Math.round((taskDone / taskTotal) * 100) : 0;
1644
- const filledW = taskTotal > 0 ? Math.round((taskDone / taskTotal) * barW) : 0;
1645
- const pctColor = pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.white;
1646
- const taskBar = chalk.green('\u2588'.repeat(filledW)) + chalk.dim('\u2591'.repeat(barW - filledW));
1647
- // Render compact box
1648
- const W = 58;
1649
- // Calculate header content width accurately (without ANSI codes)
1650
- const headerText = ` UAP v${version} on ${gitBranch}${gitDirty > 0 ? ' *' : ''}`;
1651
- const headerPad = Math.max(0, W - headerText.length);
1652
- console.log('');
1653
- console.log(chalk.cyan(` \u256D${'─'.repeat(W)}\u256E`));
1654
- console.log(chalk.cyan(` │`) +
1655
- chalk.bold.white(` UAP v${version}`) +
1656
- chalk.dim(` on ${gitBranch}${gitDirty > 0 ? ' *' : ''}`) +
1657
- ' '.repeat(headerPad) +
1658
- chalk.cyan(`│`));
1659
- console.log(chalk.cyan(` ├${'─'.repeat(W)}┤`));
1660
- // Task line
1661
- if (taskTotal > 0) {
1662
- 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')}`;
1663
- // Use raw length estimation for padding
1664
- const rawLen = ` ${'█'.repeat(barW)} ${pct}% ${taskDone}/${taskTotal} ${taskDone}✓ ${taskProgress}◐ ${taskBlocked > 0 ? taskBlocked + '❄ ' : ''}${taskOpen}○`;
1665
- console.log(chalk.cyan(` │`) + taskLine + ' '.repeat(Math.max(0, W - rawLen.length)) + chalk.cyan(`│`));
1666
- }
1667
- else {
1668
- console.log(chalk.cyan(` │`) + chalk.dim(' No tasks tracked') + ' '.repeat(W - 18) + chalk.cyan(`│`));
1669
- }
1670
- // Memory + Qdrant line
1671
- const memLine = ` Mem: ${memEntries} entries Qdrant: ${qdrantOn ? 'ON' : 'OFF'} Agents: ${activeAgents}`;
1672
- console.log(chalk.cyan(` │`) +
1673
- (qdrantOn ? chalk.white(memLine) : chalk.dim(memLine)) +
1674
- ' '.repeat(Math.max(0, W - memLine.length)) +
1675
- chalk.cyan(`│`));
1676
- // Intelligence line
1677
- const intLine = ` ${patternCount}P ${skillCount}S ${droidCount}D L1:ON L2:ON L3:${qdrantOn ? 'ON' : '?'} L4:ON`;
1678
- console.log(chalk.cyan(` │`) +
1679
- chalk.dim(intLine) +
1680
- ' '.repeat(Math.max(0, W - intLine.length)) +
1681
- chalk.cyan(`│`));
1682
- console.log(chalk.cyan(` \u2570${'─'.repeat(W)}\u256F`));
1683
- console.log('');
1684
- }
1685
- // ==================== Real-Time Benchmark Dashboard ====================
1686
- /**
1687
- * Show a real-time benchmark dashboard with memory, model routing,
1688
- * deploy batch, and pattern success stats from local databases.
1689
- *
1690
- * Uses simple ASCII formatting with no external dependencies beyond chalk.
1691
- */
1692
- export function showDashboard() {
1693
- const cwd = process.cwd();
1694
- // Database paths
1695
- const shortTermDbPath = join(cwd, 'agents/data/memory/short_term.db');
1696
- const coordDbPath = join(cwd, 'agents/data/coordination/coordination.db');
1697
- const modelDbPath = join(cwd, 'agents/data/memory/model_fingerprints.db');
1698
- const patternIndexPath = join(cwd, '.factory/patterns/index.json');
1699
- const W = 60;
1700
- const sep = '─'.repeat(W);
1701
- console.log('');
1702
- console.log(chalk.bold.cyan(' Real-Time Benchmark Dashboard'));
1703
- console.log(` ${sep}`);
1704
- console.log('');
1705
- // ── Memory Stats ──
1706
- console.log(chalk.bold(' Memory Stats'));
1707
- console.log(` ${chalk.dim('─'.repeat(W - 2))}`);
1708
- let shortTermCount = 0;
1709
- let sessionCount = 0;
1710
- let entityCount = 0;
1711
- let relationCount = 0;
1712
- let shortTermSizeKB = 0;
1713
- if (existsSync(shortTermDbPath)) {
1714
- try {
1715
- const db = new Database(shortTermDbPath, { readonly: true });
1716
- shortTermSizeKB = Math.round(statSync(shortTermDbPath).size / 1024);
1717
- shortTermCount = db.prepare('SELECT COUNT(*) as c FROM memories').get().c;
1718
- // Session memories
1719
- const hasSessions = db
1720
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='session_memories'")
1721
- .all();
1722
- if (hasSessions.length > 0) {
1723
- sessionCount = db.prepare('SELECT COUNT(*) as c FROM session_memories').get().c;
1724
- }
1725
- // Knowledge graph entities
1726
- const hasEntities = db
1727
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='entities'")
1728
- .all();
1729
- if (hasEntities.length > 0) {
1730
- entityCount = db.prepare('SELECT COUNT(*) as c FROM entities').get().c;
1731
- }
1732
- const hasRelations = db
1733
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='relationships'")
1734
- .all();
1735
- if (hasRelations.length > 0) {
1736
- relationCount = db.prepare('SELECT COUNT(*) as c FROM relationships').get().c;
1737
- }
1738
- db.close();
1739
- }
1740
- catch {
1741
- /* ignore db errors */
1742
- }
1743
- }
1744
- const memRows = [
1745
- ['Short-term memories', String(shortTermCount)],
1746
- ['Session memories', String(sessionCount)],
1747
- ['KG entities', String(entityCount)],
1748
- ['KG relationships', String(relationCount)],
1749
- ['DB size', `${shortTermSizeKB} KB`],
1750
- ];
1751
- for (const [label, value] of memRows) {
1752
- console.log(` ${chalk.white(label.padEnd(25))} ${chalk.bold(value)}`);
1753
- }
1754
- console.log('');
1755
- // ── Model Routing Stats ──
1756
- console.log(chalk.bold(' Model Routing Stats'));
1757
- console.log(` ${chalk.dim('─'.repeat(W - 2))}`);
1758
- if (existsSync(modelDbPath)) {
1759
- try {
1760
- const db = new Database(modelDbPath, { readonly: true });
1761
- const tables = db
1762
- .prepare("SELECT name FROM sqlite_master WHERE type='table'")
1763
- .all();
1764
- const tableNames = tables.map((t) => t.name);
1765
- if (tableNames.includes('model_fingerprints')) {
1766
- const fpCount = db.prepare('SELECT COUNT(*) as c FROM model_fingerprints').get().c;
1767
- console.log(` ${chalk.white('Fingerprints'.padEnd(25))} ${chalk.bold(String(fpCount))}`);
1768
- // Try to get distinct models
1769
- try {
1770
- const models = db
1771
- .prepare('SELECT DISTINCT model_id FROM model_fingerprints ORDER BY model_id LIMIT 10')
1772
- .all();
1773
- if (models.length > 0) {
1774
- console.log(` ${chalk.white('Tracked models'.padEnd(25))} ${chalk.bold(String(models.length))}`);
1775
- for (const m of models.slice(0, 5)) {
1776
- console.log(` ${chalk.dim('-')} ${m.model_id}`);
1777
- }
1778
- if (models.length > 5)
1779
- console.log(chalk.dim(` ... and ${models.length - 5} more`));
1780
- }
1781
- }
1782
- catch {
1783
- /* schema variation */
1784
- }
1785
- }
1786
- if (tableNames.includes('routing_decisions')) {
1787
- const routeCount = db.prepare('SELECT COUNT(*) as c FROM routing_decisions').get().c;
1788
- console.log(` ${chalk.white('Routing decisions'.padEnd(25))} ${chalk.bold(String(routeCount))}`);
1789
- }
1790
- db.close();
1791
- }
1792
- catch {
1793
- console.log(chalk.dim(' Could not read model fingerprints DB'));
1794
- }
1795
- }
1796
- else {
1797
- console.log(chalk.dim(' No model fingerprints DB found'));
1798
- }
1799
- console.log('');
1800
- // ── Deploy Batch Stats ──
1801
- console.log(chalk.bold(' Deploy Batch Stats'));
1802
- console.log(` ${chalk.dim('─'.repeat(W - 2))}`);
1803
- let deployPending = 0;
1804
- let deployCompleted = 0;
1805
- let deployFailed = 0;
1806
- let batchCount = 0;
1807
- if (existsSync(coordDbPath)) {
1808
- try {
1809
- const db = new Database(coordDbPath, { readonly: true });
1810
- const hasDQ = db
1811
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='deploy_queue'")
1812
- .all();
1813
- if (hasDQ.length > 0) {
1814
- deployPending = db.prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='pending'").get().c;
1815
- deployCompleted = db
1816
- .prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='completed' OR status='batched'")
1817
- .get().c;
1818
- deployFailed = db.prepare("SELECT COUNT(*) as c FROM deploy_queue WHERE status='failed'").get().c;
1819
- }
1820
- const hasDB = db
1821
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='deploy_batches'")
1822
- .all();
1823
- if (hasDB.length > 0) {
1824
- batchCount = db.prepare('SELECT COUNT(*) as c FROM deploy_batches').get()
1825
- .c;
1826
- }
1827
- db.close();
1828
- }
1829
- catch {
1830
- /* ignore */
1831
- }
1832
- }
1833
- const deployTotal = deployPending + deployCompleted + deployFailed;
1834
- const deployRows = [
1835
- ['Pending', String(deployPending)],
1836
- ['Completed', String(deployCompleted)],
1837
- ['Failed', String(deployFailed)],
1838
- ['Total actions', String(deployTotal)],
1839
- ['Batches', String(batchCount)],
1840
- ];
1841
- for (const [label, value] of deployRows) {
1842
- const color = label === 'Failed' && deployFailed > 0
1843
- ? chalk.red
1844
- : label === 'Pending' && deployPending > 0
1845
- ? chalk.yellow
1846
- : chalk.white;
1847
- console.log(` ${color(label.padEnd(25))} ${chalk.bold(value)}`);
1848
- }
1849
- console.log('');
1850
- // ── Pattern Success Rates ──
1851
- console.log(chalk.bold(' Pattern Success Rates'));
1852
- console.log(` ${chalk.dim('─'.repeat(W - 2))}`);
1853
- if (existsSync(patternIndexPath)) {
1854
- try {
1855
- const raw = readFileSync(patternIndexPath, 'utf-8');
1856
- const data = JSON.parse(raw);
1857
- const patterns = data.patterns || [];
1858
- const active = patterns.filter((p) => p.active !== false);
1859
- const withRates = active.filter((p) => typeof p.successRate === 'number');
1860
- console.log(` ${chalk.white('Total patterns'.padEnd(25))} ${chalk.bold(String(patterns.length))}`);
1861
- console.log(` ${chalk.white('Active'.padEnd(25))} ${chalk.bold(String(active.length))}`);
1862
- if (withRates.length > 0) {
1863
- const avgRate = withRates.reduce((sum, p) => sum + (p.successRate || 0), 0) / withRates.length;
1864
- console.log(` ${chalk.white('Avg success rate'.padEnd(25))} ${chalk.bold(avgRate.toFixed(1) + '%')}`);
1865
- console.log('');
1866
- // Show top patterns by success rate
1867
- const sorted = [...withRates].sort((a, b) => (b.successRate || 0) - (a.successRate || 0));
1868
- for (const p of sorted.slice(0, 8)) {
1869
- const name = p.title || p.name || p.abbreviation || (p.id != null ? `P${p.id}` : '?');
1870
- const rate = p.successRate || 0;
1871
- const barLen = Math.round(rate / 5);
1872
- const bar = chalk.green('\u2588'.repeat(barLen)) + chalk.dim('\u2591'.repeat(20 - barLen));
1873
- console.log(` ${name.slice(0, 22).padEnd(22)} ${bar} ${rate.toFixed(0)}%`);
1874
- }
1875
- if (sorted.length > 8) {
1876
- console.log(chalk.dim(` ... and ${sorted.length - 8} more`));
1877
- }
1878
- }
1879
- else {
1880
- console.log(chalk.dim(' No success rate data tracked'));
1881
- }
1882
- }
1883
- catch {
1884
- console.log(chalk.dim(' Could not parse patterns index'));
1885
- }
1886
- }
1887
- else {
1888
- console.log(chalk.dim(' No patterns index found'));
1889
- }
1890
- console.log('');
1891
- console.log(` ${sep}`);
1892
- console.log(chalk.dim(' Run: uap dashboard overview for full project dashboard'));
1893
- console.log(chalk.dim(' Run: uap dashboard session for session context'));
1894
- console.log('');
1895
- }
1896
1146
  //# sourceMappingURL=dashboard.js.map