@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
@@ -1,4 +1,4 @@
1
- """SKComm send/receive messaging tools."""
1
+ """SKComms send/receive messaging tools."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -10,7 +10,7 @@ TOOLS: list[Tool] = [
10
10
  Tool(
11
11
  name="send_message",
12
12
  description=(
13
- "Send a message to another agent via SKComm. "
13
+ "Send a message to another agent via SKComms. "
14
14
  "Routes through available transports (Syncthing, file)."
15
15
  ),
16
16
  inputSchema={
@@ -36,7 +36,7 @@ TOOLS: list[Tool] = [
36
36
  Tool(
37
37
  name="check_inbox",
38
38
  description=(
39
- "Check for new incoming messages across all SKComm transports. "
39
+ "Check for new incoming messages across all SKComms transports. "
40
40
  "Returns any unread message envelopes."
41
41
  ),
42
42
  inputSchema={"type": "object", "properties": {}, "required": []},
@@ -45,15 +45,15 @@ TOOLS: list[Tool] = [
45
45
 
46
46
 
47
47
  async def _handle_send_message(args: dict) -> list[TextContent]:
48
- """Send a message via SKComm."""
48
+ """Send a message via SKComms."""
49
49
  recipient = args.get("recipient", "")
50
50
  message = args.get("message", "")
51
51
  if not recipient or not message:
52
52
  return _error_response("recipient and message are required")
53
53
 
54
54
  try:
55
- from skcomm.core import SKComm
56
- comm = SKComm.from_config()
55
+ from skcomms.core import SKComms
56
+ comm = SKComms.from_config()
57
57
  report = comm.send(recipient, message)
58
58
  return _json_response({
59
59
  "sent": report.success,
@@ -68,7 +68,7 @@ async def _handle_send_message(args: dict) -> list[TextContent]:
68
68
  ],
69
69
  })
70
70
  except ImportError:
71
- return _error_response("SKComm not installed. Run: pip install skcomm")
71
+ return _error_response("SKComms not installed. Run: pip install skcomms")
72
72
  except Exception as exc:
73
73
  return _error_response(f"Send failed: {exc}")
74
74
 
@@ -76,8 +76,8 @@ async def _handle_send_message(args: dict) -> list[TextContent]:
76
76
  async def _handle_check_inbox(_args: dict) -> list[TextContent]:
77
77
  """Check for incoming messages."""
78
78
  try:
79
- from skcomm.core import SKComm
80
- comm = SKComm.from_config()
79
+ from skcomms.core import SKComms
80
+ comm = SKComms.from_config()
81
81
  envelopes = comm.receive()
82
82
  return _json_response([
83
83
  {
@@ -93,7 +93,7 @@ async def _handle_check_inbox(_args: dict) -> list[TextContent]:
93
93
  for e in envelopes
94
94
  ])
95
95
  except ImportError:
96
- return _error_response("SKComm not installed. Run: pip install skcomm")
96
+ return _error_response("SKComms not installed. Run: pip install skcomms")
97
97
  except Exception as exc:
98
98
  return _error_response(f"Inbox check failed: {exc}")
99
99
 
@@ -2,10 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
6
+
5
7
  from mcp.types import TextContent, Tool
6
8
 
7
9
  from ._helpers import _error_response, _home, _json_response, _shared_root
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
9
13
  TOOLS: list[Tool] = [
10
14
  Tool(
11
15
  name="coord_status",
@@ -149,8 +153,8 @@ async def _handle_coord_claim(args: dict) -> list[TextContent]:
149
153
  try:
150
154
  from .. import activity
151
155
  activity.push("task.claimed", {"task_id": task_id, "agent": agent_name})
152
- except Exception:
153
- pass
156
+ except Exception as exc:
157
+ logger.warning("Failed to push task.claimed activity for %s: %s", task_id, exc)
154
158
  return _json_response({
155
159
  "claimed": True,
156
160
  "task_id": task_id,
@@ -175,8 +179,8 @@ async def _handle_coord_complete(args: dict) -> list[TextContent]:
175
179
  try:
176
180
  from .. import activity
177
181
  activity.push("task.completed", {"task_id": task_id, "agent": agent_name})
178
- except Exception:
179
- pass
182
+ except Exception as exc:
183
+ logger.warning("Failed to push task.completed activity for %s: %s", task_id, exc)
180
184
  return _json_response({
181
185
  "completed": True,
182
186
  "task_id": task_id,
@@ -10,6 +10,7 @@ Exposes four tools:
10
10
  from __future__ import annotations
11
11
 
12
12
  import json as _json
13
+ import logging
13
14
  import os as _os
14
15
  import socket as _socket
15
16
  from pathlib import Path
@@ -18,6 +19,8 @@ from mcp.types import TextContent, Tool
18
19
 
19
20
  from ._helpers import _error_response, _home, _json_response
20
21
 
22
+ logger = logging.getLogger(__name__)
23
+
21
24
  TOOLS: list[Tool] = [
22
25
  Tool(
23
26
  name="did_show",
@@ -165,8 +168,8 @@ def _load_policy() -> dict:
165
168
  if p.exists():
166
169
  try:
167
170
  return _json.loads(p.read_text(encoding="utf-8"))
168
- except Exception:
169
- pass
171
+ except Exception as exc:
172
+ logger.warning("Failed to load DID policy from %s: %s", p, exc)
170
173
  return dict(_POLICY_DEFAULT)
171
174
 
172
175
 
@@ -191,8 +194,8 @@ def _resolve_tailnet(tailnet_hostname: str, tailnet_name: str) -> tuple[str, str
191
194
  if not tailnet_hostname:
192
195
  try:
193
196
  tailnet_hostname = _socket.gethostname()
194
- except Exception:
195
- pass
197
+ except Exception as exc:
198
+ logger.warning("Failed to resolve hostname for tailnet DID: %s", exc)
196
199
  if not tailnet_name:
197
200
  tailnet_name = _os.environ.get("SKWORLD_TAILNET", "")
198
201
  return tailnet_hostname, tailnet_name
@@ -297,8 +300,8 @@ async def _handle_did_verify_peer(args: dict) -> list[TextContent]:
297
300
  _json.dumps(peer_data, indent=2, default=str),
298
301
  encoding="utf-8",
299
302
  )
300
- except Exception:
301
- pass
303
+ except Exception as exc:
304
+ logger.warning("Failed to cache did:key back to peer file %s: %s", peer_file, exc)
302
305
 
303
306
  return _json_response({
304
307
  "name": name,
@@ -361,11 +364,11 @@ async def _handle_did_publish(args: dict) -> list[TextContent]:
361
364
  except Exception as exc:
362
365
  errors.append(f"{path}: {exc}")
363
366
 
364
- skcomm_home = Path(_os.environ.get("SKCOMM_HOME", str(Path.home() / ".skcomm")))
367
+ skcomms_home = Path(_os.environ.get("SKCOMMS_HOME", str(Path.home() / ".skcomms")))
365
368
  did_dir = _home() / "did"
366
369
 
367
370
  # Tier 2 (mesh) — always written; used by Tailscale Serve
368
- _write(skcomm_home / "well-known" / "did.json", _json.dumps(docs[DIDTier.WEB_MESH], indent=2))
371
+ _write(skcomms_home / "well-known" / "did.json", _json.dumps(docs[DIDTier.WEB_MESH], indent=2))
369
372
  # Tier 1 (did:key) — always written; self-contained anchor
370
373
  _write(did_dir / "key.json", _json.dumps(docs[DIDTier.KEY], indent=2))
371
374
  _write(did_dir / "did_key.txt", gen._ctx.did_key_id)
@@ -382,7 +382,7 @@ async def _handle_gtd_capture(args: dict) -> list[TextContent]:
382
382
  return _json_response({
383
383
  "captured": True,
384
384
  "id": item["id"],
385
- "text": item["text"],
385
+ "text": item.get("text") or item.get("title") or "",
386
386
  "source": item["source"],
387
387
  "privacy": item["privacy"],
388
388
  "context": item["context"],
@@ -498,7 +498,7 @@ async def _handle_gtd_clarify(args: dict) -> list[TextContent]:
498
498
  return _json_response({
499
499
  "clarified": True,
500
500
  "id": item["id"],
501
- "text": item["text"],
501
+ "text": item.get("text") or item.get("title") or "",
502
502
  "destination": dest_name,
503
503
  "status": item["status"],
504
504
  "priority": item.get("priority"),
@@ -550,7 +550,7 @@ async def _handle_gtd_move(args: dict) -> list[TextContent]:
550
550
  return _json_response({
551
551
  "moved": True,
552
552
  "id": item["id"],
553
- "text": item["text"],
553
+ "text": item.get("text") or item.get("title") or "",
554
554
  "from": source_list,
555
555
  "to": dest_name,
556
556
  "status": item["status"],
@@ -582,7 +582,7 @@ async def _handle_gtd_done(args: dict) -> list[TextContent]:
582
582
  return _json_response({
583
583
  "done": True,
584
584
  "id": item["id"],
585
- "text": item["text"],
585
+ "text": item.get("text") or item.get("title") or "",
586
586
  "from": source_list,
587
587
  "completed_at": item["completed_at"],
588
588
  "archive_count": len(archive),
@@ -2,10 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
6
+
5
7
  from mcp.types import TextContent, Tool
6
8
 
7
9
  from ._helpers import _error_response, _home, _json_response
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
9
13
  TOOLS: list[Tool] = [
10
14
  Tool(
11
15
  name="memory_store",
@@ -130,8 +134,8 @@ async def _handle_memory_store(args: dict) -> list[TextContent]:
130
134
  "importance": entry.importance,
131
135
  "tags": entry.tags,
132
136
  })
133
- except Exception:
134
- pass
137
+ except Exception as exc:
138
+ logger.warning("Failed to push memory.stored activity for %s: %s", entry.memory_id, exc)
135
139
  return _json_response({
136
140
  "memory_id": entry.memory_id,
137
141
  "layer": entry.layer.value,
@@ -54,10 +54,17 @@ async def _handle_send_notification(args: dict) -> list[TextContent]:
54
54
  if urgency not in {"low", "normal", "critical"}:
55
55
  return _error_response("urgency must be one of: low, normal, critical")
56
56
 
57
+ from ..notifications import desktop_notifications_enabled
58
+
59
+ if not desktop_notifications_enabled():
60
+ timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
61
+ return _json_response({"sent": False, "suppressed": True, "timestamp": timestamp})
62
+
57
63
  # Run notify-send in a subprocess (non-blocking).
58
64
  proc = await asyncio.create_subprocess_exec(
59
65
  "notify-send",
60
- "--urgency", urgency,
66
+ "--urgency",
67
+ urgency,
61
68
  title,
62
69
  body,
63
70
  stdout=asyncio.subprocess.DEVNULL,
@@ -71,16 +78,25 @@ async def _handle_send_notification(args: dict) -> list[TextContent]:
71
78
 
72
79
  timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
73
80
 
74
- # Log notification to skcomm/notifications/ (not memory/).
81
+ # Log notification to skcomms/notifications/ (not memory/).
75
82
  try:
76
83
  import json as _j
77
84
  import uuid
85
+
78
86
  home = _home()
79
- agent_name = os.environ.get("SKCAPSTONE_AGENT", "lumina")
80
- notif_dir = home / "agents" / agent_name / "skcomm" / "notifications"
87
+ from .. import active_agent_name
88
+
89
+ agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
90
+ notif_dir = home / "agents" / agent_name / "skcomms" / "notifications"
81
91
  notif_dir.mkdir(parents=True, exist_ok=True)
82
- entry = {"id": uuid.uuid4().hex[:12], "type": "notification-sent",
83
- "title": title, "body": body, "urgency": urgency, "timestamp": timestamp}
92
+ entry = {
93
+ "id": uuid.uuid4().hex[:12],
94
+ "type": "notification-sent",
95
+ "title": title,
96
+ "body": body,
97
+ "urgency": urgency,
98
+ "timestamp": timestamp,
99
+ }
84
100
  (notif_dir / f"{entry['id']}.json").write_text(_j.dumps(entry, indent=2))
85
101
  except Exception:
86
102
  pass # notification log failure must not block the response
@@ -1,8 +1,8 @@
1
- """SKComm notification and status tools.
1
+ """SKComms notification and status tools.
2
2
 
3
3
  Exposes two tools:
4
- comm_notify — Send a notification via SKComm transports
5
- comm_status — Show SKComm subsystem status
4
+ comm_notify — Send a notification via SKComms transports
5
+ comm_status — Show SKComms subsystem status
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -15,7 +15,7 @@ TOOLS: list[Tool] = [
15
15
  Tool(
16
16
  name="comm_notify",
17
17
  description=(
18
- "Send a notification message via SKComm. Routes through "
18
+ "Send a notification message via SKComms. Routes through "
19
19
  "available transports (Syncthing, file, Tailscale). "
20
20
  "Supports urgency levels for priority routing."
21
21
  ),
@@ -46,7 +46,7 @@ TOOLS: list[Tool] = [
46
46
  Tool(
47
47
  name="comm_status",
48
48
  description=(
49
- "Show SKComm subsystem status: installed version, "
49
+ "Show SKComms subsystem status: installed version, "
50
50
  "available transports, connection state, and recent "
51
51
  "delivery statistics."
52
52
  ),
@@ -56,16 +56,16 @@ TOOLS: list[Tool] = [
56
56
 
57
57
 
58
58
  async def _handle_comm_notify(args: dict) -> list[TextContent]:
59
- """Send a notification via SKComm."""
59
+ """Send a notification via SKComms."""
60
60
  recipient = args.get("recipient", "")
61
61
  message = args.get("message", "")
62
62
  if not recipient or not message:
63
63
  return _error_response("recipient and message are required")
64
64
 
65
65
  try:
66
- from skcomm.core import SKComm # type: ignore[import]
66
+ from skcomms.core import SKComms # type: ignore[import]
67
67
 
68
- comm = SKComm.from_config()
68
+ comm = SKComms.from_config()
69
69
  report = comm.send(recipient, message)
70
70
  return _json_response({
71
71
  "sent": report.success,
@@ -81,29 +81,29 @@ async def _handle_comm_notify(args: dict) -> list[TextContent]:
81
81
  ],
82
82
  })
83
83
  except ImportError:
84
- return _error_response("SKComm not installed. Run: pip install skcomm")
84
+ return _error_response("SKComms not installed. Run: pip install skcomms")
85
85
  except Exception as exc:
86
86
  return _error_response(f"Notification send failed: {exc}")
87
87
 
88
88
 
89
89
  async def _handle_comm_status(_args: dict) -> list[TextContent]:
90
- """Show SKComm subsystem status."""
90
+ """Show SKComms subsystem status."""
91
91
  result: dict = {}
92
92
 
93
93
  try:
94
- import skcomm # type: ignore[import]
94
+ import skcomms # type: ignore[import]
95
95
 
96
96
  result["installed"] = True
97
- result["version"] = getattr(skcomm, "__version__", "unknown")
97
+ result["version"] = getattr(skcomms, "__version__", "unknown")
98
98
  except ImportError:
99
99
  result["installed"] = False
100
100
  result["version"] = None
101
101
  return _json_response(result)
102
102
 
103
103
  try:
104
- from skcomm.core import SKComm # type: ignore[import]
104
+ from skcomms.core import SKComms # type: ignore[import]
105
105
 
106
- comm = SKComm.from_config()
106
+ comm = SKComms.from_config()
107
107
  result["transports"] = [
108
108
  t.name for t in getattr(comm, "transports", [])
109
109
  ]
@@ -2,10 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
6
+
5
7
  from mcp.types import TextContent, Tool
6
8
 
7
9
  from ._helpers import _error_response, _home, _json_response, _text_response
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
9
13
  TOOLS: list[Tool] = [
10
14
  Tool(
11
15
  name="soul_list",
@@ -179,6 +183,8 @@ async def _handle_ritual(args: dict) -> list[TextContent]:
179
183
  "journal_entries": result.journal_entries,
180
184
  "germination_prompts": result.germination_prompts,
181
185
  "strongest_memories": result.strongest_memories,
186
+ "song_anchors_loaded": result.song_anchors_loaded,
187
+ "song_anchor_ids": result.song_anchor_ids,
182
188
  "context_prompt": result.context_prompt,
183
189
  })
184
190
  except ImportError:
@@ -250,8 +256,8 @@ async def _handle_soul_list(args: dict) -> list[TextContent]:
250
256
  "source": "installed",
251
257
  "active": name == state.active_soul,
252
258
  })
253
- except Exception:
254
- pass
259
+ except Exception as exc:
260
+ logger.warning("Failed to list installed soul blueprints: %s", exc)
255
261
 
256
262
  # 2) Blueprints repo
257
263
  blueprints_repo = Path.home() / "clawd" / "soul-blueprints" / "blueprints"
@@ -238,8 +238,8 @@ class MDNSDiscovery:
238
238
  agent_name,
239
239
  )
240
240
  return
241
- except Exception:
242
- pass
241
+ except Exception as exc:
242
+ logger.warning("Failed to read existing mDNS heartbeat for %s: %s", agent_name, exc)
243
243
 
244
244
  heartbeat = {
245
245
  "agent_name": agent_name,
@@ -59,7 +59,7 @@ _TAG_PATTERNS: list[tuple[re.Pattern, str]] = [
59
59
  (re.compile(r"\bcapauth\b", re.I), "capauth"),
60
60
  (re.compile(r"\bskcapstone\b", re.I), "skcapstone"),
61
61
  (re.compile(r"\bskmemory\b", re.I), "skmemory"),
62
- (re.compile(r"\bskcomm\b", re.I), "skcomm"),
62
+ (re.compile(r"\bskcomms\b", re.I), "skcomms"),
63
63
  (re.compile(r"\bskchat\b", re.I), "skchat"),
64
64
  (re.compile(r"\bsyncthing\b", re.I), "syncthing"),
65
65
  (re.compile(r"\bMCP\b", re.I), "mcp"),
@@ -25,6 +25,7 @@ from datetime import datetime, timezone
25
25
  from pathlib import Path
26
26
  from typing import Optional
27
27
 
28
+ from . import active_agent_name
28
29
  from .models import MemoryEntry, MemoryLayer, MemoryState, PillarStatus
29
30
 
30
31
  logger = logging.getLogger("skcapstone.memory")
@@ -48,9 +49,15 @@ def _get_unified():
48
49
 
49
50
  def _memory_dir(home: Path) -> Path:
50
51
  """Resolve the memory directory, creating it if needed."""
51
- # Use agent-specific memory directory if agent name is set
52
- agent_name = os.environ.get("SKCAPSTONE_AGENT", "lumina")
53
- mem = home / "agents" / agent_name / "memory"
52
+ # Accept either the shared root (~/.skcapstone) or an agent home
53
+ # (~/.skcapstone/agents/<agent>) and resolve to the active memory dir.
54
+ agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
55
+ if home.parent.name == "agents":
56
+ mem = home / "memory"
57
+ elif agent_name:
58
+ mem = home / "agents" / agent_name / "memory"
59
+ else:
60
+ mem = home / "memory"
54
61
  mem.mkdir(parents=True, exist_ok=True)
55
62
  for layer in MemoryLayer:
56
63
  (mem / layer.value).mkdir(parents=True, exist_ok=True)
@@ -59,7 +59,7 @@ class ChatMetrics(BaseModel):
59
59
 
60
60
 
61
61
  class TransportMetrics(BaseModel):
62
- """SKComm transport stats."""
62
+ """SKComms transport stats."""
63
63
 
64
64
  available: bool = False
65
65
  transport_count: int = 0
@@ -242,7 +242,8 @@ class MetricsCollector:
242
242
  try:
243
243
  data = json.loads(fp.read_text(encoding="utf-8"))
244
244
  return data.get("name") or data.get("agent_name") or ""
245
- except Exception:
245
+ except Exception as e:
246
+ logger.warning("metrics.py: %s", e)
246
247
  continue
247
248
  return "unknown"
248
249
 
@@ -265,6 +266,7 @@ class MetricsCollector:
265
266
  name=entity.get("name", ""),
266
267
  )
267
268
  except Exception as exc:
269
+ logger.warning("metrics.py: %s", exc)
268
270
  report.errors.append(f"identity: {exc}")
269
271
 
270
272
  def _collect_memory(self, report: MetricsReport) -> None:
@@ -304,6 +306,7 @@ class MetricsCollector:
304
306
  except ImportError:
305
307
  pass
306
308
  except Exception as exc:
309
+ logger.warning("metrics.py: %s", exc)
307
310
  report.errors.append(f"memory: {exc}")
308
311
 
309
312
  def _collect_chat(self, report: MetricsReport) -> None:
@@ -331,13 +334,14 @@ class MetricsCollector:
331
334
  except ImportError:
332
335
  pass
333
336
  except Exception as exc:
337
+ logger.warning("metrics.py: %s", exc)
334
338
  report.errors.append(f"chat: {exc}")
335
339
 
336
340
  def _collect_transport(self, report: MetricsReport) -> None:
337
- """Collect SKComm transport metrics."""
341
+ """Collect SKComms transport metrics."""
338
342
  try:
339
- skcomm_dir = Path.home() / ".skcomm"
340
- outbox_dir = skcomm_dir / "outbox"
343
+ skcomms_dir = Path.home() / ".skcomms"
344
+ outbox_dir = skcomms_dir / "outbox"
341
345
 
342
346
  pending = 0
343
347
  dead = 0
@@ -346,20 +350,20 @@ class MetricsCollector:
346
350
  if (outbox_dir / "dead").exists():
347
351
  dead = len(list((outbox_dir / "dead").glob("*.json")))
348
352
 
349
- config_path = skcomm_dir / "config.yml"
353
+ config_path = skcomms_dir / "config.yml"
350
354
  transport_count = 0
351
355
  if config_path.exists():
352
356
  try:
353
357
  import yaml
354
358
 
355
359
  cfg = yaml.safe_load(config_path.read_text(encoding="utf-8"))
356
- transports = cfg.get("skcomm", {}).get("transports", {})
360
+ transports = cfg.get("skcomms", {}).get("transports", {})
357
361
  transport_count = sum(
358
362
  1 for t in transports.values()
359
363
  if isinstance(t, dict) and t.get("enabled", True)
360
364
  )
361
- except Exception:
362
- pass
365
+ except Exception as exc:
366
+ logger.warning("Failed to parse skcomms transport config: %s", exc)
363
367
 
364
368
  report.transport = TransportMetrics(
365
369
  available=True,
@@ -368,6 +372,7 @@ class MetricsCollector:
368
372
  outbox_dead=dead,
369
373
  )
370
374
  except Exception as exc:
375
+ logger.warning("metrics.py: %s", exc)
371
376
  report.errors.append(f"transport: {exc}")
372
377
 
373
378
  def _collect_coordination(self, report: MetricsReport) -> None:
@@ -390,7 +395,8 @@ class MetricsCollector:
390
395
  if status in counts:
391
396
  counts[status] += 1
392
397
  total += 1
393
- except Exception:
398
+ except Exception as e:
399
+ logger.warning("metrics.py: %s", e)
394
400
  total += 1
395
401
 
396
402
  report.coordination = CoordinationMetrics(
@@ -401,6 +407,7 @@ class MetricsCollector:
401
407
  claimed=counts["claimed"],
402
408
  )
403
409
  except Exception as exc:
410
+ logger.warning("metrics.py: %s", exc)
404
411
  report.errors.append(f"coordination: {exc}")
405
412
 
406
413
  def _collect_trust(self, report: MetricsReport) -> None:
@@ -424,6 +431,7 @@ class MetricsCollector:
424
431
  last_rehydration=data.get("last_rehydration", ""),
425
432
  )
426
433
  except Exception as exc:
434
+ logger.warning("metrics.py: %s", exc)
427
435
  report.errors.append(f"trust: {exc}")
428
436
 
429
437
  def _collect_security(self, report: MetricsReport) -> None:
@@ -455,6 +463,7 @@ class MetricsCollector:
455
463
  event_types=type_counts,
456
464
  )
457
465
  except Exception as exc:
466
+ logger.warning("metrics.py: %s", exc)
458
467
  report.errors.append(f"security: {exc}")
459
468
 
460
469
  def _collect_sync(self, report: MetricsReport) -> None:
@@ -474,8 +483,8 @@ class MetricsCollector:
474
483
  if state_path.exists():
475
484
  try:
476
485
  state = json.loads(state_path.read_text(encoding="utf-8"))
477
- except Exception:
478
- pass
486
+ except Exception as exc:
487
+ logger.warning("Failed to read sync_state.json: %s", exc)
479
488
 
480
489
  report.sync = SyncMetrics(
481
490
  available=True,
@@ -486,6 +495,7 @@ class MetricsCollector:
486
495
  last_pull=state.get("last_pull", ""),
487
496
  )
488
497
  except Exception as exc:
498
+ logger.warning("metrics.py: %s", exc)
489
499
  report.errors.append(f"sync: {exc}")
490
500
 
491
501
  def _collect_pubsub(self, report: MetricsReport) -> None:
@@ -510,8 +520,8 @@ class MetricsCollector:
510
520
  try:
511
521
  subs = json.loads(subs_file.read_text(encoding="utf-8"))
512
522
  sub_count = len(subs)
513
- except Exception:
514
- pass
523
+ except Exception as exc:
524
+ logger.warning("Failed to read pubsub subscriptions.json: %s", exc)
515
525
 
516
526
  report.pubsub = PubSubMetrics(
517
527
  available=True,
@@ -520,6 +530,7 @@ class MetricsCollector:
520
530
  subscriptions=sub_count,
521
531
  )
522
532
  except Exception as exc:
533
+ logger.warning("metrics.py: %s", exc)
523
534
  report.errors.append(f"pubsub: {exc}")
524
535
 
525
536
  def _collect_kms(self, report: MetricsReport) -> None:
@@ -546,8 +557,8 @@ class MetricsCollector:
546
557
  try:
547
558
  rot_data = json.loads(rot_log.read_text(encoding="utf-8"))
548
559
  rotations = len(rot_data)
549
- except Exception:
550
- pass
560
+ except Exception as exc:
561
+ logger.warning("Failed to read KMS rotation log: %s", exc)
551
562
 
552
563
  report.kms = KmsMetrics(
553
564
  available=True,
@@ -557,6 +568,7 @@ class MetricsCollector:
557
568
  rotations=rotations,
558
569
  )
559
570
  except Exception as exc:
571
+ logger.warning("metrics.py: %s", exc)
560
572
  report.errors.append(f"kms: {exc}")
561
573
 
562
574
  def _collect_fortress(self, report: MetricsReport) -> None:
@@ -573,6 +585,7 @@ class MetricsCollector:
573
585
  seal_algorithm=data.get("seal_algorithm", ""),
574
586
  )
575
587
  except Exception as exc:
588
+ logger.warning("metrics.py: %s", exc)
576
589
  report.errors.append(f"fortress: {exc}")
577
590
 
578
591
  def _collect_backup(self, report: MetricsReport) -> None:
@@ -593,6 +606,7 @@ class MetricsCollector:
593
606
  latest_size_bytes=latest.stat().st_size,
594
607
  )
595
608
  except Exception as exc:
609
+ logger.warning("metrics.py: %s", exc)
596
610
  report.errors.append(f"backup: {exc}")
597
611
 
598
612
 
@@ -100,8 +100,8 @@ def migrate(
100
100
  try:
101
101
  existing = store.list_memories(limit=10000)
102
102
  existing_ids = {m.id for m in existing}
103
- except Exception:
104
- pass
103
+ except Exception as exc:
104
+ logger.warning("Failed to load existing memory IDs for deduplication: %s", exc)
105
105
 
106
106
  for entry in entries:
107
107
  if entry.memory_id in existing_ids:
@@ -162,7 +162,8 @@ def _verify_migration(
162
162
  recalled = store.recall(entry.memory_id)
163
163
  if recalled is None:
164
164
  missing.append(entry.memory_id)
165
- except Exception:
165
+ except Exception as e:
166
+ logger.warning("migrate_memories.py: %s", e)
166
167
  missing.append(entry.memory_id)
167
168
 
168
169
  result["verified"] = len(entries) - len(missing)