@smilintux/skcapstone 0.9.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 (284) 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 +278 -1
  18. package/docs/DREAMING.md +70 -0
  19. package/docs/GETTING_STARTED.md +10 -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.daemon.plist +52 -0
  33. package/launchd/com.skcapstone.memory-compress.plist +45 -0
  34. package/launchd/com.skcapstone.skcomms-heartbeat.plist +33 -0
  35. package/launchd/com.skcapstone.skcomms-queue-drain.plist +34 -0
  36. package/launchd/install-launchd.sh +156 -0
  37. package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
  38. package/package.json +1 -1
  39. package/pyproject.toml +16 -10
  40. package/scripts/archive-sessions.sh +95 -0
  41. package/scripts/check-updates.py +4 -4
  42. package/scripts/install-bundle.sh +8 -8
  43. package/scripts/install.ps1 +12 -11
  44. package/scripts/install.sh +196 -11
  45. package/scripts/model-fallback-monitor.sh +102 -0
  46. package/scripts/notion-api.py +259 -0
  47. package/scripts/nvidia-proxy.mjs +908 -0
  48. package/scripts/proxy-monitor.sh +89 -0
  49. package/scripts/refresh-anthropic-token.sh +172 -0
  50. package/scripts/release.sh +98 -0
  51. package/scripts/session-to-memory.py +219 -0
  52. package/scripts/skgateway.mjs +856 -0
  53. package/scripts/telegram-catchup-all.sh +147 -0
  54. package/scripts/verify_install.sh +2 -2
  55. package/scripts/wargov-ufo-capture/README.md +43 -0
  56. package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
  57. package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
  58. package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
  59. package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
  60. package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
  61. package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
  62. package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
  63. package/scripts/watch-anthropic-token.sh +212 -0
  64. package/scripts/windows/install-tasks.ps1 +7 -7
  65. package/scripts/windows/skcapstone-task.xml +1 -1
  66. package/src/skcapstone/__init__.py +45 -3
  67. package/src/skcapstone/_cli_monolith.py +20 -15
  68. package/src/skcapstone/activity.py +5 -1
  69. package/src/skcapstone/agent_card.py +3 -2
  70. package/src/skcapstone/api.py +41 -40
  71. package/src/skcapstone/auction.py +14 -11
  72. package/src/skcapstone/backup.py +2 -1
  73. package/src/skcapstone/blueprint_registry.py +4 -3
  74. package/src/skcapstone/blueprints/builtins/itil-operations.yaml +40 -0
  75. package/src/skcapstone/brain_first.py +238 -0
  76. package/src/skcapstone/changelog.py +1 -1
  77. package/src/skcapstone/chat.py +22 -17
  78. package/src/skcapstone/cli/__init__.py +9 -1
  79. package/src/skcapstone/cli/_common.py +1 -0
  80. package/src/skcapstone/cli/agents_spawner.py +5 -2
  81. package/src/skcapstone/cli/alerts.py +25 -4
  82. package/src/skcapstone/cli/bench.py +15 -15
  83. package/src/skcapstone/cli/chat.py +7 -4
  84. package/src/skcapstone/cli/consciousness.py +5 -2
  85. package/src/skcapstone/cli/context_cmd.py +18 -4
  86. package/src/skcapstone/cli/daemon.py +121 -42
  87. package/src/skcapstone/cli/gtd.py +26 -1
  88. package/src/skcapstone/cli/housekeeping.py +3 -3
  89. package/src/skcapstone/cli/identity_cmd.py +378 -0
  90. package/src/skcapstone/cli/joule_cmd.py +7 -3
  91. package/src/skcapstone/cli/memory.py +8 -6
  92. package/src/skcapstone/cli/peers_dir.py +1 -1
  93. package/src/skcapstone/cli/register_cmd.py +29 -3
  94. package/src/skcapstone/cli/scheduler_cmd.py +167 -0
  95. package/src/skcapstone/cli/session.py +25 -0
  96. package/src/skcapstone/cli/setup.py +96 -29
  97. package/src/skcapstone/cli/shell_cmd.py +53 -1
  98. package/src/skcapstone/cli/skills_cmd.py +2 -2
  99. package/src/skcapstone/cli/soul.py +8 -5
  100. package/src/skcapstone/cli/status.py +37 -11
  101. package/src/skcapstone/cli/telegram.py +21 -0
  102. package/src/skcapstone/cli/test_cmd.py +5 -5
  103. package/src/skcapstone/cli/test_connection.py +2 -2
  104. package/src/skcapstone/cli/upgrade_cmd.py +23 -14
  105. package/src/skcapstone/cli/version_cmd.py +1 -1
  106. package/src/skcapstone/cli/watch_cmd.py +9 -6
  107. package/src/skcapstone/cloud9_bridge.py +14 -14
  108. package/src/skcapstone/codex_setup.py +255 -0
  109. package/src/skcapstone/config_validator.py +7 -4
  110. package/src/skcapstone/consciousness_config.py +5 -1
  111. package/src/skcapstone/consciousness_loop.py +313 -273
  112. package/src/skcapstone/context_loader.py +121 -0
  113. package/src/skcapstone/coord_federation.py +2 -1
  114. package/src/skcapstone/coordination.py +23 -6
  115. package/src/skcapstone/crush_integration.py +2 -1
  116. package/src/skcapstone/daemon.py +151 -88
  117. package/src/skcapstone/dashboard.py +10 -10
  118. package/src/skcapstone/data/sk-agent-picker.sh +421 -0
  119. package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
  120. package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
  121. package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
  122. package/src/skcapstone/data/systemd/skcapstone.service +37 -0
  123. package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
  124. package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
  125. package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
  126. package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
  127. package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
  128. package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
  129. package/src/skcapstone/defaults/claude/settings.json +74 -0
  130. package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
  131. package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
  132. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
  133. package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
  134. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
  135. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
  136. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
  137. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
  138. package/src/skcapstone/defaults/unhinged.json +13 -0
  139. package/src/skcapstone/discovery.py +43 -20
  140. package/src/skcapstone/doctor.py +941 -22
  141. package/src/skcapstone/dreaming.py +1183 -109
  142. package/src/skcapstone/emotion_tracker.py +2 -2
  143. package/src/skcapstone/export.py +4 -3
  144. package/src/skcapstone/fuse_mount.py +35 -25
  145. package/src/skcapstone/gui_installer.py +2 -2
  146. package/src/skcapstone/heartbeat.py +34 -30
  147. package/src/skcapstone/housekeeping.py +14 -14
  148. package/src/skcapstone/install_wizard.py +209 -7
  149. package/src/skcapstone/itil.py +13 -4
  150. package/src/skcapstone/kms_scheduler.py +10 -8
  151. package/src/skcapstone/launchd.py +426 -0
  152. package/src/skcapstone/mcp_launcher.py +15 -1
  153. package/src/skcapstone/mcp_server.py +341 -49
  154. package/src/skcapstone/mcp_tools/__init__.py +2 -0
  155. package/src/skcapstone/mcp_tools/_helpers.py +2 -2
  156. package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
  157. package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
  158. package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
  159. package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
  160. package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
  161. package/src/skcapstone/mcp_tools/did_tools.py +11 -8
  162. package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
  163. package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
  164. package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
  165. package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
  166. package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
  167. package/src/skcapstone/mdns_discovery.py +2 -2
  168. package/src/skcapstone/memory_curator.py +1 -1
  169. package/src/skcapstone/memory_engine.py +10 -3
  170. package/src/skcapstone/metrics.py +30 -16
  171. package/src/skcapstone/migrate_memories.py +4 -3
  172. package/src/skcapstone/migrate_multi_agent.py +8 -7
  173. package/src/skcapstone/models.py +47 -5
  174. package/src/skcapstone/notifications.py +42 -18
  175. package/src/skcapstone/onboard.py +1000 -126
  176. package/src/skcapstone/operator_link.py +170 -0
  177. package/src/skcapstone/peer_directory.py +4 -4
  178. package/src/skcapstone/peers.py +19 -19
  179. package/src/skcapstone/pillars/__init__.py +7 -5
  180. package/src/skcapstone/pillars/consciousness.py +191 -0
  181. package/src/skcapstone/pillars/identity.py +51 -7
  182. package/src/skcapstone/pillars/memory.py +9 -3
  183. package/src/skcapstone/pillars/sync.py +2 -2
  184. package/src/skcapstone/preflight.py +3 -3
  185. package/src/skcapstone/providers/docker.py +28 -28
  186. package/src/skcapstone/register.py +6 -6
  187. package/src/skcapstone/registry_client.py +5 -4
  188. package/src/skcapstone/runtime.py +14 -3
  189. package/src/skcapstone/scheduled_tasks.py +254 -19
  190. package/src/skcapstone/scheduler_jobs.py +456 -0
  191. package/src/skcapstone/scheduler_runner.py +239 -0
  192. package/src/skcapstone/scheduler_state.py +162 -0
  193. package/src/skcapstone/sdk.py +310 -0
  194. package/src/skcapstone/service_health.py +279 -39
  195. package/src/skcapstone/session_briefing.py +108 -0
  196. package/src/skcapstone/session_capture.py +1 -1
  197. package/src/skcapstone/shell.py +7 -1
  198. package/src/skcapstone/soul.py +3 -1
  199. package/src/skcapstone/soul_switch.py +3 -1
  200. package/src/skcapstone/summary.py +6 -6
  201. package/src/skcapstone/sync_engine.py +15 -15
  202. package/src/skcapstone/sync_watcher.py +2 -2
  203. package/src/skcapstone/systemd.py +72 -21
  204. package/src/skcapstone/team_comms.py +8 -8
  205. package/src/skcapstone/team_engine.py +1 -1
  206. package/src/skcapstone/testrunner.py +3 -3
  207. package/src/skcapstone/trust_graph.py +40 -5
  208. package/src/skcapstone/unified_search.py +15 -6
  209. package/src/skcapstone/uninstall_wizard.py +11 -3
  210. package/src/skcapstone/version_check.py +8 -4
  211. package/src/skcapstone/warmth_anchor.py +4 -2
  212. package/src/skcapstone/whoami.py +4 -4
  213. package/systemd/skcapstone.service +4 -6
  214. package/systemd/skcapstone@.service +7 -8
  215. package/systemd/skcomms-heartbeat.service +21 -0
  216. package/systemd/skcomms-heartbeat.timer +12 -0
  217. package/systemd/skcomms-queue-drain.service +17 -0
  218. package/systemd/skcomms-queue-drain.timer +12 -0
  219. package/tests/conftest.py +39 -0
  220. package/tests/integration/test_consciousness_e2e.py +39 -39
  221. package/tests/test_agent_card.py +1 -1
  222. package/tests/test_agent_home_scaffold.py +34 -0
  223. package/tests/test_alerts_consumer_topics.py +27 -0
  224. package/tests/test_backup.py +2 -1
  225. package/tests/test_chat.py +6 -6
  226. package/tests/test_claude_md.py +2 -2
  227. package/tests/test_cli_skills.py +10 -10
  228. package/tests/test_cli_test_cmd.py +4 -4
  229. package/tests/test_cli_test_connection.py +1 -1
  230. package/tests/test_cloud9_bridge.py +6 -6
  231. package/tests/test_consciousness_e2e.py +1 -1
  232. package/tests/test_consciousness_loop.py +10 -10
  233. package/tests/test_coordination.py +25 -0
  234. package/tests/test_cross_package.py +21 -21
  235. package/tests/test_daemon.py +4 -4
  236. package/tests/test_daemon_shutdown.py +1 -1
  237. package/tests/test_docker_provider.py +29 -29
  238. package/tests/test_doctor.py +400 -0
  239. package/tests/test_doctor_skscheduler.py +50 -0
  240. package/tests/test_dreaming_engine.py +147 -0
  241. package/tests/test_dreaming_gtd_capture.py +35 -0
  242. package/tests/test_e2e_automated.py +8 -5
  243. package/tests/test_fuse_mount.py +10 -10
  244. package/tests/test_gtd_brief.py +46 -0
  245. package/tests/test_gtd_malformed_tolerance.py +31 -0
  246. package/tests/test_housekeeping.py +15 -15
  247. package/tests/test_identity_migrate.py +251 -0
  248. package/tests/test_integration_backbone.py +598 -0
  249. package/tests/test_itil_gtd_lifecycle.py +37 -0
  250. package/tests/test_jobs_dropins.py +84 -0
  251. package/tests/test_mcp_server.py +82 -37
  252. package/tests/test_models.py +48 -4
  253. package/tests/test_multi_agent.py +31 -29
  254. package/tests/test_notifications.py +122 -32
  255. package/tests/test_onboard.py +63 -75
  256. package/tests/test_operator_link.py +78 -0
  257. package/tests/test_peers.py +14 -14
  258. package/tests/test_pillars.py +98 -0
  259. package/tests/test_preflight.py +3 -3
  260. package/tests/test_runtime.py +21 -0
  261. package/tests/test_scheduled_tasks.py +11 -6
  262. package/tests/test_scheduler_cli.py +47 -0
  263. package/tests/test_scheduler_features.py +133 -0
  264. package/tests/test_scheduler_integration.py +87 -0
  265. package/tests/test_scheduler_jobs.py +155 -0
  266. package/tests/test_scheduler_runner.py +64 -0
  267. package/tests/test_scheduler_state.py +57 -0
  268. package/tests/test_sdk.py +70 -0
  269. package/tests/test_service_health_incidents.py +34 -0
  270. package/tests/test_service_registry.py +52 -0
  271. package/tests/test_session_briefing.py +130 -0
  272. package/tests/test_snapshots.py +4 -4
  273. package/tests/test_sync_pipeline.py +26 -26
  274. package/tests/test_team_comms.py +2 -2
  275. package/tests/test_testrunner.py +2 -2
  276. package/tests/test_trust_graph.py +18 -0
  277. package/tests/test_unified_search.py +2 -2
  278. package/tests/test_version_check.py +10 -0
  279. package/tests/test_version_cmd.py +8 -8
  280. package/tests/test_whoami.py +1 -1
  281. package/systemd/skcomm-heartbeat.service +0 -18
  282. package/systemd/skcomm-queue-drain.service +0 -17
  283. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
  284. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
@@ -392,8 +392,8 @@ class EmotionTracker:
392
392
  if item.get("timestamp", "") >= cutoff:
393
393
  try:
394
394
  entries.append(EmotionEntry(**item))
395
- except Exception:
396
- pass
395
+ except Exception as exc:
396
+ logger.warning("Failed to parse emotion log entry: %s", exc)
397
397
  return entries
398
398
  except Exception as exc:
399
399
  logger.debug("Failed to load emotion log: %s", exc)
@@ -235,7 +235,8 @@ def _read_agent_name(home: Path) -> str:
235
235
  name = data.get("name") or data.get("agent_name")
236
236
  if name:
237
237
  return str(name)
238
- except Exception:
238
+ except Exception as e:
239
+ logger.warning("export.py: %s", e)
239
240
  continue
240
241
  return "unknown"
241
242
 
@@ -423,8 +424,8 @@ def _import_conversations(
423
424
  existing_data = json.loads(peer_file.read_text(encoding="utf-8"))
424
425
  if isinstance(existing_data, list):
425
426
  existing_messages = existing_data
426
- except Exception:
427
- pass
427
+ except Exception as exc:
428
+ logger.warning("Failed to read existing conversation for peer %s: %s", peer, exc)
428
429
 
429
430
  # Deduplicate by (role, content, timestamp) tuple
430
431
  existing_keys = {
@@ -15,11 +15,11 @@ Virtual directory layout::
15
15
  ├── identity/
16
16
  │ ├── card.json — CapAuth identity card
17
17
  │ └── fingerprint.txt — PGP fingerprint
18
- ├── inbox/ — SKComm incoming messages (read-only)
19
- ├── outbox/ — Write here to send via SKComm
18
+ ├── inbox/ — SKComms incoming messages (read-only)
19
+ ├── outbox/ — Write here to send via SKComms
20
20
  └── coordination/ — Task board files (.json)
21
21
 
22
- Writing to ``/outbox/<agent_name>.msg`` enqueues a message via SKComm.
22
+ Writing to ``/outbox/<agent_name>.msg`` enqueues a message via SKComms.
23
23
 
24
24
  Dependencies (optional):
25
25
  pip install skcapstone[fuse] # pulls in fusepy
@@ -31,6 +31,7 @@ import errno
31
31
  import json
32
32
  import logging
33
33
  import os
34
+ import platform
34
35
  import stat
35
36
  import subprocess
36
37
  import sys
@@ -314,7 +315,7 @@ def _build_fingerprint_txt(agent_home: Path) -> bytes:
314
315
 
315
316
 
316
317
  def _list_inbox(agent_home: Path) -> List[str]:
317
- """List files in the SKComm inbox.
318
+ """List files in the SKComms inbox.
318
319
 
319
320
  Args:
320
321
  agent_home: Agent home directory.
@@ -329,7 +330,7 @@ def _list_inbox(agent_home: Path) -> List[str]:
329
330
 
330
331
 
331
332
  def _read_inbox_file(agent_home: Path, filename: str) -> Optional[bytes]:
332
- """Read a message from the SKComm inbox.
333
+ """Read a message from the SKComms inbox.
333
334
 
334
335
  Args:
335
336
  agent_home: Agent home directory.
@@ -416,12 +417,12 @@ def _read_coordination_task(agent_home: Path, filename: str) -> Optional[bytes]:
416
417
 
417
418
 
418
419
  # ---------------------------------------------------------------------------
419
- # SKComm send helper
420
+ # SKComms send helper
420
421
  # ---------------------------------------------------------------------------
421
422
 
422
423
 
423
- def _send_via_skcomm(agent_home: Path, recipient: str, message: str) -> bool:
424
- """Send a message via SKComm by writing to the outbox directory.
424
+ def _send_via_skcomms(agent_home: Path, recipient: str, message: str) -> bool:
425
+ """Send a message via SKComms by writing to the outbox directory.
425
426
 
426
427
  Attempts to use the skcapstone CLI for delivery. Falls back to writing
427
428
  an envelope JSON file in the outbox directory.
@@ -497,7 +498,7 @@ class SovereignFS:
497
498
 
498
499
  Exposes agent memories, identity, inbox, outbox, and coordination tasks
499
500
  as a read-mostly virtual filesystem. Writing to ``/outbox/<agent>.msg``
500
- delivers a message via SKComm.
501
+ delivers a message via SKComms.
501
502
 
502
503
  This class is designed to be used with ``fusepy``:
503
504
 
@@ -513,7 +514,9 @@ class SovereignFS:
513
514
 
514
515
  def __init__(self, agent_home: Path) -> None:
515
516
  self._home = agent_home
516
- agent_name = os.environ.get("SKCAPSTONE_AGENT", "lumina")
517
+ from . import active_agent_name
518
+
519
+ agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
517
520
  self._memory_dir = agent_home / "agents" / agent_name / "memory"
518
521
  # Buffer for outbox writes: maps virtual path → bytes written so far
519
522
  self._outbox_buffers: Dict[str, bytes] = {}
@@ -829,7 +832,7 @@ class SovereignFS:
829
832
  return 0
830
833
 
831
834
  def flush(self, path: str, fh: int) -> int:
832
- """Flush an outbox file buffer, delivering the message via SKComm.
835
+ """Flush an outbox file buffer, delivering the message via SKComms.
833
836
 
834
837
  Called when an outbox file handle is closed. The accumulated buffer
835
838
  is interpreted as the message body; the filename (without ``.msg``)
@@ -861,7 +864,7 @@ class SovereignFS:
861
864
  return 0
862
865
 
863
866
  if message:
864
- _send_via_skcomm(self._home, recipient, message)
867
+ _send_via_skcomms(self._home, recipient, message)
865
868
 
866
869
  # Clear buffer after sending
867
870
  self._outbox_buffers.pop(path, None)
@@ -1001,16 +1004,17 @@ class FUSEDaemon:
1001
1004
  mount_str = str(self._mount_point)
1002
1005
 
1003
1006
  # Linux: parse /proc/mounts
1004
- proc_mounts = Path("/proc/mounts")
1005
- if proc_mounts.exists():
1006
- try:
1007
- for line in proc_mounts.read_text(encoding="utf-8").splitlines():
1008
- parts = line.split()
1009
- if len(parts) >= 2 and parts[1] == mount_str:
1010
- return True
1011
- except OSError as exc:
1012
- logger.warning("Failed to read /proc/mounts: %s", exc)
1013
- return False
1007
+ if platform.system() == "Linux":
1008
+ proc_mounts = Path("/proc/mounts")
1009
+ if proc_mounts.exists():
1010
+ try:
1011
+ for line in proc_mounts.read_text(encoding="utf-8").splitlines():
1012
+ parts = line.split()
1013
+ if len(parts) >= 2 and parts[1] == mount_str:
1014
+ return True
1015
+ except OSError as exc:
1016
+ logger.warning("Failed to read /proc/mounts: %s", exc)
1017
+ return False
1014
1018
 
1015
1019
  # macOS / other: use mount command
1016
1020
  try:
@@ -1113,8 +1117,13 @@ class FUSEDaemon:
1113
1117
 
1114
1118
  mount_str = str(self._mount_point)
1115
1119
 
1116
- # Linux: fusermount
1117
- for cmd in (["fusermount", "-u", mount_str], ["umount", mount_str]):
1120
+ # On Linux try fusermount first, then umount; on macOS skip fusermount
1121
+ if platform.system() == "Linux":
1122
+ unmount_cmds = [["fusermount", "-u", mount_str], ["umount", mount_str]]
1123
+ else:
1124
+ unmount_cmds = [["umount", mount_str]]
1125
+
1126
+ for cmd in unmount_cmds:
1118
1127
  try:
1119
1128
  result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
1120
1129
  if result.returncode == 0:
@@ -1130,7 +1139,8 @@ class FUSEDaemon:
1130
1139
  except (FileNotFoundError, subprocess.TimeoutExpired, OSError) as exc:
1131
1140
  logger.debug("Unmount command %s failed: %s", cmd, exc)
1132
1141
 
1133
- logger.error("Could not unmount %s try: fusermount -u %s", mount_str, mount_str)
1142
+ hint = "fusermount -u" if platform.system() == "Linux" else "umount"
1143
+ logger.error("Could not unmount %s — try: %s %s", mount_str, hint, mount_str)
1134
1144
  return False
1135
1145
 
1136
1146
  def status(self) -> Dict[str, Any]:
@@ -399,7 +399,7 @@ class InstallerApp:
399
399
  """Execute Path 1 (fresh) or Path 2 (join) install steps."""
400
400
  # Install pip packages
401
401
  self._log("Installing software packages...")
402
- packages = ["capauth", "skmemory", "skcomm", "cloud9-protocol"]
402
+ packages = ["capauth", "skmemory", "skcomms", "cloud9"]
403
403
  try:
404
404
  result = subprocess.run(
405
405
  [sys.executable, "-m", "pip", "install", *packages],
@@ -453,7 +453,7 @@ class InstallerApp:
453
453
  def _run_update(self) -> None:
454
454
  """Execute Path 3 update steps."""
455
455
  self._log("Updating software packages...")
456
- packages = ["capauth", "skmemory", "skcomm", "cloud9-protocol", "skcapstone"]
456
+ packages = ["capauth", "skmemory", "skcomms", "cloud9", "skcapstone"]
457
457
  try:
458
458
  result = subprocess.run(
459
459
  [sys.executable, "-m", "pip", "install", "--upgrade", *packages],
@@ -398,27 +398,30 @@ class HeartbeatBeacon:
398
398
  mem = psutil.virtual_memory()
399
399
  mem_used_mb = (mem.total - mem.available) // (1024 * 1024)
400
400
  except ImportError:
401
- # Fallback: /proc on Linux
402
- try:
403
- loadavg = Path("/proc/loadavg")
404
- if loadavg.exists():
405
- parts = loadavg.read_text().split()
406
- cpu_load = round(float(parts[0]), 2)
407
- except Exception as exc:
408
- logger.debug("CPU load fallback failed: %s", exc)
409
- try:
410
- meminfo = Path("/proc/meminfo")
411
- if meminfo.exists():
412
- info: dict[str, int] = {}
413
- for line in meminfo.read_text().splitlines():
414
- parts = line.split()
415
- if len(parts) >= 2:
416
- info[parts[0].rstrip(":")] = int(parts[1])
417
- total_kb = info.get("MemTotal", 0)
418
- avail_kb = info.get("MemAvailable", 0)
419
- mem_used_mb = (total_kb - avail_kb) // 1024
420
- except Exception as exc:
421
- logger.debug("Memory fallback failed: %s", exc)
401
+ # Fallback: /proc on Linux only
402
+ if platform.system() == "Linux":
403
+ try:
404
+ loadavg = Path("/proc/loadavg")
405
+ if loadavg.exists():
406
+ parts = loadavg.read_text().split()
407
+ cpu_load = round(float(parts[0]), 2)
408
+ except Exception as exc:
409
+ logger.debug("CPU load fallback failed: %s", exc)
410
+ try:
411
+ meminfo = Path("/proc/meminfo")
412
+ if meminfo.exists():
413
+ info: dict[str, int] = {}
414
+ for line in meminfo.read_text().splitlines():
415
+ parts = line.split()
416
+ if len(parts) >= 2:
417
+ info[parts[0].rstrip(":")] = int(parts[1])
418
+ total_kb = info.get("MemTotal", 0)
419
+ avail_kb = info.get("MemAvailable", 0)
420
+ mem_used_mb = (total_kb - avail_kb) // 1024
421
+ except Exception as exc:
422
+ logger.debug("Memory fallback failed: %s", exc)
423
+ else:
424
+ logger.debug("psutil not available and not on Linux; skipping CPU/mem detection")
422
425
  except Exception as exc:
423
426
  logger.debug("CPU/mem detection failed: %s", exc)
424
427
 
@@ -439,14 +442,15 @@ class HeartbeatBeacon:
439
442
  mem_total = mem.total // (1024 * 1024)
440
443
  mem_avail = mem.available // (1024 * 1024)
441
444
  except ImportError:
442
- # Fallback: read from /proc/meminfo on Linux
443
- meminfo = Path("/proc/meminfo")
444
- if meminfo.exists():
445
- for line in meminfo.read_text().splitlines():
446
- if line.startswith("MemTotal:"):
447
- mem_total = int(line.split()[1]) // 1024
448
- elif line.startswith("MemAvailable:"):
449
- mem_avail = int(line.split()[1]) // 1024
445
+ # Fallback: read from /proc/meminfo on Linux only
446
+ if platform.system() == "Linux":
447
+ meminfo = Path("/proc/meminfo")
448
+ if meminfo.exists():
449
+ for line in meminfo.read_text().splitlines():
450
+ if line.startswith("MemTotal:"):
451
+ mem_total = int(line.split()[1]) // 1024
452
+ elif line.startswith("MemAvailable:"):
453
+ mem_avail = int(line.split()[1]) // 1024
450
454
 
451
455
  gpu_available = False
452
456
  gpu_name = ""
@@ -506,7 +510,7 @@ class HeartbeatBeacon:
506
510
  ("skcapstone", "skcapstone"),
507
511
  ("skmemory", "skmemory"),
508
512
  ("skchat", "skchat"),
509
- ("skcomm", "skcomm"),
513
+ ("skcomms", "skcomms"),
510
514
  ("capauth", "capauth"),
511
515
  ("cloud9", "cloud9"),
512
516
  ]
@@ -2,9 +2,9 @@
2
2
  Housekeeping — storage pruning for the sovereign agent.
3
3
 
4
4
  Prunes stale files that accumulate in the agent profile:
5
- - ACK files in ~/.skcomm/acks/ (age-based, 24h default)
5
+ - ACK files in ~/.skcomms/acks/ (age-based, 24h default)
6
6
  - Delivered envelopes in ~/.skcapstone/sync/comms/outbox/ (age-based, 48h)
7
- - Seed snapshots in ~/.skcapstone/sync/sync/outbox/ (count-based, keep 10)
7
+ - Seed snapshots in ~/.skcapstone/sync/outbox/ (count-based, keep 10)
8
8
 
9
9
  These directories grow unbounded and can bloat a ~15MB profile to 300MB+.
10
10
  Run via daemon loop (hourly) or CLI: ``skcapstone housekeeping [--dry-run]``.
@@ -26,20 +26,20 @@ DEFAULT_COMMS_MAX_AGE_HOURS = 48
26
26
  DEFAULT_SEEDS_KEEP_PER_AGENT = 10
27
27
 
28
28
 
29
- def prune_acks(skcomm_home: Path, max_age_hours: int = DEFAULT_ACK_MAX_AGE_HOURS) -> int:
29
+ def prune_acks(skcomms_home: Path, max_age_hours: int = DEFAULT_ACK_MAX_AGE_HOURS) -> int:
30
30
  """Remove ACK files older than max_age_hours.
31
31
 
32
- ACK files in ~/.skcomm/acks/ confirm message delivery but are never
32
+ ACK files in ~/.skcomms/acks/ confirm message delivery but are never
33
33
  read after initial processing. They accumulate indefinitely.
34
34
 
35
35
  Args:
36
- skcomm_home: Path to ~/.skcomm.
36
+ skcomms_home: Path to ~/.skcomms.
37
37
  max_age_hours: Delete ACKs older than this. Default 24.
38
38
 
39
39
  Returns:
40
40
  Number of files deleted.
41
41
  """
42
- acks_dir = skcomm_home / "acks"
42
+ acks_dir = skcomms_home / "acks"
43
43
  if not acks_dir.is_dir():
44
44
  return 0
45
45
 
@@ -114,7 +114,7 @@ def prune_seeds(
114
114
  ) -> int:
115
115
  """Keep only the most recent seeds per agent, delete the rest.
116
116
 
117
- Seed files in ~/.skcapstone/sync/sync/outbox/ are named like
117
+ Seed files in ~/.skcapstone/sync/outbox/ are named like
118
118
  ``<agent>-<timestamp>.json.gpg`` or ``<agent>-<timestamp>.json``.
119
119
  A new seed is pushed every 5 minutes by the daemon, so they
120
120
  accumulate quickly.
@@ -163,14 +163,14 @@ def prune_seeds(
163
163
 
164
164
  def run_housekeeping(
165
165
  skcapstone_home: Optional[Path] = None,
166
- skcomm_home: Optional[Path] = None,
166
+ skcomms_home: Optional[Path] = None,
167
167
  dry_run: bool = False,
168
168
  ) -> dict:
169
169
  """Run all housekeeping tasks.
170
170
 
171
171
  Args:
172
172
  skcapstone_home: Path to ~/.skcapstone. Defaults to AGENT_HOME.
173
- skcomm_home: Path to ~/.skcomm. Defaults to ~/.skcomm.
173
+ skcomms_home: Path to ~/.skcomms. Defaults to ~/.skcomms.
174
174
  dry_run: If True, report what would be deleted without deleting.
175
175
 
176
176
  Returns:
@@ -180,16 +180,16 @@ def run_housekeeping(
180
180
 
181
181
  if skcapstone_home is None:
182
182
  skcapstone_home = Path(AGENT_HOME).expanduser()
183
- if skcomm_home is None:
184
- skcomm_home = Path("~/.skcomm").expanduser()
183
+ if skcomms_home is None:
184
+ skcomms_home = Path("~/.skcomms").expanduser()
185
185
 
186
186
  results: dict[str, dict] = {}
187
187
 
188
188
  # Measure sizes before pruning
189
189
  targets = {
190
- "acks": skcomm_home / "acks",
190
+ "acks": skcomms_home / "acks",
191
191
  "comms_outbox": skcapstone_home / "sync" / "comms" / "outbox",
192
- "seed_outbox": skcapstone_home / "sync" / "sync" / "outbox",
192
+ "seed_outbox": skcapstone_home / "sync" / "outbox",
193
193
  }
194
194
 
195
195
  for key, path in targets.items():
@@ -214,7 +214,7 @@ def run_housekeeping(
214
214
  return results
215
215
 
216
216
  # Actually prune
217
- results["acks"]["deleted"] = prune_acks(skcomm_home)
217
+ results["acks"]["deleted"] = prune_acks(skcomms_home)
218
218
  results["comms_outbox"]["deleted"] = prune_comms_outbox(skcapstone_home / "sync")
219
219
  results["seed_outbox"]["deleted"] = prune_seeds(targets["seed_outbox"])
220
220