@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
@@ -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],