@smilintux/skcapstone 0.10.0 → 0.12.5

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 (279) hide show
  1. package/.env.example +10 -4
  2. package/.github/workflows/ci.yml +2 -2
  3. package/.github/workflows/publish.yml +9 -2
  4. package/.openclaw-workspace.json +2 -2
  5. package/CLAUDE.md +37 -0
  6. package/MISSION.md +17 -2
  7. package/README.md +282 -3
  8. package/docker/Dockerfile +7 -7
  9. package/docker/compose-templates/dev-team.yml +12 -12
  10. package/docker/compose-templates/mini-team.yml +9 -9
  11. package/docker/compose-templates/ops-team.yml +10 -10
  12. package/docker/compose-templates/research-team.yml +10 -10
  13. package/docker/entrypoint.sh +4 -4
  14. package/docs/ADR-optional-integration-backbone.md +181 -0
  15. package/docs/ARCHITECTURE.md +186 -43
  16. package/docs/BOND_WITH_GROK.md +6 -6
  17. package/docs/CUSTOM_AGENT.md +123 -30
  18. package/docs/DREAMING.md +70 -0
  19. package/docs/GETTING_STARTED.md +7 -7
  20. package/docs/QUICKSTART.md +10 -6
  21. package/docs/SKJOULE_ARCHITECTURE.md +3 -3
  22. package/docs/SOUL_SWAPPER.md +5 -5
  23. package/docs/hammertime-audit.md +402 -0
  24. package/docs/sk-integration-HANDOFF.md +117 -0
  25. package/docs/skscheduler.md +155 -0
  26. package/docs/superpowers/examples/jobs.yaml +31 -0
  27. package/docs/superpowers/plans/2026-06-08-skscheduler.md +1265 -0
  28. package/docs/superpowers/specs/2026-06-08-skscheduler-design.md +186 -0
  29. package/examples/custom-bond-template.json +1 -1
  30. package/examples/grok-feb.json +1 -1
  31. package/examples/queen-ava-feb.json +1 -1
  32. package/launchd/{com.skcapstone.skcomm-heartbeat.plist → com.skcapstone.skcomms-heartbeat.plist} +4 -4
  33. package/launchd/{com.skcapstone.skcomm-queue-drain.plist → com.skcapstone.skcomms-queue-drain.plist} +4 -4
  34. package/launchd/install-launchd.sh +6 -6
  35. package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
  36. package/package.json +1 -1
  37. package/pyproject.toml +16 -10
  38. package/scripts/archive-sessions.sh +7 -0
  39. package/scripts/check-updates.py +4 -4
  40. package/scripts/install-bundle.sh +8 -8
  41. package/scripts/install.ps1 +12 -11
  42. package/scripts/install.sh +159 -5
  43. package/scripts/model-fallback-monitor.sh +102 -0
  44. package/scripts/nvidia-proxy.mjs +78 -26
  45. package/scripts/refresh-anthropic-token.sh +172 -0
  46. package/scripts/release.sh +98 -0
  47. package/scripts/session-to-memory.py +219 -0
  48. package/scripts/skgateway.mjs +3 -3
  49. package/scripts/telegram-catchup-all.sh +12 -1
  50. package/scripts/verify_install.sh +2 -2
  51. package/scripts/wargov-ufo-capture/README.md +43 -0
  52. package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
  53. package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
  54. package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
  55. package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
  56. package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
  57. package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
  58. package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
  59. package/scripts/watch-anthropic-token.sh +212 -0
  60. package/scripts/windows/install-tasks.ps1 +7 -7
  61. package/scripts/windows/skcapstone-task.xml +1 -1
  62. package/src/skcapstone/__init__.py +45 -3
  63. package/src/skcapstone/_cli_monolith.py +20 -15
  64. package/src/skcapstone/activity.py +5 -1
  65. package/src/skcapstone/agent_card.py +3 -2
  66. package/src/skcapstone/api.py +41 -40
  67. package/src/skcapstone/auction.py +14 -11
  68. package/src/skcapstone/backup.py +2 -1
  69. package/src/skcapstone/blueprint_registry.py +4 -3
  70. package/src/skcapstone/brain_first.py +238 -0
  71. package/src/skcapstone/changelog.py +1 -1
  72. package/src/skcapstone/chat.py +22 -17
  73. package/src/skcapstone/cli/__init__.py +9 -1
  74. package/src/skcapstone/cli/_common.py +1 -0
  75. package/src/skcapstone/cli/agents_spawner.py +5 -2
  76. package/src/skcapstone/cli/alerts.py +25 -4
  77. package/src/skcapstone/cli/bench.py +15 -15
  78. package/src/skcapstone/cli/chat.py +7 -4
  79. package/src/skcapstone/cli/consciousness.py +5 -2
  80. package/src/skcapstone/cli/context_cmd.py +18 -4
  81. package/src/skcapstone/cli/daemon.py +11 -7
  82. package/src/skcapstone/cli/gtd.py +26 -1
  83. package/src/skcapstone/cli/housekeeping.py +3 -3
  84. package/src/skcapstone/cli/identity_cmd.py +378 -0
  85. package/src/skcapstone/cli/joule_cmd.py +7 -3
  86. package/src/skcapstone/cli/memory.py +8 -6
  87. package/src/skcapstone/cli/peers_dir.py +1 -1
  88. package/src/skcapstone/cli/register_cmd.py +29 -3
  89. package/src/skcapstone/cli/scheduler_cmd.py +167 -0
  90. package/src/skcapstone/cli/session.py +25 -0
  91. package/src/skcapstone/cli/setup.py +96 -29
  92. package/src/skcapstone/cli/shell_cmd.py +53 -1
  93. package/src/skcapstone/cli/skills_cmd.py +2 -2
  94. package/src/skcapstone/cli/soul.py +8 -5
  95. package/src/skcapstone/cli/status.py +37 -11
  96. package/src/skcapstone/cli/telegram.py +21 -0
  97. package/src/skcapstone/cli/test_cmd.py +5 -5
  98. package/src/skcapstone/cli/test_connection.py +2 -2
  99. package/src/skcapstone/cli/upgrade_cmd.py +23 -14
  100. package/src/skcapstone/cli/version_cmd.py +1 -1
  101. package/src/skcapstone/cli/watch_cmd.py +9 -6
  102. package/src/skcapstone/cloud9_bridge.py +14 -14
  103. package/src/skcapstone/codex_setup.py +255 -0
  104. package/src/skcapstone/config_validator.py +7 -4
  105. package/src/skcapstone/consciousness_config.py +5 -1
  106. package/src/skcapstone/consciousness_loop.py +313 -273
  107. package/src/skcapstone/context_loader.py +121 -0
  108. package/src/skcapstone/coord_federation.py +2 -1
  109. package/src/skcapstone/coordination.py +23 -6
  110. package/src/skcapstone/crush_integration.py +2 -1
  111. package/src/skcapstone/daemon.py +132 -77
  112. package/src/skcapstone/dashboard.py +10 -10
  113. package/src/skcapstone/data/sk-agent-picker.sh +421 -0
  114. package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
  115. package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
  116. package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
  117. package/src/skcapstone/data/systemd/skcapstone.service +37 -0
  118. package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
  119. package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
  120. package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
  121. package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
  122. package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
  123. package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
  124. package/src/skcapstone/defaults/claude/settings.json +74 -0
  125. package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
  126. package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
  127. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
  128. package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
  129. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
  130. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
  131. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
  132. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
  133. package/src/skcapstone/defaults/unhinged.json +13 -0
  134. package/src/skcapstone/discovery.py +43 -20
  135. package/src/skcapstone/doctor.py +941 -22
  136. package/src/skcapstone/dreaming.py +1183 -109
  137. package/src/skcapstone/emotion_tracker.py +2 -2
  138. package/src/skcapstone/export.py +4 -3
  139. package/src/skcapstone/fuse_mount.py +14 -12
  140. package/src/skcapstone/gui_installer.py +2 -2
  141. package/src/skcapstone/heartbeat.py +1 -1
  142. package/src/skcapstone/housekeeping.py +14 -14
  143. package/src/skcapstone/install_wizard.py +209 -7
  144. package/src/skcapstone/itil.py +13 -4
  145. package/src/skcapstone/kms_scheduler.py +10 -8
  146. package/src/skcapstone/launchd.py +19 -19
  147. package/src/skcapstone/mcp_launcher.py +15 -1
  148. package/src/skcapstone/mcp_server.py +83 -49
  149. package/src/skcapstone/mcp_tools/__init__.py +2 -0
  150. package/src/skcapstone/mcp_tools/_helpers.py +2 -2
  151. package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
  152. package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
  153. package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
  154. package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
  155. package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
  156. package/src/skcapstone/mcp_tools/did_tools.py +11 -8
  157. package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
  158. package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
  159. package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
  160. package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
  161. package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
  162. package/src/skcapstone/mdns_discovery.py +2 -2
  163. package/src/skcapstone/memory_curator.py +1 -1
  164. package/src/skcapstone/memory_engine.py +10 -3
  165. package/src/skcapstone/metrics.py +30 -16
  166. package/src/skcapstone/migrate_memories.py +4 -3
  167. package/src/skcapstone/migrate_multi_agent.py +8 -7
  168. package/src/skcapstone/models.py +47 -5
  169. package/src/skcapstone/notifications.py +42 -18
  170. package/src/skcapstone/onboard.py +875 -121
  171. package/src/skcapstone/operator_link.py +170 -0
  172. package/src/skcapstone/peer_directory.py +4 -4
  173. package/src/skcapstone/peers.py +19 -19
  174. package/src/skcapstone/pillars/__init__.py +7 -5
  175. package/src/skcapstone/pillars/consciousness.py +191 -0
  176. package/src/skcapstone/pillars/identity.py +51 -7
  177. package/src/skcapstone/pillars/memory.py +9 -3
  178. package/src/skcapstone/pillars/sync.py +2 -2
  179. package/src/skcapstone/preflight.py +3 -3
  180. package/src/skcapstone/providers/docker.py +28 -28
  181. package/src/skcapstone/register.py +6 -6
  182. package/src/skcapstone/registry_client.py +5 -4
  183. package/src/skcapstone/runtime.py +14 -3
  184. package/src/skcapstone/scheduled_tasks.py +254 -19
  185. package/src/skcapstone/scheduler_jobs.py +456 -0
  186. package/src/skcapstone/scheduler_runner.py +239 -0
  187. package/src/skcapstone/scheduler_state.py +162 -0
  188. package/src/skcapstone/sdk.py +310 -0
  189. package/src/skcapstone/service_health.py +279 -39
  190. package/src/skcapstone/session_briefing.py +108 -0
  191. package/src/skcapstone/session_capture.py +1 -1
  192. package/src/skcapstone/shell.py +7 -1
  193. package/src/skcapstone/soul.py +3 -1
  194. package/src/skcapstone/soul_switch.py +3 -1
  195. package/src/skcapstone/summary.py +6 -6
  196. package/src/skcapstone/sync_engine.py +15 -15
  197. package/src/skcapstone/sync_watcher.py +2 -2
  198. package/src/skcapstone/systemd.py +55 -21
  199. package/src/skcapstone/team_comms.py +8 -8
  200. package/src/skcapstone/team_engine.py +1 -1
  201. package/src/skcapstone/testrunner.py +3 -3
  202. package/src/skcapstone/trust_graph.py +40 -5
  203. package/src/skcapstone/unified_search.py +15 -6
  204. package/src/skcapstone/uninstall_wizard.py +11 -3
  205. package/src/skcapstone/version_check.py +8 -4
  206. package/src/skcapstone/warmth_anchor.py +4 -2
  207. package/src/skcapstone/whoami.py +4 -4
  208. package/systemd/skcapstone.service +4 -6
  209. package/systemd/skcapstone@.service +7 -8
  210. package/systemd/skcomms-heartbeat.service +21 -0
  211. package/systemd/skcomms-heartbeat.timer +12 -0
  212. package/systemd/skcomms-queue-drain.service +17 -0
  213. package/systemd/skcomms-queue-drain.timer +12 -0
  214. package/tests/conftest.py +39 -0
  215. package/tests/integration/test_consciousness_e2e.py +39 -39
  216. package/tests/test_agent_card.py +1 -1
  217. package/tests/test_agent_home_scaffold.py +34 -0
  218. package/tests/test_alerts_consumer_topics.py +27 -0
  219. package/tests/test_backup.py +2 -1
  220. package/tests/test_chat.py +6 -6
  221. package/tests/test_claude_md.py +2 -2
  222. package/tests/test_cli_skills.py +10 -10
  223. package/tests/test_cli_test_cmd.py +4 -4
  224. package/tests/test_cli_test_connection.py +1 -1
  225. package/tests/test_cloud9_bridge.py +6 -6
  226. package/tests/test_consciousness_e2e.py +1 -1
  227. package/tests/test_consciousness_loop.py +10 -10
  228. package/tests/test_coordination.py +25 -0
  229. package/tests/test_cross_package.py +21 -21
  230. package/tests/test_daemon.py +4 -4
  231. package/tests/test_daemon_shutdown.py +1 -1
  232. package/tests/test_docker_provider.py +29 -29
  233. package/tests/test_doctor.py +400 -0
  234. package/tests/test_doctor_skscheduler.py +50 -0
  235. package/tests/test_dreaming_engine.py +147 -0
  236. package/tests/test_dreaming_gtd_capture.py +35 -0
  237. package/tests/test_e2e_automated.py +8 -5
  238. package/tests/test_fuse_mount.py +10 -10
  239. package/tests/test_gtd_brief.py +46 -0
  240. package/tests/test_gtd_malformed_tolerance.py +31 -0
  241. package/tests/test_housekeeping.py +15 -15
  242. package/tests/test_identity_migrate.py +251 -0
  243. package/tests/test_integration_backbone.py +598 -0
  244. package/tests/test_itil_gtd_lifecycle.py +37 -0
  245. package/tests/test_jobs_dropins.py +84 -0
  246. package/tests/test_mcp_server.py +82 -37
  247. package/tests/test_models.py +48 -4
  248. package/tests/test_multi_agent.py +31 -29
  249. package/tests/test_notifications.py +122 -32
  250. package/tests/test_onboard.py +63 -75
  251. package/tests/test_operator_link.py +78 -0
  252. package/tests/test_peers.py +14 -14
  253. package/tests/test_pillars.py +98 -0
  254. package/tests/test_preflight.py +3 -3
  255. package/tests/test_runtime.py +21 -0
  256. package/tests/test_scheduled_tasks.py +11 -6
  257. package/tests/test_scheduler_cli.py +47 -0
  258. package/tests/test_scheduler_features.py +133 -0
  259. package/tests/test_scheduler_integration.py +87 -0
  260. package/tests/test_scheduler_jobs.py +155 -0
  261. package/tests/test_scheduler_runner.py +64 -0
  262. package/tests/test_scheduler_state.py +57 -0
  263. package/tests/test_sdk.py +70 -0
  264. package/tests/test_service_health_incidents.py +34 -0
  265. package/tests/test_service_registry.py +52 -0
  266. package/tests/test_session_briefing.py +130 -0
  267. package/tests/test_snapshots.py +4 -4
  268. package/tests/test_sync_pipeline.py +26 -26
  269. package/tests/test_team_comms.py +2 -2
  270. package/tests/test_testrunner.py +2 -2
  271. package/tests/test_trust_graph.py +18 -0
  272. package/tests/test_unified_search.py +2 -2
  273. package/tests/test_version_check.py +10 -0
  274. package/tests/test_version_cmd.py +8 -8
  275. package/tests/test_whoami.py +1 -1
  276. package/systemd/skcomm-heartbeat.service +0 -18
  277. package/systemd/skcomm-queue-drain.service +0 -17
  278. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
  279. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
@@ -139,7 +139,7 @@ class HealthResponse(BaseModel):
139
139
  )
140
140
  backend_health: Dict[str, Any] = Field(
141
141
  default_factory=dict,
142
- description="Per-transport liveness flags (e.g. {skcomm: true}).",
142
+ description="Per-transport liveness flags (e.g. {skcomms: true}).",
143
143
  )
144
144
  disk_free_gb: float = Field(0.0, description="Free disk space in gigabytes.")
145
145
  memory_usage_mb: float = Field(0.0, description="Current RSS memory usage in MB.")
@@ -507,7 +507,7 @@ def _check_bearer(
507
507
  ) -> str:
508
508
  """Validate a CapAuth Bearer token for privileged endpoints.
509
509
 
510
- Attempts CapAuth validation first (if skcomm is installed), then falls
510
+ Attempts CapAuth validation first (if skcomms is installed), then falls
511
511
  back to skcapstone signed token verification.
512
512
 
513
513
  Args:
@@ -527,7 +527,7 @@ def _check_bearer(
527
527
  config = _ctx.get("config")
528
528
 
529
529
  try:
530
- from skcomm.capauth_validator import CapAuthValidator
530
+ from skcomms.capauth_validator import CapAuthValidator
531
531
 
532
532
  fingerprint = CapAuthValidator(require_auth=True).validate(token_str)
533
533
  except ImportError:
@@ -828,23 +828,23 @@ async def get_dashboard(
828
828
  "consciousness": m.consciousness.value if hasattr(m, "consciousness") else "",
829
829
  "version": m.version,
830
830
  }
831
- except Exception:
832
- pass
831
+ except Exception as exc:
832
+ logger.warning("Failed to read agent identity from runtime manifest: %s", exc)
833
833
  if not agent:
834
834
  try:
835
835
  identity_path = config.home / "identity" / "identity.json"
836
836
  if identity_path.exists():
837
837
  agent = json.loads(identity_path.read_text(encoding="utf-8"))
838
- except Exception:
839
- pass
838
+ except Exception as exc:
839
+ logger.warning("Failed to read identity.json for API status: %s", exc)
840
840
 
841
841
  # Consciousness stats
842
842
  c_stats: Dict[str, Any] = {}
843
843
  if consciousness:
844
844
  try:
845
845
  c_stats = dict(consciousness.stats)
846
- except Exception:
847
- pass
846
+ except Exception as exc:
847
+ logger.warning("Failed to read consciousness stats: %s", exc)
848
848
 
849
849
  # Recent conversations
850
850
  conversations: List[Dict[str, Any]] = []
@@ -862,8 +862,8 @@ async def get_dashboard(
862
862
  "last": last.get("timestamp"),
863
863
  "preview": preview,
864
864
  })
865
- except Exception:
866
- pass
865
+ except Exception as exc:
866
+ logger.warning("Failed to list recent conversations for API status: %s", exc)
867
867
 
868
868
  daemon_summary = DaemonSummary(
869
869
  running=snap.get("running", True),
@@ -924,8 +924,8 @@ async def get_capstone(
924
924
  m = runtime.manifest
925
925
  agent = {"name": m.identity.name, "fingerprint": m.identity.fingerprint or ""}
926
926
  pillars = {k: v.value for k, v in m.pillar_summary.items()}
927
- except Exception:
928
- pass
927
+ except Exception as exc:
928
+ logger.warning("Failed to read agent pillars from runtime manifest: %s", exc)
929
929
 
930
930
  # Memory stats
931
931
  memory = MemorySummary()
@@ -940,8 +940,8 @@ async def get_capstone(
940
940
  long_term=ms.long_term,
941
941
  status=ms.status.value,
942
942
  )
943
- except Exception:
944
- pass
943
+ except Exception as exc:
944
+ logger.warning("Failed to collect memory stats for capstone API: %s", exc)
945
945
 
946
946
  # Coordination board
947
947
  board: Dict[str, Any] = {"summary": {}, "active": []}
@@ -970,16 +970,16 @@ async def get_capstone(
970
970
  if v.status.value in ("in_progress", "claimed")
971
971
  ],
972
972
  }
973
- except Exception:
974
- pass
973
+ except Exception as exc:
974
+ logger.warning("Failed to collect coordination board data for capstone API: %s", exc)
975
975
 
976
976
  # Consciousness stats
977
977
  c_stats: Dict[str, Any] = {}
978
978
  if consciousness:
979
979
  try:
980
980
  c_stats = dict(consciousness.stats)
981
- except Exception:
982
- pass
981
+ except Exception as exc:
982
+ logger.warning("Failed to read consciousness stats for capstone API: %s", exc)
983
983
 
984
984
  return CapstoneResponse(
985
985
  agent=agent,
@@ -1027,8 +1027,8 @@ async def get_activity_stream(
1027
1027
  try:
1028
1028
  for chunk in _activity.get_history_encoded():
1029
1029
  yield chunk
1030
- except Exception:
1031
- pass
1030
+ except Exception as exc:
1031
+ logger.warning("Failed to replay activity stream history: %s", exc)
1032
1032
  # Stream live events; yield keep-alive comments on timeout
1033
1033
  try:
1034
1034
  while True:
@@ -1100,8 +1100,8 @@ async def list_household_agents(
1100
1100
  if identity_path.exists():
1101
1101
  try:
1102
1102
  entry["identity"] = json.loads(identity_path.read_text(encoding="utf-8"))
1103
- except Exception:
1104
- pass
1103
+ except Exception as exc:
1104
+ logger.warning("Failed to read identity for agent %s: %s", agent_name, exc)
1105
1105
 
1106
1106
  hb: Optional[Dict[str, Any]] = None
1107
1107
  hb_path = heartbeats_dir / f"{agent_name.lower()}.json"
@@ -1112,7 +1112,8 @@ async def list_household_agents(
1112
1112
  hb["alive"] = alive
1113
1113
  entry["heartbeat"] = hb
1114
1114
  entry["status"] = hb.get("status", "unknown") if alive else "stale"
1115
- except Exception:
1115
+ except Exception as exc:
1116
+ logger.warning("Failed to read heartbeat for agent %s: %s", agent_name, exc)
1116
1117
  entry["status"] = "unknown"
1117
1118
  else:
1118
1119
  entry["status"] = "no_heartbeat"
@@ -1174,8 +1175,8 @@ async def get_household_agent(
1174
1175
  if identity_path.exists():
1175
1176
  try:
1176
1177
  entry["identity"] = json.loads(identity_path.read_text(encoding="utf-8"))
1177
- except Exception:
1178
- pass
1178
+ except Exception as exc:
1179
+ logger.warning("Failed to read identity for agent %s: %s", name, exc)
1179
1180
 
1180
1181
  hb_path = config.shared_root / "heartbeats" / f"{name.lower()}.json"
1181
1182
  if hb_path.exists():
@@ -1185,8 +1186,8 @@ async def get_household_agent(
1185
1186
  hb["alive"] = alive
1186
1187
  entry["heartbeat"] = hb
1187
1188
  entry["status"] = hb.get("status", "unknown") if alive else "stale"
1188
- except Exception:
1189
- pass
1189
+ except Exception as exc:
1190
+ logger.warning("Failed to read heartbeat for agent %s: %s", name, exc)
1190
1191
 
1191
1192
  # Memory count
1192
1193
  memory_dir = agent_dir / "memory"
@@ -1249,8 +1250,8 @@ async def list_conversations(
1249
1250
  last_message_preview=(last_content or "")[:120],
1250
1251
  )
1251
1252
  )
1252
- except Exception:
1253
- pass
1253
+ except Exception as exc:
1254
+ logger.warning("Failed to read conversation file %s: %s", cf, exc)
1254
1255
  return ConversationsResponse(conversations=conversations)
1255
1256
 
1256
1257
 
@@ -1329,7 +1330,7 @@ async def send_message(
1329
1330
  ) -> SendMessageResponse:
1330
1331
  """Send a message to a named peer.
1331
1332
 
1332
- Writes the message envelope to the SKComm outbox for delivery by the
1333
+ Writes the message envelope to the SKComms outbox for delivery by the
1333
1334
  transport layer. If the consciousness loop is running the message is
1334
1335
  also processed inline to generate a reply.
1335
1336
 
@@ -1534,8 +1535,8 @@ def _load_first_argocd_doc(path: Path) -> Optional[dict]:
1534
1535
  "sync_policy": spec.get("syncPolicy", {}),
1535
1536
  "manifest_file": path.name,
1536
1537
  }
1537
- except Exception:
1538
- pass
1538
+ except Exception as exc:
1539
+ logger.warning("Failed to parse ArgoCD manifest %s: %s", path, exc)
1539
1540
  return None
1540
1541
 
1541
1542
 
@@ -1618,8 +1619,8 @@ def _get_argocd_status() -> dict:
1618
1619
  "last_synced": (item_status.get("operationState") or {}).get("finishedAt"),
1619
1620
  }
1620
1621
  source = "yaml+kubectl"
1621
- except Exception:
1622
- pass
1622
+ except Exception as exc:
1623
+ logger.warning("kubectl ArgoCD status query failed (using yaml only): %s", exc)
1623
1624
 
1624
1625
  # ── Merge and build output ───────────────────────────────────────────────
1625
1626
  apps = []
@@ -1737,7 +1738,7 @@ async def websocket_logs(
1737
1738
  config = _ctx.get("config")
1738
1739
 
1739
1740
  try:
1740
- from skcomm.capauth_validator import CapAuthValidator
1741
+ from skcomms.capauth_validator import CapAuthValidator
1741
1742
 
1742
1743
  fingerprint = CapAuthValidator(require_auth=True).validate(token_str)
1743
1744
  except ImportError:
@@ -1767,8 +1768,8 @@ async def websocket_logs(
1767
1768
  tail_lines = list(deque(fh, maxlen=50))
1768
1769
  for line in tail_lines:
1769
1770
  await websocket.send_json({"type": "line", "line": line.rstrip("\n")})
1770
- except Exception:
1771
- pass
1771
+ except Exception as exc:
1772
+ logger.warning("Failed to replay log tail history over websocket: %s", exc)
1772
1773
 
1773
1774
  # Tail the log file and stream new lines
1774
1775
  try:
@@ -1783,8 +1784,8 @@ async def websocket_logs(
1783
1784
  for ln in chunk.splitlines():
1784
1785
  await websocket.send_json({"type": "line", "line": ln})
1785
1786
  offset = fh.tell()
1786
- except Exception:
1787
- pass
1787
+ except Exception as exc:
1788
+ logger.warning("Log tail websocket read error: %s", exc)
1788
1789
  await asyncio.sleep(0.5)
1789
1790
  except WebSocketDisconnect:
1790
1791
  pass
@@ -95,7 +95,8 @@ def _collect_local_bid(task_id: str, agent_name: str, shared_root: Path) -> Auct
95
95
  import psutil # optional dep
96
96
 
97
97
  cpu = psutil.cpu_percent(interval=0.1)
98
- except Exception:
98
+ except Exception as e:
99
+ logger.warning("auction.py: %s", e)
99
100
  cpu = 0.0
100
101
 
101
102
  # Claimed-tasks count from agent file
@@ -107,8 +108,8 @@ def _collect_local_bid(task_id: str, agent_name: str, shared_root: Path) -> Auct
107
108
  agent_file = board.load_agent(agent_name)
108
109
  if agent_file:
109
110
  claimed_count = len(agent_file.claimed_tasks)
110
- except Exception:
111
- pass
111
+ except Exception as exc:
112
+ logger.warning("Failed to read claimed task count for agent %s: %s", agent_name, exc)
112
113
 
113
114
  return AuctionBid(
114
115
  task_id=task_id,
@@ -283,8 +284,8 @@ class AuctionManager:
283
284
  ttl_seconds=3600,
284
285
  tags=["auction", "no_bidders"],
285
286
  )
286
- except Exception:
287
- pass
287
+ except Exception as exc:
288
+ logger.warning("Failed to publish auction_no_bidders event for %s: %s", task_id, exc)
288
289
  return None
289
290
 
290
291
  winner_bid = min(record.bids, key=_load_score)
@@ -327,8 +328,8 @@ class AuctionManager:
327
328
  ttl_seconds=3600,
328
329
  tags=["auction", "resolved"],
329
330
  )
330
- except Exception:
331
- pass
331
+ except Exception as exc:
332
+ logger.warning("Failed to publish auction_resolved event for %s: %s", task_id, exc)
332
333
 
333
334
  # Notify activity stream
334
335
  try:
@@ -338,8 +339,8 @@ class AuctionManager:
338
339
  "task.auction_resolved",
339
340
  {"task_id": task_id, "winner": winner, "bids": len(record.bids)},
340
341
  )
341
- except Exception:
342
- pass
342
+ except Exception as exc:
343
+ logger.warning("Failed to push auction_resolved activity for %s: %s", task_id, exc)
343
344
 
344
345
  return winner
345
346
 
@@ -361,7 +362,8 @@ class AuctionManager:
361
362
  key=lambda p: p.stat().st_mtime,
362
363
  reverse=True,
363
364
  )[:limit]
364
- except Exception:
365
+ except Exception as e:
366
+ logger.warning("auction.py: %s", e)
365
367
  paths = []
366
368
 
367
369
  for path in paths:
@@ -371,7 +373,8 @@ class AuctionManager:
371
373
  json.loads(path.read_text(encoding="utf-8"))
372
374
  )
373
375
  )
374
- except Exception:
376
+ except Exception as e:
377
+ logger.warning("auction.py: %s", e)
375
378
  continue
376
379
 
377
380
  return {
@@ -340,6 +340,7 @@ def _read_agent_name(home: Path) -> str:
340
340
  name = data.get("name") or data.get("agent_name")
341
341
  if name:
342
342
  return name
343
- except Exception:
343
+ except Exception as e:
344
+ logger.warning("backup.py: %s", e)
344
345
  continue
345
346
  return "unknown"
@@ -183,8 +183,8 @@ class BlueprintRegistryClient:
183
183
  error_body = ""
184
184
  try:
185
185
  error_body = exc.read().decode("utf-8", errors="replace")
186
- except Exception:
187
- pass
186
+ except Exception as read_exc:
187
+ logger.debug("Failed to read error body from registry HTTP error: %s", read_exc)
188
188
  msg = f"Registry API error {exc.code} for {method} {path}"
189
189
  if error_body:
190
190
  msg += f": {error_body[:500]}"
@@ -406,7 +406,8 @@ def _fetch_github_blueprints(query: str = "") -> Optional[list[dict[str, Any]]]:
406
406
  )
407
407
  with urllib.request.urlopen(cat_req, timeout=10) as resp:
408
408
  files = json.loads(resp.read().decode("utf-8"))
409
- except Exception:
409
+ except Exception as e:
410
+ logger.warning("blueprint_registry.py: %s", e)
410
411
  continue
411
412
 
412
413
  for file_entry in files:
@@ -0,0 +1,238 @@
1
+ """
2
+ Brain-First Protocol — think before you act.
3
+
4
+ Before an agent acts on any task, it consults its memory to see if it
5
+ already knows something relevant. This avoids redundant work, surfaces
6
+ prior decisions, and grounds the agent in its own experience.
7
+
8
+ Usage:
9
+ from skcapstone.brain_first import brain_first_check
10
+
11
+ result = brain_first_check("deploy the monitoring stack")
12
+ if result.has_memories:
13
+ # use result.memories as additional context
14
+ ...
15
+
16
+ Configuration (config.yaml):
17
+ brain_first:
18
+ enabled: true # master toggle (default: true)
19
+ max_results: 5 # how many memories to surface (default: 5)
20
+ min_importance: 0.3 # ignore low-importance memories (default: 0.3)
21
+ auto_inject: false # auto-prepend memories to MCP tool responses (default: false)
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ import os
28
+ import re
29
+ from dataclasses import dataclass, field
30
+ from pathlib import Path
31
+ from typing import Optional
32
+
33
+ logger = logging.getLogger("skcapstone.brain_first")
34
+
35
+ # Stop-words to strip from queries before searching memory
36
+ _STOP_WORDS = frozenset({
37
+ "a", "an", "the", "is", "are", "was", "were", "be", "been", "being",
38
+ "have", "has", "had", "do", "does", "did", "will", "would", "shall",
39
+ "should", "may", "might", "must", "can", "could", "to", "of", "in",
40
+ "for", "on", "with", "at", "by", "from", "as", "into", "through",
41
+ "during", "before", "after", "above", "below", "between", "out",
42
+ "off", "over", "under", "again", "further", "then", "once", "here",
43
+ "there", "when", "where", "why", "how", "all", "each", "every",
44
+ "both", "few", "more", "most", "other", "some", "such", "no", "nor",
45
+ "not", "only", "own", "same", "so", "than", "too", "very", "just",
46
+ "because", "but", "and", "or", "if", "while", "about", "up", "it",
47
+ "its", "this", "that", "these", "those", "i", "me", "my", "we",
48
+ "our", "you", "your", "he", "him", "his", "she", "her", "they",
49
+ "them", "their", "what", "which", "who", "whom",
50
+ })
51
+
52
+
53
+ @dataclass
54
+ class BrainFirstConfig:
55
+ """Configuration for the brain-first protocol."""
56
+
57
+ enabled: bool = True
58
+ max_results: int = 5
59
+ min_importance: float = 0.3
60
+ auto_inject: bool = False
61
+
62
+ @classmethod
63
+ def from_dict(cls, data: dict) -> "BrainFirstConfig":
64
+ """Create config from a dict (e.g. from config.yaml brain_first section)."""
65
+ return cls(
66
+ enabled=data.get("enabled", True),
67
+ max_results=data.get("max_results", 5),
68
+ min_importance=data.get("min_importance", 0.3),
69
+ auto_inject=data.get("auto_inject", False),
70
+ )
71
+
72
+
73
+ @dataclass
74
+ class BrainFirstResult:
75
+ """Result of a brain-first memory consultation."""
76
+
77
+ query: str
78
+ keywords: list[str]
79
+ memories: list[dict] = field(default_factory=list)
80
+ enabled: bool = True
81
+ error: Optional[str] = None
82
+
83
+ @property
84
+ def has_memories(self) -> bool:
85
+ """Whether any relevant memories were found."""
86
+ return len(self.memories) > 0
87
+
88
+ def as_context(self) -> str:
89
+ """Format memories as a context block for injection into prompts."""
90
+ if not self.has_memories:
91
+ return ""
92
+ lines = ["[Brain-First: relevant memories found]"]
93
+ for i, mem in enumerate(self.memories, 1):
94
+ content = mem.get("content", "")[:200]
95
+ layer = mem.get("layer", "?")
96
+ importance = mem.get("importance", 0)
97
+ tags = ", ".join(mem.get("tags", []))
98
+ lines.append(
99
+ f" {i}. [{layer}|imp={importance:.1f}] {content}"
100
+ + (f" tags: {tags}" if tags else "")
101
+ )
102
+ return "\n".join(lines)
103
+
104
+
105
+ def extract_keywords(text: str) -> list[str]:
106
+ """Extract meaningful keywords from a text string.
107
+
108
+ Strips stop-words and short tokens, keeping domain-relevant terms.
109
+
110
+ Args:
111
+ text: Input text (task title, prompt, etc.).
112
+
113
+ Returns:
114
+ List of unique keywords, longest first.
115
+ """
116
+ # Lowercase, split on non-alphanumeric
117
+ tokens = re.split(r"[^a-zA-Z0-9_-]+", text.lower())
118
+ # Filter: no stop-words, no short tokens
119
+ keywords = list(dict.fromkeys(
120
+ t for t in tokens if t and t not in _STOP_WORDS and len(t) > 2
121
+ ))
122
+ # Sort longest first (longer terms tend to be more specific)
123
+ keywords.sort(key=len, reverse=True)
124
+ return keywords
125
+
126
+
127
+ def _load_config() -> BrainFirstConfig:
128
+ """Load brain-first config from the agent's config.yaml.
129
+
130
+ Falls back to defaults if the file or section is missing.
131
+ """
132
+ try:
133
+ import yaml
134
+ except ImportError:
135
+ return BrainFirstConfig()
136
+
137
+ from . import AGENT_HOME, SKCAPSTONE_AGENT
138
+
139
+ for base in [
140
+ Path(AGENT_HOME).expanduser() / "agents" / SKCAPSTONE_AGENT,
141
+ Path(AGENT_HOME).expanduser(),
142
+ ]:
143
+ config_file = base / "config" / "config.yaml"
144
+ if config_file.exists():
145
+ try:
146
+ data = yaml.safe_load(config_file.read_text(encoding="utf-8")) or {}
147
+ bf_data = data.get("brain_first", {})
148
+ if bf_data:
149
+ return BrainFirstConfig.from_dict(bf_data)
150
+ except Exception as exc:
151
+ logger.debug("Failed to load brain_first config from %s: %s", config_file, exc)
152
+
153
+ return BrainFirstConfig()
154
+
155
+
156
+ def brain_first_check(
157
+ context: str,
158
+ config: Optional[BrainFirstConfig] = None,
159
+ tags: Optional[list[str]] = None,
160
+ ) -> BrainFirstResult:
161
+ """Consult memory before acting on a task.
162
+
163
+ This is the core brain-first function. Given a task description or
164
+ prompt context, it extracts keywords, searches memory, and returns
165
+ any relevant memories that the agent should consider.
166
+
167
+ Args:
168
+ context: The task description, prompt, or action context.
169
+ config: Override config (uses agent config.yaml if None).
170
+ tags: Optional tag filter for the memory search.
171
+
172
+ Returns:
173
+ BrainFirstResult with any relevant memories.
174
+ """
175
+ if config is None:
176
+ config = _load_config()
177
+
178
+ keywords = extract_keywords(context)
179
+ result = BrainFirstResult(
180
+ query=context,
181
+ keywords=keywords,
182
+ enabled=config.enabled,
183
+ )
184
+
185
+ if not config.enabled:
186
+ result.error = "brain-first protocol disabled"
187
+ return result
188
+
189
+ if not keywords:
190
+ result.error = "no meaningful keywords extracted"
191
+ return result
192
+
193
+ # Build a search query from top keywords (limit to 6 to avoid noise)
194
+ search_query = " ".join(keywords[:6])
195
+
196
+ try:
197
+ from .memory_engine import search as memory_search
198
+ from .mcp_tools._helpers import _home
199
+
200
+ home = _home()
201
+ entries = memory_search(
202
+ home=home,
203
+ query=search_query,
204
+ tags=tags,
205
+ limit=config.max_results * 2, # over-fetch, then filter
206
+ )
207
+
208
+ # Filter by minimum importance
209
+ entries = [e for e in entries if e.importance >= config.min_importance]
210
+
211
+ # Truncate to max_results
212
+ entries = entries[:config.max_results]
213
+
214
+ result.memories = [
215
+ {
216
+ "memory_id": e.memory_id,
217
+ "content": e.content[:300],
218
+ "layer": e.layer.value,
219
+ "tags": e.tags,
220
+ "importance": e.importance,
221
+ "access_count": e.access_count,
222
+ "source": e.source,
223
+ }
224
+ for e in entries
225
+ ]
226
+
227
+ logger.info(
228
+ "Brain-first check: %d memories found for %d keywords from '%s'",
229
+ len(result.memories),
230
+ len(keywords),
231
+ context[:80],
232
+ )
233
+
234
+ except Exception as exc:
235
+ result.error = f"memory search failed: {exc}"
236
+ logger.warning("Brain-first check failed: %s", exc)
237
+
238
+ return result
@@ -21,7 +21,7 @@ from .coordination import Board, TaskStatus
21
21
 
22
22
 
23
23
  TAG_CATEGORIES = {
24
- "feature": ["skcapstone", "skchat", "skcomm", "skmemory", "capauth", "cloud9", "skworld"],
24
+ "feature": ["skcapstone", "skchat", "skcomms", "skmemory", "capauth", "cloud9", "skworld"],
25
25
  "security": ["security", "encryption", "capauth", "integrity", "quantum-resistant", "pgp"],
26
26
  "infrastructure": ["ci", "docker", "systemd", "daemon", "syncthing", "pypi", "packaging"],
27
27
  "documentation": ["documentation", "docs", "readme", "quickstart", "api"],