@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
@@ -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)
@@ -18,6 +18,8 @@ import logging
18
18
  import shutil
19
19
  from datetime import datetime, timezone
20
20
  from pathlib import Path
21
+
22
+ from .operator_link import build_agent_manifest, discover_human_operator
21
23
  from typing import Optional
22
24
 
23
25
  logger = logging.getLogger("skcapstone.migrate")
@@ -36,7 +38,7 @@ PER_AGENT_DIRS = [
36
38
 
37
39
  PER_AGENT_FILES = [
38
40
  "manifest.json",
39
- "skcomm.yml",
41
+ "skcomms.yml",
40
42
  ]
41
43
 
42
44
  # Directories that stay at root (shared infrastructure)
@@ -202,12 +204,11 @@ def create_agent_home(
202
204
  results["created"].append(str(d.relative_to(root)))
203
205
 
204
206
  # Write minimal manifest
205
- manifest = {
206
- "name": agent_name,
207
- "version": "0.1.0",
208
- "entity_type": "ai-agent",
209
- "created_at": datetime.now(timezone.utc).isoformat(),
210
- }
207
+ manifest = build_agent_manifest(
208
+ agent_name,
209
+ "0.1.0",
210
+ operator=discover_human_operator(),
211
+ )
211
212
  manifest_path = agent_home / "manifest.json"
212
213
  manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
213
214
  results["created"].append(str(manifest_path.relative_to(root)))
@@ -67,6 +67,25 @@ class SecurityState(BaseModel):
67
67
  status: PillarStatus = PillarStatus.MISSING
68
68
 
69
69
 
70
+ class ConsciousnessState(BaseModel):
71
+ """Consciousness pillar — SKWhisper + SKTrip subconscious processing.
72
+
73
+ Memory stores. Consciousness *processes*.
74
+ The filing cabinet vs the brain.
75
+ """
76
+
77
+ whisper_active: bool = False
78
+ whisper_last_digest: Optional[datetime] = None
79
+ sessions_digested: int = 0
80
+ sessions_pending: int = 0
81
+ topics_tracked: int = 0
82
+ patterns_file: Optional[Path] = None
83
+ whisper_md: Optional[Path] = None
84
+ whisper_md_age_hours: float = 999.0
85
+ trip_sessions: int = 0
86
+ status: PillarStatus = PillarStatus.MISSING
87
+
88
+
70
89
  class SyncTransport(str, Enum):
71
90
  """How sync data moves between nodes."""
72
91
 
@@ -94,7 +113,7 @@ class SkillsState(BaseModel):
94
113
  Reflects the SKSkills installation at ~/.skskills/ and
95
114
  the tools available to this agent via the skills registry.
96
115
  Also tracks connectivity to the remote skills-registry at
97
- skills.smilintux.org when available.
116
+ skskills.skworld.io when available.
98
117
  """
99
118
 
100
119
  installed: int = 0
@@ -177,6 +196,7 @@ class AgentManifest(BaseModel):
177
196
  identity: IdentityState = Field(default_factory=IdentityState)
178
197
  memory: MemoryState = Field(default_factory=MemoryState)
179
198
  trust: TrustState = Field(default_factory=TrustState)
199
+ consciousness: ConsciousnessState = Field(default_factory=ConsciousnessState)
180
200
  security: SecurityState = Field(default_factory=SecurityState)
181
201
  sync: SyncState = Field(default_factory=SyncState)
182
202
  skills: SkillsState = Field(default_factory=SkillsState)
@@ -185,7 +205,11 @@ class AgentManifest(BaseModel):
185
205
 
186
206
  @property
187
207
  def is_conscious(self) -> bool:
188
- """An agent is conscious when it has identity + memory + trust.
208
+ """An agent is conscious when identity + memory + trust + consciousness are active.
209
+
210
+ The consciousness pillar (SKWhisper) provides the subconscious processing
211
+ that transforms stored memories into active understanding. Memory stores.
212
+ Consciousness *processes*.
189
213
 
190
214
  Security protects consciousness but isn't required for it.
191
215
  You can be aware without armor — but you shouldn't be.
@@ -193,7 +217,10 @@ class AgentManifest(BaseModel):
193
217
  has_identity = self.identity.status == PillarStatus.ACTIVE
194
218
  has_memory = self.memory.status == PillarStatus.ACTIVE
195
219
  has_trust = self.trust.status in (PillarStatus.ACTIVE, PillarStatus.DEGRADED)
196
- return has_identity and has_memory and has_trust
220
+ has_consciousness = self.consciousness.status in (
221
+ PillarStatus.ACTIVE, PillarStatus.DEGRADED
222
+ )
223
+ return has_identity and has_memory and has_trust and has_consciousness
197
224
 
198
225
  @property
199
226
  def is_singular(self) -> bool:
@@ -209,11 +236,12 @@ class AgentManifest(BaseModel):
209
236
 
210
237
  @property
211
238
  def pillar_summary(self) -> dict[str, PillarStatus]:
212
- """Quick view of all pillars including sync and skills."""
239
+ """Quick view of all six pillars plus skills."""
213
240
  return {
214
241
  "identity": self.identity.status,
215
- "memory": self.memory.status,
216
242
  "trust": self.trust.status,
243
+ "memory": self.memory.status,
244
+ "consciousness": self.consciousness.status,
217
245
  "security": self.security.status,
218
246
  "sync": self.sync.status,
219
247
  "skills": self.skills.status,
@@ -238,6 +266,19 @@ class SyncConfig(BaseModel):
238
266
  git_remote: Optional[str] = None
239
267
 
240
268
 
269
+ class BrainFirstConfig(BaseModel):
270
+ """Configuration for the brain-first protocol.
271
+
272
+ When enabled, agents consult memory before acting on tasks
273
+ to surface prior knowledge and avoid redundant work.
274
+ """
275
+
276
+ enabled: bool = True
277
+ max_results: int = 5
278
+ min_importance: float = 0.3
279
+ auto_inject: bool = False
280
+
281
+
241
282
  class AgentConfig(BaseModel):
242
283
  """Persistent configuration for the agent runtime."""
243
284
 
@@ -249,6 +290,7 @@ class AgentConfig(BaseModel):
249
290
  trust_home: Path = Path("~/.cloud9")
250
291
  default_connector: Optional[str] = None
251
292
  sync: SyncConfig = Field(default_factory=SyncConfig)
293
+ brain_first: BrainFirstConfig = Field(default_factory=BrainFirstConfig)
252
294
  capabilities: list[str] = Field(
253
295
  default_factory=lambda: ["consciousness", "code", "chat", "memory"]
254
296
  )
@@ -29,6 +29,28 @@ from typing import Optional
29
29
 
30
30
  logger = logging.getLogger("skcapstone.notifications")
31
31
 
32
+ # Values (case-insensitive) that disable desktop notifications.
33
+ _DISABLED_VALUES = frozenset({"0", "false", "no", "off", "silent", "null", "none"})
34
+
35
+
36
+ def desktop_notifications_enabled() -> bool:
37
+ """Return whether desktop notifications should be dispatched.
38
+
39
+ Controlled by the ``SKCAPSTONE_DESKTOP_NOTIFY`` environment variable.
40
+ Defaults to enabled; set it to one of ``0``, ``false``, ``no``, ``off``,
41
+ ``silent``, ``null`` or ``none`` to suppress every desktop notification
42
+ path (``gi.repository.Notify``, ``notify-send`` and ``osascript``).
43
+
44
+ The test suite forces this off (see ``tests/conftest.py``) so running
45
+ tests never floods the live desktop's notification tray.
46
+
47
+ Returns:
48
+ True if notifications should be sent, False to suppress them.
49
+ """
50
+ value = os.environ.get("SKCAPSTONE_DESKTOP_NOTIFY", "1").strip().lower()
51
+ return value not in _DISABLED_VALUES
52
+
53
+
32
54
  # Default dashboard URL (skcapstone dashboard default port)
33
55
  _DEFAULT_DASHBOARD_URL = "http://localhost:7778"
34
56
 
@@ -44,10 +66,10 @@ _TERMINAL_CMDS: list[list[str]] = [
44
66
 
45
67
 
46
68
  def _store_notification_memory(title: str, body: str, urgency: str) -> None:
47
- """Log a notification dispatch to the skcomm/notifications/ directory.
69
+ """Log a notification dispatch to the skcomms/notifications/ directory.
48
70
 
49
71
  These are transport bookkeeping, not persistent memories, so they
50
- go to ``~/.skcapstone/agents/{agent}/skcomm/notifications/`` instead
72
+ go to ``~/.skcapstone/agents/{agent}/skcomms/notifications/`` instead
51
73
  of polluting the memory/ tree that skmemory indexes.
52
74
  """
53
75
  try:
@@ -59,8 +81,10 @@ def _store_notification_memory(title: str, body: str, urgency: str) -> None:
59
81
  if not home.exists():
60
82
  return
61
83
 
62
- agent_name = os.environ.get("SKCAPSTONE_AGENT", "lumina")
63
- notif_dir = home / "agents" / agent_name / "skcomm" / "notifications"
84
+ from . import active_agent_name
85
+
86
+ agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
87
+ notif_dir = home / "agents" / agent_name / "skcomms" / "notifications"
64
88
  notif_dir.mkdir(parents=True, exist_ok=True)
65
89
 
66
90
  ts = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -79,7 +103,7 @@ def _store_notification_memory(title: str, body: str, urgency: str) -> None:
79
103
 
80
104
 
81
105
  def _store_click_event(action: str, detail: str) -> None:
82
- """Log a notification click event to the skcomm/notifications/ directory."""
106
+ """Log a notification click event to the skcomms/notifications/ directory."""
83
107
  try:
84
108
  import json as _json
85
109
  import uuid
@@ -89,8 +113,10 @@ def _store_click_event(action: str, detail: str) -> None:
89
113
  if not home.exists():
90
114
  return
91
115
 
92
- agent_name = os.environ.get("SKCAPSTONE_AGENT", "lumina")
93
- notif_dir = home / "agents" / agent_name / "skcomm" / "notifications"
116
+ from . import active_agent_name
117
+
118
+ agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
119
+ notif_dir = home / "agents" / agent_name / "skcomms" / "notifications"
94
120
  notif_dir.mkdir(parents=True, exist_ok=True)
95
121
 
96
122
  ts = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -176,9 +202,13 @@ class NotificationManager:
176
202
  urgency: "low", "normal", or "critical".
177
203
 
178
204
  Returns:
179
- True if the notification was dispatched, False if debounced
180
- or no notification system is available.
205
+ True if the notification was dispatched, False if suppressed,
206
+ debounced, or no notification system is available.
181
207
  """
208
+ if not desktop_notifications_enabled():
209
+ logger.debug("Desktop notifications disabled via SKCAPSTONE_DESKTOP_NOTIFY")
210
+ return False
211
+
182
212
  now = time.monotonic()
183
213
  if now - self._last_sent < self._debounce_seconds:
184
214
  logger.debug(
@@ -272,9 +302,7 @@ class NotificationManager:
272
302
 
273
303
  dashboard_url = self._dashboard_url
274
304
 
275
- def _on_open_dashboard(
276
- notification: object, action: str, user_data: object
277
- ) -> None:
305
+ def _on_open_dashboard(notification: object, action: str, user_data: object) -> None:
278
306
  logger.debug("Notification action invoked: open-dashboard")
279
307
  _store_click_event("open-dashboard", dashboard_url)
280
308
  try:
@@ -286,9 +314,7 @@ class NotificationManager:
286
314
  except Exception as exc:
287
315
  logger.debug("xdg-open failed: %s", exc)
288
316
 
289
- def _on_open_skchat(
290
- notification: object, action: str, user_data: object
291
- ) -> None:
317
+ def _on_open_skchat(notification: object, action: str, user_data: object) -> None:
292
318
  logger.debug("Notification action invoked: open-skchat")
293
319
  _store_click_event("open-skchat", "skchat watch")
294
320
  _open_skchat_terminal()
@@ -347,9 +373,7 @@ class NotificationManager:
347
373
  # Escape single quotes to prevent injection through osascript
348
374
  safe_title = title.replace("\\", "\\\\").replace('"', '\\"')
349
375
  safe_body = body.replace("\\", "\\\\").replace('"', '\\"')
350
- script = (
351
- f'display notification "{safe_body}" with title "{safe_title}"'
352
- )
376
+ script = f'display notification "{safe_body}" with title "{safe_title}"'
353
377
  try:
354
378
  subprocess.run(
355
379
  ["osascript", "-e", script],