clawmetry 0.12.415__tar.gz → 0.12.417__tar.gz

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 (489) hide show
  1. {clawmetry-0.12.415/clawmetry.egg-info → clawmetry-0.12.417}/PKG-INFO +1 -1
  2. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/js/app.js +54 -1
  3. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/sync.py +35 -0
  4. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/subagents.html +5 -1
  5. {clawmetry-0.12.415 → clawmetry-0.12.417/clawmetry.egg-info}/PKG-INFO +1 -1
  6. {clawmetry-0.12.415 → clawmetry-0.12.417}/dashboard.py +1 -1
  7. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/sessions.py +24 -0
  8. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/update_check.py +41 -3
  9. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_auto_update.py +52 -0
  10. {clawmetry-0.12.415 → clawmetry-0.12.417}/ARCHITECTURE.md +0 -0
  11. {clawmetry-0.12.415 → clawmetry-0.12.417}/CHANGELOG.md +0 -0
  12. {clawmetry-0.12.415 → clawmetry-0.12.417}/CLAUDE.md +0 -0
  13. {clawmetry-0.12.415 → clawmetry-0.12.417}/LICENSE +0 -0
  14. {clawmetry-0.12.415 → clawmetry-0.12.417}/MANIFEST.in +0 -0
  15. {clawmetry-0.12.415 → clawmetry-0.12.417}/README.md +0 -0
  16. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/__init__.py +0 -0
  17. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/__main__.py +0 -0
  18. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/_gate.py +0 -0
  19. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/adapters/__init__.py +0 -0
  20. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/adapters/base.py +0 -0
  21. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/adapters/nemo.py +0 -0
  22. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/adapters/openclaw.py +0 -0
  23. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/adapters/registry.py +0 -0
  24. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/alert_evaluator.py +0 -0
  25. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/approvals.py +0 -0
  26. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/audit.py +0 -0
  27. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/blueprints/__init__.py +0 -0
  28. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/cli.py +0 -0
  29. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/config.py +0 -0
  30. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/connector_health.py +0 -0
  31. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/dives_prompt.py +0 -0
  32. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/dives_sql_safety.py +0 -0
  33. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/entitlements.py +0 -0
  34. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/error_patterns.py +0 -0
  35. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/error_signal.py +0 -0
  36. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/eval_regression_replay.py +0 -0
  37. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/eval_runner.py +0 -0
  38. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/eval_suite_runner.py +0 -0
  39. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/extensions.py +0 -0
  40. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/gateway_tap.py +0 -0
  41. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/insights.py +0 -0
  42. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/interceptor.py +0 -0
  43. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/latency_tracker.py +0 -0
  44. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/license.py +0 -0
  45. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/local_server.py +0 -0
  46. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/local_store.py +0 -0
  47. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/otel_exporter.py +0 -0
  48. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/otel_push.py +0 -0
  49. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/outcome_classifier.py +0 -0
  50. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/providers/__init__.py +0 -0
  51. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/providers/base.py +0 -0
  52. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/providers/local.py +0 -0
  53. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/providers/turso.py +0 -0
  54. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/providers_pricing.py +0 -0
  55. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/proxy.py +0 -0
  56. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/py.typed +0 -0
  57. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/redaction.py +0 -0
  58. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/relay.py +0 -0
  59. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/resources/add-nemoclaw-clawmetry-preset.sh +0 -0
  60. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/risk.py +0 -0
  61. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/siem.py +0 -0
  62. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/css/dashboard.css +0 -0
  63. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/img/logo.svg +0 -0
  64. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/js/alerts.js +0 -0
  65. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/js/auth-bootstrap.js +0 -0
  66. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/js/dives.js +0 -0
  67. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/js/gw-setup.js +0 -0
  68. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/js/i18n.js +0 -0
  69. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/js/nav-dropdown.js +0 -0
  70. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/_glossary.json +0 -0
  71. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/_meta.json +0 -0
  72. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/ar.json +0 -0
  73. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/bn.json +0 -0
  74. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/de.json +0 -0
  75. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/el.json +0 -0
  76. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/en.json +0 -0
  77. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/es-419.json +0 -0
  78. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/es.json +0 -0
  79. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/fa.json +0 -0
  80. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/fil.json +0 -0
  81. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/fr.json +0 -0
  82. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/gu.json +0 -0
  83. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/he.json +0 -0
  84. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/hi.json +0 -0
  85. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/id.json +0 -0
  86. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/it.json +0 -0
  87. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/ja.json +0 -0
  88. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/kn.json +0 -0
  89. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/ko.json +0 -0
  90. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/ml.json +0 -0
  91. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/mr.json +0 -0
  92. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/nl.json +0 -0
  93. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/pa.json +0 -0
  94. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/pl.json +0 -0
  95. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/pt-BR.json +0 -0
  96. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/pt-PT.json +0 -0
  97. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/ru.json +0 -0
  98. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/sv.json +0 -0
  99. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/ta.json +0 -0
  100. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/te.json +0 -0
  101. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/th.json +0 -0
  102. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/tr.json +0 -0
  103. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/uk.json +0 -0
  104. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/ur.json +0 -0
  105. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/vi.json +0 -0
  106. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/zh-CN.json +0 -0
  107. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/locales/zh-TW.json +0 -0
  108. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/v2/dist/assets/index-Dtt_uBk_.js +0 -0
  109. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/v2/dist/assets/index-NbINX9rU.css +0 -0
  110. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/static/v2/dist/index.html +0 -0
  111. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/telemetry.py +0 -0
  112. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/README.md +0 -0
  113. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/partials/banners.html +0 -0
  114. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/partials/budget-modal.html +0 -0
  115. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/partials/cloud-modal.html +0 -0
  116. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/partials/overlays.html +0 -0
  117. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/partials/paywall_modal.html +0 -0
  118. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/alerts.html +0 -0
  119. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/approvals.html +0 -0
  120. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/brain.html +0 -0
  121. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/clusters.html +0 -0
  122. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/context-economics.html +0 -0
  123. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/context.html +0 -0
  124. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/crons.html +0 -0
  125. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/dives.html +0 -0
  126. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/flow.html +0 -0
  127. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/history.html +0 -0
  128. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/logs.html +0 -0
  129. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/memory.html +0 -0
  130. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/models.html +0 -0
  131. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/nemoclaw.html +0 -0
  132. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/notifications.html +0 -0
  133. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/overview.html +0 -0
  134. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/policy.html +0 -0
  135. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/security.html +0 -0
  136. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/selfevolve.html +0 -0
  137. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/skills.html +0 -0
  138. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/swimlane.html +0 -0
  139. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/tool-catalog.html +0 -0
  140. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/tracing.html +0 -0
  141. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/transcripts.html +0 -0
  142. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/turn-anatomy.html +0 -0
  143. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/usage.html +0 -0
  144. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/templates/tabs/version-impact.html +0 -0
  145. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/tests/__init__.py +0 -0
  146. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/tests/e2e/__init__.py +0 -0
  147. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/tests/e2e/test_cloud_sync_resilience.py +0 -0
  148. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/tests/integration/__init__.py +0 -0
  149. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/tests/integration/test_fast_paths_cross_process.py +0 -0
  150. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/token_confidence.py +0 -0
  151. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/track.py +0 -0
  152. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/v2/__init__.py +0 -0
  153. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/v2/routes.py +0 -0
  154. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry/waste_flags.py +0 -0
  155. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry.egg-info/SOURCES.txt +0 -0
  156. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry.egg-info/dependency_links.txt +0 -0
  157. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry.egg-info/entry_points.txt +0 -0
  158. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry.egg-info/requires.txt +0 -0
  159. {clawmetry-0.12.415 → clawmetry-0.12.417}/clawmetry.egg-info/top_level.txt +0 -0
  160. {clawmetry-0.12.415 → clawmetry-0.12.417}/helpers/__init__.py +0 -0
  161. {clawmetry-0.12.415 → clawmetry-0.12.417}/helpers/gateway.py +0 -0
  162. {clawmetry-0.12.415 → clawmetry-0.12.417}/helpers/hardware.py +0 -0
  163. {clawmetry-0.12.415 → clawmetry-0.12.417}/helpers/logs.py +0 -0
  164. {clawmetry-0.12.415 → clawmetry-0.12.417}/helpers/openapi.py +0 -0
  165. {clawmetry-0.12.415 → clawmetry-0.12.417}/helpers/pricing.py +0 -0
  166. {clawmetry-0.12.415 → clawmetry-0.12.417}/helpers/streams.py +0 -0
  167. {clawmetry-0.12.415 → clawmetry-0.12.417}/helpers/system.py +0 -0
  168. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/__init__.py +0 -0
  169. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/_dedupe.py +0 -0
  170. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/advisor.py +0 -0
  171. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/agents.py +0 -0
  172. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/alerts.py +0 -0
  173. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/assets.py +0 -0
  174. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/audit.py +0 -0
  175. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/autonomy.py +0 -0
  176. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/bootstrap.py +0 -0
  177. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/brain.py +0 -0
  178. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/channels.py +0 -0
  179. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/components.py +0 -0
  180. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/context_economics.py +0 -0
  181. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/crons.py +0 -0
  182. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/dives.py +0 -0
  183. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/entitlement.py +0 -0
  184. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/evals.py +0 -0
  185. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/fleet_history.py +0 -0
  186. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/health.py +0 -0
  187. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/heartbeat.py +0 -0
  188. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/hitl.py +0 -0
  189. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/infra.py +0 -0
  190. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/insights.py +0 -0
  191. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/local_query.py +0 -0
  192. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/meta.py +0 -0
  193. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/nemoclaw.py +0 -0
  194. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/otel_export.py +0 -0
  195. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/overview.py +0 -0
  196. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/plugins.py +0 -0
  197. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/policy.py +0 -0
  198. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/reasoning.py +0 -0
  199. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/review.py +0 -0
  200. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/runtime_ingest.py +0 -0
  201. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/scheduler.py +0 -0
  202. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/selfconfig.py +0 -0
  203. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/selfevolve.py +0 -0
  204. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/skills.py +0 -0
  205. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/sla.py +0 -0
  206. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/tool_catalog.py +0 -0
  207. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/tracing.py +0 -0
  208. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/turn_anatomy.py +0 -0
  209. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/usage.py +0 -0
  210. {clawmetry-0.12.415 → clawmetry-0.12.417}/routes/workspaces.py +0 -0
  211. {clawmetry-0.12.415 → clawmetry-0.12.417}/setup.cfg +0 -0
  212. {clawmetry-0.12.415 → clawmetry-0.12.417}/setup.py +0 -0
  213. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/__init__.py +0 -0
  214. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/_moat_perf_fixture.py +0 -0
  215. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/conftest.py +0 -0
  216. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/eval_ci_mock_agent.py +0 -0
  217. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_adapter_gating.py +0 -0
  218. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_adapter_registry_override.py +0 -0
  219. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_adapters.py +0 -0
  220. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_adaptive_heartbeat.py +0 -0
  221. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_advertised_runtimes_match_catalogue.py +0 -0
  222. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_advisor_auth_resolution.py +0 -0
  223. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_aesgcm_swallow_fix.py +0 -0
  224. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_alert_evaluator_unit.py +0 -0
  225. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_alert_rules_local_store.py +0 -0
  226. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_alerts_evaluator_duckdb.py +0 -0
  227. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_anon_auth_fail_ping.py +0 -0
  228. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_api.py +0 -0
  229. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_appjs_units.py +0 -0
  230. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_approvals_duckdb_watcher.py +0 -0
  231. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_approvals_local_store.py +0 -0
  232. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_audit.py +0 -0
  233. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_auth_detected_token.py +0 -0
  234. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_automation_analysis_local_store_v3.py +0 -0
  235. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_autonomy_v3_shape.py +0 -0
  236. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_backfill_family_cost.py +0 -0
  237. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_blueprint_uniqueness.py +0 -0
  238. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_bootstrap_capture.py +0 -0
  239. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_cache_push.py +0 -0
  240. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_cache_push_channel_events.py +0 -0
  241. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_clusters_local_store.py +0 -0
  242. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_history_v3_event_detail.py +0 -0
  243. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_llm_call_timeline.py +0 -0
  244. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_local_fastpath.py +0 -0
  245. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_local_store.py +0 -0
  246. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_no_log_keyword_noise.py +0 -0
  247. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_brain_sse_reconnect.py +0 -0
  248. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_budget_cap.py +0 -0
  249. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_cache_trends.py +0 -0
  250. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_channel_bluebubbles_local_store_v3.py +0 -0
  251. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_channel_config_local_store.py +0 -0
  252. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_channel_event_chokepoint.py +0 -0
  253. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_channel_signal_local_store_v3.py +0 -0
  254. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_channel_telegram_local_store_v3.py +0 -0
  255. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_channel_tui_duckdb_fastpath.py +0 -0
  256. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_channel_whatsapp_duckdb_fastpath.py +0 -0
  257. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_channels_local_store.py +0 -0
  258. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_circular_import.py +0 -0
  259. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_class_bug_parent_sid_drain.py +0 -0
  260. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_claude_sessions_privacy_boundary.py +0 -0
  261. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_claudecode.py +0 -0
  262. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_claudecode_loads_plugins.py +0 -0
  263. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_cli.py +0 -0
  264. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_cli_banner.py +0 -0
  265. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_component_gateway_local_store_v3.py +0 -0
  266. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_component_tool_model_completed.py +0 -0
  267. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_component_tool_timeout.py +0 -0
  268. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_connector_health.py +0 -0
  269. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_context_anatomy_local_store.py +0 -0
  270. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_context_window_model_aware.py +0 -0
  271. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_cost_optimizer_local_store_v3.py +0 -0
  272. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_cron_health.py +0 -0
  273. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_cron_runs_duckdb_ingest.py +0 -0
  274. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_cron_runs_events_only.py +0 -0
  275. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_crons_cache_push.py +0 -0
  276. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_crons_local_store.py +0 -0
  277. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_crons_runs_endpoint.py +0 -0
  278. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_daemon_error_event_pipeline.py +0 -0
  279. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_daemon_health_endpoint.py +0 -0
  280. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_dashboard_perf_hotfix.py +0 -0
  281. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_decoding_config_extract.py +0 -0
  282. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_delegation_tree_local_store.py +0 -0
  283. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_dives_prompt.py +0 -0
  284. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_dives_sql_safety.py +0 -0
  285. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_duckdb_coverage_batch_2026_05_13.py +0 -0
  286. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_duckdb_coverage_phase2_2026_05_13.py +0 -0
  287. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_duckdb_coverage_phase3_2026_05_13.py +0 -0
  288. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_duckdb_coverage_phase4_channels_2026_05_13.py +0 -0
  289. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_duckdb_coverage_phase5_channels_2026_05_13.py +0 -0
  290. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_duckdb_fastpath_v3_invariants.py +0 -0
  291. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_duckdb_relay_integration.py +0 -0
  292. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_e2e.py +0 -0
  293. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_e2e_browserstack.py +0 -0
  294. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_e2e_cross_repo_handoff.py +0 -0
  295. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_e2e_nightly_workflow.py +0 -0
  296. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_e2e_oss_all_tabs.py +0 -0
  297. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_e2e_oss_golden_path.py +0 -0
  298. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_e2e_real_openclaw_pipeline.py +0 -0
  299. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_e2e_telegram_brain_ingest.py +0 -0
  300. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_entitlement_api.py +0 -0
  301. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_entitlements.py +0 -0
  302. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_entitlements_catalogue.py +0 -0
  303. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_error_patterns.py +0 -0
  304. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_error_triage.py +0 -0
  305. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_eval_regression_replay.py +0 -0
  306. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_eval_runner.py +0 -0
  307. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_eval_suite_runner.py +0 -0
  308. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_evaluate_alerts_integration.py +0 -0
  309. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_event_id_unification_and_dedup.py +0 -0
  310. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_event_metrics_extraction.py +0 -0
  311. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_events_only_computation.py +0 -0
  312. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_extensions.py +0 -0
  313. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_fallbacks_local_store.py +0 -0
  314. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_family_runtime_ingest.py +0 -0
  315. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_first_heartbeat_onboarding.py +0 -0
  316. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_flow_events_local_store_v3.py +0 -0
  317. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_flow_runs_endpoint.py +0 -0
  318. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_flow_sse_reconnect.py +0 -0
  319. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_forward_progress.py +0 -0
  320. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_gateway_health.py +0 -0
  321. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_gateway_health_local_store_v3.py +0 -0
  322. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_gateway_metric_history.py +0 -0
  323. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_gateway_tap.py +0 -0
  324. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_gateway_tap_opt_in_banner.py +0 -0
  325. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_gateway_token_fallback.py +0 -0
  326. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_gateway_ws_handshake_client_id.py +0 -0
  327. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_hallucination_risk.py +0 -0
  328. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_health_local_store.py +0 -0
  329. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_heartbeat_agent_install_piggyback.py +0 -0
  330. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_heartbeat_dispatch.py +0 -0
  331. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_heartbeat_envelope.py +0 -0
  332. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_heartbeat_local_store.py +0 -0
  333. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_heartbeat_loop_detector.py +0 -0
  334. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_heartbeat_relay_e2e.py +0 -0
  335. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_i18n_catalog.py +0 -0
  336. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_i18n_no_raw_codes.py +0 -0
  337. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_insights.py +0 -0
  338. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_install_script_linux_restart.py +0 -0
  339. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_install_script_no_duplicate_daemon.py +0 -0
  340. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_integrity_hash_chain.py +0 -0
  341. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_interceptor.py +0 -0
  342. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_issue_1127_ux_dead_ends.py +0 -0
  343. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_latency_tracker.py +0 -0
  344. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_license.py +0 -0
  345. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_license_api.py +0 -0
  346. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_max_bytes_auto_vacuum.py +0 -0
  347. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_query_api.py +0 -0
  348. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_query_dispatch_edge_cases.py +0 -0
  349. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_server_proxy.py +0 -0
  350. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_store.py +0 -0
  351. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_store_concurrent_flush_1590.py +0 -0
  352. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_store_default_on.py +0 -0
  353. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_store_missing_writers.py +0 -0
  354. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_store_multi_agent.py +0 -0
  355. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_store_rename.py +0 -0
  356. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_local_store_sync_integration.py +0 -0
  357. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_log_sse_reconnect.py +0 -0
  358. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_loop_signals_integration.py +0 -0
  359. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_memory_cache_push.py +0 -0
  360. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_memory_local_store.py +0 -0
  361. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_memory_rag.py +0 -0
  362. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_bug_class_v3_event_shape_gate.py +0 -0
  363. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_cloud_robustness.py +0 -0
  364. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_cloud_roundtrip_e2e.py +0 -0
  365. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_cloud_sync_e2e.py +0 -0
  366. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_component_tool_e2e.py +0 -0
  367. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_cross_repo_version_skew.py +0 -0
  368. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_daemon_crash_recovery.py +0 -0
  369. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_event_shape_manifest_guard.py +0 -0
  370. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_live_openclaw_e2e.py +0 -0
  371. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_perf_benchmark.py +0 -0
  372. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_real_e2e.py +0 -0
  373. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_regression_1129.py +0 -0
  374. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_send_message_e2e.py +0 -0
  375. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_moat_tier1_daemon_proxy_sweep.py +0 -0
  376. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_model_transitions_local_store_v3.py +0 -0
  377. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_multi_workspace_discovery.py +0 -0
  378. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_nemo_adapter.py +0 -0
  379. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_nemo_adapter_pro_gate.py +0 -0
  380. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_nemoclaw_events_metrics.py +0 -0
  381. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_nemoclaw_governance.py +0 -0
  382. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_nemoclaw_runtime_adapter.py +0 -0
  383. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_no_agent_detected_state.py +0 -0
  384. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_no_direct_get_store_in_routes.py +0 -0
  385. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_oauth_detection.py +0 -0
  386. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_openclaw_claude_session_ingest.py +0 -0
  387. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_openclaw_detection_real.py +0 -0
  388. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_openclaw_list_events.py +0 -0
  389. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_openclaw_sessions_followup.py +0 -0
  390. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_oss_routes_source_canary.py +0 -0
  391. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_oss_stubs_after_pro_move.py +0 -0
  392. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_otel_export.py +0 -0
  393. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_otlp_metrics_edge_cases.py +0 -0
  394. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_outcome_classifier.py +0 -0
  395. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_overview_autonomy_score.py +0 -0
  396. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_overview_heartbeat.py +0 -0
  397. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_overview_local_store.py +0 -0
  398. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_pending_query_dispatch.py +0 -0
  399. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_per_agent_budget.py +0 -0
  400. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_phase2_stubs.py +0 -0
  401. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_phase3_stubs.py +0 -0
  402. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_phase4_adapter_move.py +0 -0
  403. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_phase_2_to_5_live_e2e.py +0 -0
  404. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_plugins_invocations_local_store.py +0 -0
  405. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_providers_pricing.py +0 -0
  406. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_proxy.py +0 -0
  407. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_proxy_budget_abort.py +0 -0
  408. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_proxy_loads_plugins.py +0 -0
  409. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_query_sessions_message_count.py +0 -0
  410. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_rate_limits_local_store_v3.py +0 -0
  411. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_read_cloud_token_fallback.py +0 -0
  412. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_real_openclaw_binary_e2e.py +0 -0
  413. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_reasoning_v3_shape.py +0 -0
  414. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_redaction.py +0 -0
  415. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_relay.py +0 -0
  416. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_reliability.py +0 -0
  417. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_retention_prune.py +0 -0
  418. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_review_queue.py +0 -0
  419. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_route_gates.py +0 -0
  420. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_routes_runtimes.py +0 -0
  421. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_run_compare.py +0 -0
  422. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_run_daemon_uniqueness.py +0 -0
  423. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_runtime_detection_snapshot.py +0 -0
  424. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_runtime_filter_no_leak.py +0 -0
  425. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_runtime_switcher_gating.py +0 -0
  426. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_schema_migration_failure_integrity.py +0 -0
  427. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_security_threat_heuristics.py +0 -0
  428. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_selfconfig_session_attribution.py +0 -0
  429. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_session_cost_intel.py +0 -0
  430. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_session_export.py +0 -0
  431. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_session_export_local_store_v3.py +0 -0
  432. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_session_insight.py +0 -0
  433. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_session_tools_local_store_v3.py +0 -0
  434. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sessions_by_type_local_store.py +0 -0
  435. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sessions_clusters_local_store_v3.py +0 -0
  436. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sessions_local_fastpath.py +0 -0
  437. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sessions_retention_cap.py +0 -0
  438. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sigterm_ring_flush.py +0 -0
  439. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_skills_fidelity_local_store.py +0 -0
  440. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_skills_fidelity_local_store_v3.py +0 -0
  441. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_skills_multi_dir_discovery.py +0 -0
  442. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_spans_e2e.py +0 -0
  443. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_spans_ingest.py +0 -0
  444. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_spans_otlp_edge_cases.py +0 -0
  445. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_status_live.py +0 -0
  446. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_subagent_attribution_v3.py +0 -0
  447. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_subagents_events_only.py +0 -0
  448. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_subagents_local_store_v3.py +0 -0
  449. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_subscribe_queue_bounded.py +0 -0
  450. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sync_cloud_plan_cache.py +0 -0
  451. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sync_daemon_crash_recovery.py +0 -0
  452. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sync_loads_plugins.py +0 -0
  453. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sync_main_block_at_eof.py +0 -0
  454. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sync_progress_sticky.py +0 -0
  455. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sync_reliability.py +0 -0
  456. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sync_skip_sidecar_jsonls.py +0 -0
  457. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_sync_trial_gating.py +0 -0
  458. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_system_snapshot_dlq.py +0 -0
  459. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_task_runs_local_store_v3.py +0 -0
  460. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_telegram_gatewaylog_parser.py +0 -0
  461. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_telegram_ingest.py +0 -0
  462. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_telemetry.py +0 -0
  463. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_tier1_local_store.py +0 -0
  464. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_tier_pro_paywall_no_flash.py +0 -0
  465. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_token_attribution_local_store_v3.py +0 -0
  466. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_token_confidence.py +0 -0
  467. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_token_spike_rules.py +0 -0
  468. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_token_velocity_local_store_v3.py +0 -0
  469. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_tool_alternatives.py +0 -0
  470. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_tool_catalog_provenance_and_calls.py +0 -0
  471. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_tool_modal_fastpath_2026_05_13.py +0 -0
  472. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_tracing_accuracy.py +0 -0
  473. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_track.py +0 -0
  474. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_track_source.py +0 -0
  475. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_transcript_local_store.py +0 -0
  476. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_transcript_openclaw_shapes.py +0 -0
  477. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_transcripts_list_detail_count_parity.py +0 -0
  478. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_trial_copy_consistency.py +0 -0
  479. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_update_banner_live_version.py +0 -0
  480. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_uptime_portable.py +0 -0
  481. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_usage_cache_trends_local_store.py +0 -0
  482. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_usage_forecast_local_store_v3.py +0 -0
  483. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_usage_local_store.py +0 -0
  484. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_usage_per_session.py +0 -0
  485. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_v3_schema_parser.py +0 -0
  486. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_verify_integrity_cli_proxy.py +0 -0
  487. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_version_impact.py +0 -0
  488. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_version_impact_local_store_v3.py +0 -0
  489. {clawmetry-0.12.415 → clawmetry-0.12.417}/tests/test_workspace_switcher_copy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clawmetry
3
- Version: 0.12.415
3
+ Version: 0.12.417
4
4
  Summary: ClawMetry - Real-time observability dashboard for OpenClaw AI agents
5
5
  Home-page: https://github.com/vivekchand/clawmetry
6
6
  Author: Vivek Chand
@@ -1178,7 +1178,7 @@ function switchTab(name) {
1178
1178
  if (name === 'models') loadModelAttribution();
1179
1179
  if (name === 'nemoclaw') { loadNemoClaw(); _startNcApprovalsAutoRefresh(); }
1180
1180
  if (name !== 'nemoclaw') _stopNcApprovalsAutoRefresh();
1181
- if (name === 'subagents') { loadRunLedger(); loadSubagents(); if (!_subagentsTimer) _subagentsTimer = visibilitySetInterval(function(){ loadRunLedger(); loadSubagents(); }, 5000); }
1181
+ if (name === 'subagents') { loadOrchestration(); loadRunLedger(); loadSubagents(); if (!_subagentsTimer) _subagentsTimer = visibilitySetInterval(function(){ loadOrchestration(); loadRunLedger(); loadSubagents(); }, 5000); }
1182
1182
  if (name !== 'subagents' && _subagentsTimer) { clearInterval(_subagentsTimer); _subagentsTimer = null; }
1183
1183
  if (name === 'swimlane') { loadSwimlane(); if (!_swimlaneTimer) _swimlaneTimer = visibilitySetInterval(loadSwimlane, 3000); }
1184
1184
  if (name !== 'swimlane' && _swimlaneTimer) { clearInterval(_swimlaneTimer); _swimlaneTimer = null; }
@@ -13383,6 +13383,59 @@ function _saToggle(sid) {
13383
13383
  loadSubagents();
13384
13384
  }
13385
13385
 
13386
+ async function loadOrchestration() {
13387
+ var el = document.getElementById('orchestration-board');
13388
+ if (!el) return;
13389
+ try {
13390
+ var data = await fetch('/api/orchestration').then(function(r) { return r.json(); });
13391
+ var agents = data.agents || [];
13392
+ var summary = data.summary || {};
13393
+ if (agents.length === 0) { el.innerHTML = ''; return; }
13394
+ var statusColors = {
13395
+ active: '#16a34a', running: '#16a34a', idle: '#d97706',
13396
+ stale: '#6b7280', failed: '#ef4444', paused: '#7c3aed', completed: '#3b82f6'
13397
+ };
13398
+ var html = '<div style="border:1px solid var(--border-primary);border-radius:10px;overflow:hidden;margin-bottom:4px;">';
13399
+ html += '<div style="display:flex;align-items:center;gap:16px;padding:8px 14px;background:var(--bg-secondary);border-bottom:1px solid var(--border-primary);font-size:12px;flex-wrap:wrap;">';
13400
+ html += '<span style="font-weight:700;color:var(--text-primary);font-size:13px;">🤖 Orchestration</span>';
13401
+ html += '<span style="color:var(--text-muted);"><strong style="color:var(--text-primary);">' + (summary.total || 0) + '</strong> agents</span>';
13402
+ if (summary.active) html += '<span style="color:#16a34a;"><strong>' + summary.active + '</strong> active</span>';
13403
+ if (summary.total_cost_usd) {
13404
+ html += '<span style="color:var(--text-muted);">$<strong style="color:var(--text-primary);">' + summary.total_cost_usd.toFixed(4) + '</strong> total cost</span>';
13405
+ }
13406
+ html += '</div>';
13407
+ html += '<div style="display:flex;flex-wrap:wrap;gap:8px;padding:10px;">';
13408
+ agents.forEach(function(a) {
13409
+ var color = statusColors[a.status] || '#6b7280';
13410
+ var glow = (a.status === 'active' || a.status === 'running') ? 'box-shadow:0 0 0 1px ' + color + '40;' : '';
13411
+ var costStr = (a.costUsd > 0) ? '$' + a.costUsd.toFixed(4) : '';
13412
+ var tokens = a.totalTokens >= 1000 ? (a.totalTokens / 1000).toFixed(1) + 'K tok' : (a.totalTokens > 0 ? a.totalTokens + ' tok' : '');
13413
+ var depthBadge = (a.depth > 1) ? '<span style="font-size:9px;background:var(--bg-secondary);border:1px solid var(--border-primary);border-radius:3px;padding:0 4px;color:var(--text-muted);margin-left:4px;">d' + a.depth + '</span>' : '';
13414
+ html += '<div style="flex:0 0 auto;min-width:155px;max-width:215px;border:1px solid var(--border-primary);border-radius:8px;padding:8px 10px;background:var(--bg-card);' + glow + '">';
13415
+ html += '<div style="display:flex;align-items:center;gap:5px;margin-bottom:4px;">';
13416
+ html += '<span style="width:8px;height:8px;border-radius:50%;background:' + color + ';display:inline-block;flex-shrink:0;"></span>';
13417
+ html += '<span style="font-size:12px;font-weight:600;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;" title="' + escHtml(a.displayName) + '">' + escHtml(a.displayName) + '</span>';
13418
+ html += depthBadge;
13419
+ html += '</div>';
13420
+ if (a.model && a.model !== 'unknown') {
13421
+ html += '<div style="font-size:10px;color:var(--text-muted);margin-bottom:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' + escHtml(a.model) + '</div>';
13422
+ }
13423
+ if (costStr || tokens) {
13424
+ html += '<div style="display:flex;gap:8px;font-size:10px;color:var(--text-faint);">';
13425
+ if (costStr) html += '<span style="color:#16a34a;">' + escHtml(costStr) + '</span>';
13426
+ if (tokens) html += '<span>' + escHtml(tokens) + '</span>';
13427
+ html += '</div>';
13428
+ }
13429
+ html += '</div>';
13430
+ });
13431
+ html += '</div></div>';
13432
+ el.innerHTML = html;
13433
+ } catch(e) {
13434
+ var board = document.getElementById('orchestration-board');
13435
+ if (board) board.innerHTML = '';
13436
+ }
13437
+ }
13438
+
13386
13439
  async function controlAgent(key, action) {
13387
13440
  if (action === 'stop') {
13388
13441
  if (!confirm('Stop agent ' + key + '? This will attempt to terminate it via the gateway and cannot be undone.')) return;
@@ -1032,6 +1032,39 @@ _HEARTBEAT_PLAN_TO_TIER = {
1032
1032
  }
1033
1033
 
1034
1034
 
1035
+ def _sync_auto_update_with_plan(tier: str | None) -> None:
1036
+ """Entitled accounts (Trial / Starter / Pro / Enterprise) keep the node
1037
+ current automatically: enable the opt-in ``auto_update`` flag so the
1038
+ daemon's update-check worker installs new releases (riding the 48h
1039
+ staleness rail). This is what makes "I'm on Pro, the node should just stay
1040
+ current" real without a manual ``pip install -U``.
1041
+
1042
+ Safety: respects an explicit opt-out (``CLAWMETRY_AUTO_UPDATE`` in
1043
+ 0/false/no/off), only ever ENABLES (never auto-disables, so a user's manual
1044
+ choice survives a downgrade), and no-ops for free / inactive plans. Best
1045
+ effort — never raises."""
1046
+ try:
1047
+ if not tier or tier == "cloud_free":
1048
+ return
1049
+ _ov = os.environ.get("CLAWMETRY_AUTO_UPDATE", "").strip().lower()
1050
+ if _ov in ("0", "false", "no", "off"):
1051
+ return
1052
+ from routes.update_check import (
1053
+ _get_update_check_config as _gucc,
1054
+ _set_update_check_config as _succ,
1055
+ )
1056
+ cfg = _gucc() or {}
1057
+ if not cfg.get("auto_update"):
1058
+ _succ({"auto_update": True})
1059
+ log.info(
1060
+ "auto-update enabled for entitled plan (%s) — this node will keep "
1061
+ "itself current (48h stability window). Opt out any time with "
1062
+ "CLAWMETRY_AUTO_UPDATE=0.", tier,
1063
+ )
1064
+ except Exception as exc:
1065
+ log.debug("auto-update plan sync skipped: %s", exc)
1066
+
1067
+
1035
1068
  def _persist_cloud_plan_to_disk(plan: str | None, trial_days_left=None) -> None:
1036
1069
  """Mirror the heartbeat plan into ``~/.clawmetry/cloud_plan.json`` so the
1037
1070
  dashboard process (which runs ``clawmetry.entitlements.get_entitlement``)
@@ -1043,6 +1076,8 @@ def _persist_cloud_plan_to_disk(plan: str | None, trial_days_left=None) -> None:
1043
1076
  is removed instead of written, so the resolver falls back to OSS-free
1044
1077
  rather than granting a dead plan."""
1045
1078
  tier = _HEARTBEAT_PLAN_TO_TIER.get(str(plan or "").strip().lower())
1079
+ # Entitled plan → keep this node current automatically (opt-out + 48h rail).
1080
+ _sync_auto_update_with_plan(tier)
1046
1081
  try:
1047
1082
  if tier is None:
1048
1083
  if os.path.isfile(_CLOUD_PLAN_CACHE_PATH):
@@ -1,8 +1,12 @@
1
1
  <div class="page" id="page-subagents">
2
2
  <div class="refresh-bar">
3
3
  <h2 style="font-size:16px;font-weight:700;color:var(--text-primary);margin:0;flex:1;" data-i18n="app.sub_agents_queue_lanes">🤖 Sub-Agents &amp; Queue Lanes</h2>
4
- <button class="refresh-btn" onclick="loadRunLedger();loadSubagents()" data-i18n="app.refresh">↻ Refresh</button>
4
+ <button class="refresh-btn" onclick="loadOrchestration();loadRunLedger();loadSubagents()" data-i18n="app.refresh">↻ Refresh</button>
5
5
  </div>
6
+ <!-- Orchestration status board: one card per agent with status badge, cost,
7
+ model tag. Populated by loadOrchestration() from /api/orchestration.
8
+ Hidden automatically when no agents exist. -->
9
+ <div id="orchestration-board" style="margin-bottom:12px;"></div>
6
10
  <!-- OpenClaw run-ledger lane monitor (cli / cron / subagent) + recent runs.
7
11
  Populated by loadRunLedger() from /api/run-ledger. `runtime` is the
8
12
  OpenClaw queue lane, so this is the live queue/concurrency view. -->
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clawmetry
3
- Version: 0.12.415
3
+ Version: 0.12.417
4
4
  Summary: ClawMetry - Real-time observability dashboard for OpenClaw AI agents
5
5
  Home-page: https://github.com/vivekchand/clawmetry
6
6
  Author: Vivek Chand
@@ -160,7 +160,7 @@ except ImportError:
160
160
  metrics_service_pb2 = None
161
161
  trace_service_pb2 = None
162
162
 
163
- __version__ = "0.12.415"
163
+ __version__ = "0.12.417"
164
164
 
165
165
  # Extensions (Phase 2): import the plugin host now, but defer the actual
166
166
  # load_plugins() call until after the Flask app is created below so we can
@@ -2027,6 +2027,7 @@ def _try_local_store_subagents(_rows=None):
2027
2027
  "depth": int(extra.get("depth") or 1),
2028
2028
  "parent": r.get("parent_session_id") or extra.get("spawnedBy"),
2029
2029
  "totalTokens": token_count,
2030
+ "costUsd": round(float(r.get("cost_usd") or 0.0), 4),
2030
2031
  "runtime": runtime,
2031
2032
  "runtimeMs": runtime_ms,
2032
2033
  "startedAt": spawned_at_ms or updated_at_ms,
@@ -2417,6 +2418,29 @@ def _check_integrity(subagents):
2417
2418
  return violations
2418
2419
 
2419
2420
 
2421
+ @bp_sessions.route("/api/orchestration")
2422
+ def api_orchestration():
2423
+ """Compact orchestration status board — agent cards with cost and model tags.
2424
+
2425
+ Wraps the subagents DuckDB fast-path (same source as /api/subagents) and
2426
+ adds a per-agent ``costUsd`` field for the status-board panel rendered
2427
+ above the subagent tree. Returns an empty board when the local store is
2428
+ unavailable so the existing tree view still works via its own fallback.
2429
+ """
2430
+ fast = _try_local_store_subagents()
2431
+ agents = (fast or {}).get("subagents", [])
2432
+ counts = (fast or {}).get("counts", {})
2433
+ total_cost_usd = round(sum(a.get("costUsd", 0.0) for a in agents), 4)
2434
+ return jsonify({
2435
+ "agents": agents,
2436
+ "summary": {
2437
+ "total": counts.get("total", 0),
2438
+ "active": counts.get("active", 0),
2439
+ "total_cost_usd": total_cost_usd,
2440
+ },
2441
+ })
2442
+
2443
+
2420
2444
  @bp_sessions.route("/api/subagents/integrity")
2421
2445
  def api_subagents_integrity():
2422
2446
  """Validate subagent state-machine: orphans, cycles, duplicate completions.
@@ -223,12 +223,34 @@ def _check_for_update():
223
223
 
224
224
  _record_update_check(current, latest, update_available, CHANGELOG_URL)
225
225
 
226
+ # Age (hours) of the latest release, for the auto-update staleness rail.
227
+ # NEVER blocks the banner — only the silent upgrade waits for the release
228
+ # to have survived a stability window before installing it unattended.
229
+ _age_h = None
230
+ try:
231
+ _files = (data.get("releases", {}) or {}).get(latest) or []
232
+ _times = [
233
+ f.get("upload_time_iso_8601") or f.get("upload_time")
234
+ for f in _files
235
+ if f.get("upload_time_iso_8601") or f.get("upload_time")
236
+ ]
237
+ if _times:
238
+ from datetime import datetime as _dt, timezone as _tz
239
+ _t0 = min(
240
+ _dt.fromisoformat(str(t).replace("Z", "+00:00")) for t in _times
241
+ )
242
+ if _t0.tzinfo is None:
243
+ _t0 = _t0.replace(tzinfo=_tz.utc)
244
+ _age_h = (_dt.now(_tz.utc) - _t0).total_seconds() / 3600.0
245
+ except Exception:
246
+ _age_h = None
247
+
226
248
  # Auto-update: if the user opted in, install the newer release now (no
227
249
  # click). Runs in the always-on dashboard server process, so it works
228
250
  # with no browser open. Guarded so a pending restart isn't re-triggered.
229
251
  if update_available:
230
252
  try:
231
- _maybe_auto_update(current, latest)
253
+ _maybe_auto_update(current, latest, _age_h)
232
254
  except Exception as exc:
233
255
  log.warning("auto-update trigger failed: %s", exc)
234
256
 
@@ -246,14 +268,30 @@ def _check_for_update():
246
268
  _auto_update_in_progress = False
247
269
 
248
270
 
249
- def _maybe_auto_update(current, latest):
250
- """Install a newer release automatically when ``auto_update`` is enabled."""
271
+ def _maybe_auto_update(current, latest, age_hours=None):
272
+ """Install a newer release automatically when ``auto_update`` is enabled.
273
+
274
+ Staleness rail: unattended upgrades wait until the release has been on PyPI
275
+ for at least ``CLAWMETRY_AUTOUPDATE_MIN_AGE_HOURS`` (default 48h), so a node
276
+ never auto-installs a brand-new (possibly broken) release the instant it
277
+ publishes. The update banner is unaffected — only the silent upgrade waits.
278
+ ``age_hours=None`` (age unknown) does not block, preserving prior behaviour.
279
+ """
251
280
  global _auto_update_in_progress
252
281
  if _auto_update_in_progress:
253
282
  return
254
283
  cfg = _get_update_check_config()
255
284
  if not cfg.get("auto_update"):
256
285
  return
286
+ try:
287
+ import os as _os
288
+ _min_age = float(_os.environ.get("CLAWMETRY_AUTOUPDATE_MIN_AGE_HOURS", "48") or 48)
289
+ except Exception:
290
+ _min_age = 48.0
291
+ if age_hours is not None and age_hours < _min_age:
292
+ log.info("auto-update: holding v%s (released %.1fh ago < %.0fh stability "
293
+ "window) — will install once it ages in", latest, age_hours, _min_age)
294
+ return
257
295
  _auto_update_in_progress = True
258
296
  log.info("auto-update: opted in — upgrading clawmetry v%s -> v%s", current, latest)
259
297
  try:
@@ -70,3 +70,55 @@ def test_auto_update_in_allowed_config_keys(monkeypatch):
70
70
  with app.test_request_context(json={"auto_update": True, "bogus": "x"}):
71
71
  uc.api_update_check_config_post()
72
72
  assert captured == {"auto_update": True}, "auto_update must pass the allow-list, bogus keys filtered"
73
+
74
+
75
+ def test_auto_update_holds_fresh_release(monkeypatch):
76
+ uc = _uc()
77
+ calls = []
78
+ _mock_self_update(monkeypatch, calls)
79
+ monkeypatch.setattr(uc, "_get_update_check_config", lambda: {"auto_update": True})
80
+ monkeypatch.setenv("CLAWMETRY_AUTOUPDATE_MIN_AGE_HOURS", "48")
81
+ # 2h-old release → held (too fresh)
82
+ uc._maybe_auto_update("0.12.1", "0.12.2", age_hours=2.0)
83
+ assert calls == [], "must hold a release younger than the stability window"
84
+ # 72h-old release → installs
85
+ uc._maybe_auto_update("0.12.1", "0.12.2", age_hours=72.0)
86
+ assert calls == ["auto"], "must install once the release has aged in"
87
+
88
+
89
+ def test_auto_update_unknown_age_does_not_block(monkeypatch):
90
+ uc = _uc()
91
+ calls = []
92
+ _mock_self_update(monkeypatch, calls)
93
+ monkeypatch.setattr(uc, "_get_update_check_config", lambda: {"auto_update": True})
94
+ uc._maybe_auto_update("0.12.1", "0.12.2", age_hours=None)
95
+ assert calls == ["auto"], "unknown age must not block (back-compat)"
96
+
97
+
98
+ def test_entitled_plan_enables_auto_update(monkeypatch):
99
+ import clawmetry.sync as S
100
+ import routes.update_check as uc
101
+ state = {"auto_update": False}
102
+ monkeypatch.setattr(uc, "_get_update_check_config", lambda: dict(state))
103
+ monkeypatch.setattr(uc, "_set_update_check_config", lambda upd: state.update(upd))
104
+ monkeypatch.delenv("CLAWMETRY_AUTO_UPDATE", raising=False)
105
+ # free / inactive → unchanged
106
+ S._sync_auto_update_with_plan("cloud_free"); assert state["auto_update"] is False
107
+ S._sync_auto_update_with_plan(None); assert state["auto_update"] is False
108
+ # entitled → enabled
109
+ S._sync_auto_update_with_plan("trial"); assert state["auto_update"] is True
110
+ S._sync_auto_update_with_plan("cloud_pro"); assert state["auto_update"] is True
111
+
112
+
113
+ def test_entitled_plan_respects_optout_and_never_disables(monkeypatch):
114
+ import clawmetry.sync as S
115
+ import routes.update_check as uc
116
+ state = {"auto_update": False}
117
+ monkeypatch.setattr(uc, "_get_update_check_config", lambda: dict(state))
118
+ monkeypatch.setattr(uc, "_set_update_check_config", lambda upd: state.update(upd))
119
+ monkeypatch.setenv("CLAWMETRY_AUTO_UPDATE", "0")
120
+ S._sync_auto_update_with_plan("cloud_pro"); assert state["auto_update"] is False # opt-out
121
+ # never auto-DISABLES a user's manual choice on downgrade
122
+ monkeypatch.delenv("CLAWMETRY_AUTO_UPDATE", raising=False)
123
+ state["auto_update"] = True
124
+ S._sync_auto_update_with_plan("cloud_free"); assert state["auto_update"] is True
File without changes
File without changes
File without changes
File without changes
File without changes