@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
@@ -2,14 +2,14 @@
2
2
  Sync pipeline engine — comms path alignment for Syncthing ↔ consciousness loop.
3
3
 
4
4
  Verifies that the inbox/outbox directories used by the consciousness loop
5
- inotify watcher and the SKComm Syncthing transport are aligned under the
5
+ inotify watcher and the SKComms Syncthing transport are aligned under the
6
6
  Syncthing-synced comms root, and reports pipeline health.
7
7
 
8
8
  Pipeline:
9
9
  Syncthing → {shared_root}/sync/comms/inbox/{peer}/*.skc.json
10
10
  ↓ inotify (ConsciousnessLoop._INBOX_DIR = "sync/comms/inbox")
11
11
  ConsciousnessLoop.process_envelope()
12
- skcomm.send() — must route to SyncthingTransport with
12
+ skcomms.send() — must route to SyncthingTransport with
13
13
  comms_root = {shared_root}/sync/comms
14
14
  SyncthingTransport → {shared_root}/sync/comms/outbox/{peer}/*.skc.json
15
15
  ↓ Syncthing propagates to peer
@@ -17,11 +17,11 @@ Pipeline:
17
17
 
18
18
  Path alignment requirement
19
19
  --------------------------
20
- The SKComm Syncthing transport must be configured with::
20
+ The SKComms Syncthing transport must be configured with::
21
21
 
22
22
  comms_root: ~/.skcapstone/sync/comms
23
23
 
24
- in ~/.skcomm/config.yml so its outbox and inbox paths match the paths the
24
+ in ~/.skcomms/config.yml so its outbox and inbox paths match the paths the
25
25
  consciousness loop watches via inotify. This module provides
26
26
  :func:`verify_pipeline_paths` to detect mismatches and
27
27
  :func:`get_sync_pipeline_status` for the daemon health loop.
@@ -128,19 +128,19 @@ def ensure_comms_dirs(shared_root: Path) -> None:
128
128
  # ---------------------------------------------------------------------------
129
129
 
130
130
 
131
- def verify_pipeline_paths(shared_root: Path, skcomm=None) -> dict:
131
+ def verify_pipeline_paths(shared_root: Path, skcomms=None) -> dict:
132
132
  """Verify inbox/outbox path alignment for the full sync pipeline.
133
133
 
134
134
  Checks:
135
135
 
136
136
  1. The consciousness loop's watched inbox dir exists.
137
137
  2. The outbox dir exists so responses can be written and synced.
138
- 3. (Optional) The active SKComm Syncthing transport's ``comms_root``
138
+ 3. (Optional) The active SKComms Syncthing transport's ``comms_root``
139
139
  matches ``{shared_root}/sync/comms``.
140
140
 
141
141
  Args:
142
142
  shared_root: The agent SHARED_ROOT.
143
- skcomm: Optional :class:`SKComm` instance to inspect transport config.
143
+ skcomms: Optional :class:`SKComms` instance to inspect transport config.
144
144
 
145
145
  Returns:
146
146
  Dict with keys:
@@ -148,7 +148,7 @@ def verify_pipeline_paths(shared_root: Path, skcomm=None) -> dict:
148
148
  - ``inbox_ok`` (bool) — inbox dir exists.
149
149
  - ``outbox_ok`` (bool) — outbox dir exists.
150
150
  - ``transport_aligned`` (bool | None) — transport comms_root matches
151
- (None if skcomm not provided or check failed).
151
+ (None if skcomms not provided or check failed).
152
152
  - ``inbox_path`` (str) — absolute inbox path.
153
153
  - ``outbox_path`` (str) — absolute outbox path.
154
154
  - ``expected_comms_root`` (str) — the expected comms root.
@@ -168,9 +168,9 @@ def verify_pipeline_paths(shared_root: Path, skcomm=None) -> dict:
168
168
  issues.append(f"Outbox dir missing: {outbox}")
169
169
 
170
170
  transport_aligned: Optional[bool] = None
171
- if skcomm is not None:
171
+ if skcomms is not None:
172
172
  try:
173
- transport_aligned = _check_transport_alignment(skcomm, expected_root, issues)
173
+ transport_aligned = _check_transport_alignment(skcomms, expected_root, issues)
174
174
  except Exception as exc:
175
175
  logger.debug("Transport alignment check failed: %s", exc)
176
176
  issues.append(f"Transport alignment check error: {exc}")
@@ -186,11 +186,11 @@ def verify_pipeline_paths(shared_root: Path, skcomm=None) -> dict:
186
186
  }
187
187
 
188
188
 
189
- def _check_transport_alignment(skcomm, expected_root: Path, issues: list[str]) -> bool:
190
- """Walk SKComm router transports and check the Syncthing transport's root.
189
+ def _check_transport_alignment(skcomms, expected_root: Path, issues: list[str]) -> bool:
190
+ """Walk SKComms router transports and check the Syncthing transport's root.
191
191
 
192
192
  Args:
193
- skcomm: SKComm instance (has ``router`` or ``_router`` attribute).
193
+ skcomms: SKComms instance (has ``router`` or ``_router`` attribute).
194
194
  expected_root: The expected comms root path.
195
195
  issues: Mutable list; problem strings are appended here.
196
196
 
@@ -198,7 +198,7 @@ def _check_transport_alignment(skcomm, expected_root: Path, issues: list[str]) -
198
198
  ``True`` if aligned (or no Syncthing transport registered),
199
199
  ``False`` on mismatch.
200
200
  """
201
- router = getattr(skcomm, "router", None) or getattr(skcomm, "_router", None)
201
+ router = getattr(skcomms, "router", None) or getattr(skcomms, "_router", None)
202
202
  if router is None:
203
203
  return True # can't check — assume ok
204
204
 
@@ -214,7 +214,7 @@ def _check_transport_alignment(skcomm, expected_root: Path, issues: list[str]) -
214
214
  issues.append(
215
215
  f"SyncthingTransport comms_root mismatch — "
216
216
  f"expected {expected_root}, got {actual_root}. "
217
- f"Set comms_root: {expected_root} in ~/.skcomm/config.yml"
217
+ f"Set comms_root: {expected_root} in ~/.skcomms/config.yml"
218
218
  )
219
219
  aligned = False
220
220
  return aligned
@@ -574,8 +574,8 @@ class SyncWatcher:
574
574
  try:
575
575
  self._observer.stop()
576
576
  self._observer.join(timeout=5)
577
- except Exception:
578
- pass
577
+ except Exception as exc:
578
+ logger.warning("Error stopping SyncWatcher observer: %s", exc)
579
579
  self._observer = None
580
580
  logger.info("SyncWatcher stopped.")
581
581
 
@@ -35,10 +35,10 @@ def _require_linux() -> None:
35
35
 
36
36
  SERVICE_NAME = "skcapstone.service"
37
37
  SOCKET_NAME = "skcapstone-api.socket"
38
- HEARTBEAT_SERVICE = "skcomm-heartbeat.service"
39
- HEARTBEAT_TIMER = "skcomm-heartbeat.timer"
40
- QUEUE_DRAIN_SERVICE = "skcomm-queue-drain.service"
41
- QUEUE_DRAIN_TIMER = "skcomm-queue-drain.timer"
38
+ HEARTBEAT_SERVICE = "skcomms-heartbeat.service"
39
+ HEARTBEAT_TIMER = "skcomms-heartbeat.timer"
40
+ QUEUE_DRAIN_SERVICE = "skcomms-queue-drain.service"
41
+ QUEUE_DRAIN_TIMER = "skcomms-queue-drain.timer"
42
42
 
43
43
  ALL_UNITS = [
44
44
  SERVICE_NAME,
@@ -53,7 +53,7 @@ TIMER_UNITS = [HEARTBEAT_TIMER, QUEUE_DRAIN_TIMER]
53
53
 
54
54
  SYSTEMD_USER_DIR = Path.home() / ".config" / "systemd" / "user"
55
55
 
56
- BUNDLED_DIR = Path(__file__).parent.parent.parent / "systemd"
56
+ BUNDLED_DIR = Path(__file__).parent / "data" / "systemd"
57
57
 
58
58
 
59
59
  @dataclass
@@ -121,24 +121,32 @@ def systemd_available() -> bool:
121
121
 
122
122
 
123
123
  def install_service(
124
+ agent_name: Optional[str] = None,
124
125
  unit_dir: Optional[Path] = None,
125
126
  source_dir: Optional[Path] = None,
126
127
  enable: bool = True,
127
128
  start: bool = True,
128
129
  ) -> dict:
129
- """Install the skcapstone systemd user service.
130
+ """Install the skcapstone systemd user service for an agent.
130
131
 
131
- Copies unit files to ~/.config/systemd/user/, reloads the
132
- daemon, and optionally enables + starts the service.
132
+ When *agent_name* is provided, installs the template unit
133
+ ``skcapstone@.service`` and enables ``skcapstone@<agent>.service``.
134
+ This supports multiple agents running as independent services on
135
+ the same machine.
136
+
137
+ When *agent_name* is None, falls back to the single-agent
138
+ ``skcapstone.service`` unit for backwards compatibility.
133
139
 
134
140
  Args:
141
+ agent_name: Agent name (e.g. "jarvis"). Uses template unit.
135
142
  unit_dir: Target directory for unit files.
136
143
  source_dir: Directory containing the .service/.socket files.
137
144
  enable: Whether to enable the service at login.
138
145
  start: Whether to start the service immediately.
139
146
 
140
147
  Returns:
141
- dict: Result with 'installed', 'enabled', 'started' bools.
148
+ dict: Result with 'installed', 'enabled', 'started' bools
149
+ and 'service_name' for the installed unit.
142
150
  """
143
151
  _require_linux()
144
152
  target = unit_dir or SYSTEMD_USER_DIR
@@ -146,15 +154,41 @@ def install_service(
146
154
 
147
155
  target.mkdir(parents=True, exist_ok=True)
148
156
 
149
- result = {"installed": False, "enabled": False, "started": False, "timers_enabled": False}
150
-
151
- service_src = source / SERVICE_NAME
152
- if not service_src.exists():
153
- logger.error("Service unit not found: %s", service_src)
154
- return result
155
-
156
- copied = 0
157
+ result = {
158
+ "installed": False,
159
+ "enabled": False,
160
+ "started": False,
161
+ "timers_enabled": False,
162
+ "service_name": "",
163
+ }
164
+
165
+ # Determine which unit to use
166
+ if agent_name:
167
+ # Multi-agent: use template unit skcapstone@.service
168
+ template_name = "skcapstone@.service"
169
+ instance_name = f"skcapstone@{agent_name}.service"
170
+ template_src = source / template_name
171
+ if not template_src.exists():
172
+ logger.error("Template unit not found: %s", template_src)
173
+ return result
174
+ shutil.copy2(template_src, target / template_name)
175
+ service_unit = instance_name
176
+ result["service_name"] = instance_name
177
+ else:
178
+ # Single-agent fallback
179
+ service_src = source / SERVICE_NAME
180
+ if not service_src.exists():
181
+ logger.error("Service unit not found: %s", service_src)
182
+ return result
183
+ shutil.copy2(service_src, target / SERVICE_NAME)
184
+ service_unit = SERVICE_NAME
185
+ result["service_name"] = SERVICE_NAME
186
+
187
+ # Copy ancillary units (socket, timers, etc.)
188
+ copied = 1 # already copied the main unit
157
189
  for unit_name in ALL_UNITS:
190
+ if unit_name == SERVICE_NAME:
191
+ continue # already handled above
158
192
  src = source / unit_name
159
193
  if src.exists():
160
194
  shutil.copy2(src, target / unit_name)
@@ -162,10 +196,10 @@ def install_service(
162
196
 
163
197
  _systemctl("daemon-reload")
164
198
  result["installed"] = True
165
- logger.info("Installed %d unit file(s) to %s", copied, target)
199
+ logger.info("Installed %d unit file(s) to %s (service: %s)", copied, target, service_unit)
166
200
 
167
201
  if enable:
168
- r = _systemctl("enable", SERVICE_NAME)
202
+ r = _systemctl("enable", service_unit)
169
203
  result["enabled"] = r.returncode == 0
170
204
 
171
205
  timers_ok = True
@@ -177,7 +211,7 @@ def install_service(
177
211
  result["timers_enabled"] = timers_ok
178
212
 
179
213
  if start:
180
- r = _systemctl("start", SERVICE_NAME)
214
+ r = _systemctl("start", service_unit)
181
215
  result["started"] = r.returncode == 0
182
216
 
183
217
  for timer in TIMER_UNITS:
@@ -336,7 +370,7 @@ WatchdogSec=120
336
370
  NoNewPrivileges=true
337
371
  ProtectSystem=strict
338
372
  ProtectHome=read-only
339
- ReadWritePaths=%h/.skcapstone %h/.capauth %h/.cloud9 %h/.skcomm %h/.skchat
373
+ ReadWritePaths=%h/.skcapstone %h/.capauth %h/.cloud9 %h/.skcomms %h/.skchat
340
374
  PrivateTmp=true
341
375
  ProtectKernelTunables=true
342
376
  ProtectControlGroups=true
@@ -1,7 +1,7 @@
1
1
  """
2
- Team Communications — SKComm/SKChat wiring for deployed agent teams.
2
+ Team Communications — SKComms/SKChat wiring for deployed agent teams.
3
3
 
4
- Bootstraps a local file-based SKComm channel for each deployed team so that
4
+ Bootstraps a local file-based SKComms channel for each deployed team so that
5
5
  agents can message each other without external infrastructure. Each agent gets
6
6
  its own inbox directory; messages are plain JSON envelopes written atomically
7
7
  to the recipient's inbox folder.
@@ -34,7 +34,7 @@ logger = logging.getLogger(__name__)
34
34
  # Sentinel value: the queen/manager role name used to discover the broadcast sender
35
35
  _QUEEN_ROLES = frozenset({"manager", "queen"})
36
36
 
37
- # File suffix used by the SKComm file transport
37
+ # File suffix used by the SKComms file transport
38
38
  _ENVELOPE_SUFFIX = ".skc.json"
39
39
 
40
40
 
@@ -105,9 +105,9 @@ def _build_envelope(
105
105
  content: str,
106
106
  thread_id: Optional[str] = None,
107
107
  ) -> dict:
108
- """Build a minimal SKComm-compatible envelope dict.
108
+ """Build a minimal SKComms-compatible envelope dict.
109
109
 
110
- Uses the same schema as skcomm.models.MessageEnvelope so downstream
110
+ Uses the same schema as skcomms.models.MessageEnvelope so downstream
111
111
  consumers can deserialize with MessageEnvelope.from_bytes().
112
112
 
113
113
  Args:
@@ -121,7 +121,7 @@ def _build_envelope(
121
121
  """
122
122
  now = datetime.now(timezone.utc).isoformat()
123
123
  return {
124
- "skcomm_version": "1.0.0",
124
+ "skcomms_version": "1.0.0",
125
125
  "envelope_id": str(uuid.uuid4()),
126
126
  "sender": sender,
127
127
  "recipient": recipient,
@@ -276,7 +276,7 @@ def send_to_teammate(
276
276
  ) -> str:
277
277
  """Send a message from one agent to another within the same team.
278
278
 
279
- Writes a SKComm-compatible envelope JSON file to the recipient's inbox
279
+ Writes a SKComms-compatible envelope JSON file to the recipient's inbox
280
280
  directory. Optionally logs the activity to the coordination board.
281
281
 
282
282
  Args:
@@ -392,7 +392,7 @@ def receive_messages(
392
392
  board: Optional Board instance for activity logging.
393
393
 
394
394
  Returns:
395
- List[dict]: Raw envelope dicts (skcomm MessageEnvelope schema).
395
+ List[dict]: Raw envelope dicts (skcomms MessageEnvelope schema).
396
396
  """
397
397
  envelopes = _drain_inbox(agent_name, channel)
398
398
 
@@ -472,7 +472,7 @@ class TeamEngine:
472
472
  deployment: TeamDeployment,
473
473
  blueprint: BlueprintManifest,
474
474
  ) -> TeamChannel:
475
- """Bootstrap SKComm file channel for all agents in a deployment.
475
+ """Bootstrap SKComms file channel for all agents in a deployment.
476
476
 
477
477
  Identifies the queen agent from the blueprint's coordination config
478
478
  (falling back to any agent with role=manager) and provisions per-agent
@@ -7,7 +7,7 @@ terminal — no CI server, no IDE, no special tooling.
7
7
 
8
8
  Usage:
9
9
  skcapstone test # run all packages
10
- skcapstone test --package skcomm # run one package
10
+ skcapstone test --package skcomms # run one package
11
11
  skcapstone test --fast # stop on first failure
12
12
  skcapstone test --json-out # machine-readable results
13
13
  """
@@ -26,10 +26,10 @@ from typing import Optional
26
26
  ECOSYSTEM_PACKAGES = [
27
27
  {"name": "skcapstone", "path": "skcapstone", "tests": "skcapstone/tests"},
28
28
  {"name": "capauth", "path": "capauth", "tests": "capauth/tests"},
29
- {"name": "skcomm", "path": "skcomm", "tests": "skcomm/tests"},
29
+ {"name": "skcomms", "path": "skcomms", "tests": "skcomms/tests"},
30
30
  {"name": "skchat", "path": "skchat", "tests": "skchat/tests"},
31
31
  {"name": "skmemory", "path": "skmemory", "tests": "skmemory/tests"},
32
- {"name": "cloud9-python", "path": "cloud9-python", "tests": "cloud9-python/tests"},
32
+ {"name": "cloud9", "path": "cloud9", "tests": "cloud9/tests"},
33
33
  ]
34
34
 
35
35
 
@@ -117,6 +117,8 @@ def build_trust_graph(home: Path) -> TrustGraph:
117
117
 
118
118
  def _add_self_node(home: Path, graph: TrustGraph) -> None:
119
119
  """Add the local agent as the central node."""
120
+ manifest_data: dict[str, Any] = {}
121
+
120
122
  identity_file = home / "identity" / "identity.json"
121
123
  if identity_file.exists():
122
124
  try:
@@ -130,20 +132,53 @@ def _add_self_node(home: Path, graph: TrustGraph) -> None:
130
132
  fingerprint=data.get("fingerprint"),
131
133
  metadata={"capauth_managed": data.get("capauth_managed", False)},
132
134
  ))
133
- return
134
135
  except (json.JSONDecodeError, OSError):
135
136
  pass
136
137
 
137
138
  manifest = home / "manifest.json"
138
139
  if manifest.exists():
139
140
  try:
140
- data = json.loads(manifest.read_text(encoding="utf-8"))
141
- name = data.get("name", "self")
142
- graph.agent_name = name
143
- graph.add_node(TrustNode(id=name, label=name, node_type="agent"))
141
+ manifest_data = json.loads(manifest.read_text(encoding="utf-8"))
142
+ if not graph.nodes:
143
+ name = manifest_data.get("name", "self")
144
+ graph.agent_name = name
145
+ graph.add_node(TrustNode(id=name, label=name, node_type="agent"))
144
146
  except (json.JSONDecodeError, OSError):
145
147
  pass
146
148
 
149
+ _add_operator_edge(manifest_data, graph)
150
+
151
+
152
+ def _add_operator_edge(manifest_data: dict[str, Any], graph: TrustGraph) -> None:
153
+ """Add an explicit human-operator relationship from manifest metadata."""
154
+ operator = manifest_data.get("operator")
155
+ if not isinstance(operator, dict):
156
+ return
157
+
158
+ name = str(operator.get("name", "")).strip()
159
+ if not name or not graph.agent_name:
160
+ return
161
+
162
+ node_id = str(operator.get("fingerprint", "")).strip() or f"operator:{name}"
163
+ graph.add_node(TrustNode(
164
+ id=node_id,
165
+ label=name,
166
+ node_type="peer",
167
+ fingerprint=str(operator.get("fingerprint", "")).strip() or None,
168
+ metadata={
169
+ "relationship": operator.get("relationship", "human-operator"),
170
+ "entity_type": operator.get("entity_type", "human"),
171
+ "source": operator.get("source", "manifest"),
172
+ },
173
+ ))
174
+ graph.add_edge(TrustEdge(
175
+ source=graph.agent_name,
176
+ target=node_id,
177
+ edge_type="operator",
178
+ label=operator.get("relationship", "human-operator"),
179
+ strength=1.0,
180
+ ))
181
+
147
182
 
148
183
  def _add_token_edges(home: Path, graph: TrustGraph) -> None:
149
184
  """Add edges from capability token issuance (issuer trusts subject)."""
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Unified Search — full-text search across all agent data stores.
3
3
 
4
- Searches memories, conversations, and SKComm messages in one query.
4
+ Searches memories, conversations, and SKComms messages in one query.
5
5
  Results are ranked by a combined relevance + recency score.
6
6
 
7
7
  Data stores searched:
@@ -15,6 +15,7 @@ from __future__ import annotations
15
15
 
16
16
  import json
17
17
  import logging
18
+ import os
18
19
  import re
19
20
  from dataclasses import dataclass, field
20
21
  from datetime import datetime, timezone
@@ -140,8 +141,16 @@ def _search_memories(
140
141
  List of SearchResult objects from the memory store.
141
142
  """
142
143
  results: list[SearchResult] = []
143
- agent_name = os.environ.get("SKCAPSTONE_AGENT", "lumina")
144
- mem_dir = home / "agents" / agent_name / "memory"
144
+ from . import active_agent_name
145
+
146
+ agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
147
+ if home.parent.name == "agents":
148
+ # home is already an agent-specific dir (e.g. ~/.skcapstone/agents/lumina)
149
+ mem_dir = home / "memory"
150
+ elif agent_name:
151
+ mem_dir = home / "agents" / agent_name / "memory"
152
+ else:
153
+ mem_dir = home / "memory"
145
154
  if not mem_dir.exists():
146
155
  return results
147
156
 
@@ -252,7 +261,7 @@ def _search_messages(
252
261
  home: Path,
253
262
  pattern: re.Pattern,
254
263
  ) -> list[SearchResult]:
255
- """Search archived SKComm envelope files (.skc.json).
264
+ """Search archived SKComms envelope files (.skc.json).
256
265
 
257
266
  Handles both the legacy schema (payload.text) and the newer
258
267
  schema (payload.content).
@@ -262,7 +271,7 @@ def _search_messages(
262
271
  pattern: Compiled search pattern.
263
272
 
264
273
  Returns:
265
- List of SearchResult objects from SKComm messages.
274
+ List of SearchResult objects from SKComms messages.
266
275
  """
267
276
  results: list[SearchResult] = []
268
277
 
@@ -387,7 +396,7 @@ def search(
387
396
  """Search across all agent data stores.
388
397
 
389
398
  Performs full-text regex matching against memories, conversations,
390
- SKComm messages, and journal entries. Results are ranked by a
399
+ SKComms messages, and journal entries. Results are ranked by a
391
400
  combined relevance × recency score, highest first.
392
401
 
393
402
  Args:
@@ -35,6 +35,9 @@ from rich.table import Table
35
35
 
36
36
  from . import AGENT_HOME
37
37
 
38
+ import logging
39
+ logger = logging.getLogger(__name__)
40
+
38
41
  console = Console()
39
42
 
40
43
 
@@ -236,7 +239,8 @@ def _transfer_to_device(
236
239
  d for name, d in registry.get("devices", {}).items()
237
240
  if name != hostname and d.get("is_datastore")
238
241
  ]
239
- except Exception:
242
+ except Exception as e:
243
+ logger.warning("uninstall_wizard.py: %s", e)
240
244
  other_devices = []
241
245
 
242
246
  if not other_devices:
@@ -310,6 +314,7 @@ def _deregister_from_vault_registry(home_path: Path) -> None:
310
314
  except ImportError:
311
315
  console.print("[dim]skref not installed — skipping[/]")
312
316
  except Exception as exc:
317
+ logger.warning("uninstall_wizard.py: %s", exc)
313
318
  console.print(f"[yellow]{exc}[/]")
314
319
 
315
320
 
@@ -331,6 +336,7 @@ def _teardown_tailscale() -> None:
331
336
  except ImportError:
332
337
  console.print("[dim]skref not installed — skipping[/]")
333
338
  except Exception as exc:
339
+ logger.warning("uninstall_wizard.py: %s", exc)
334
340
  console.print(f"[yellow]{exc}[/]")
335
341
 
336
342
 
@@ -369,6 +375,7 @@ def _remove_syncthing_folder(home_path: Path) -> None:
369
375
 
370
376
  console.print("[dim]no matching folder found[/]")
371
377
  except Exception as exc:
378
+ logger.warning("uninstall_wizard.py: %s", exc)
372
379
  console.print(f"[dim]could not reach Syncthing API: {exc}[/]")
373
380
  console.print(
374
381
  " [dim]You may need to remove the shared folder manually\n"
@@ -450,8 +457,8 @@ def _uninstall_packages() -> None:
450
457
 
451
458
  console.print(" Uninstalling packages...", end=" ")
452
459
  packages = [
453
- "skcapstone", "capauth", "skmemory", "skcomm",
454
- "cloud9-protocol", "skref", "skchat",
460
+ "skcapstone", "capauth", "skmemory", "skcomms",
461
+ "cloud9", "skref", "skchat",
455
462
  ]
456
463
  try:
457
464
  result = subprocess.run(
@@ -529,6 +536,7 @@ def _export_backup(home_path: Path) -> Optional[Path]:
529
536
  )
530
537
  return archive_path
531
538
  except Exception as exc:
539
+ logger.warning("uninstall_wizard.py: %s", exc)
532
540
  console.print(f"[red]failed: {exc}[/]")
533
541
  return None
534
542
 
@@ -9,20 +9,22 @@ standalone ``skcapstone version-check`` CLI command.
9
9
  from __future__ import annotations
10
10
 
11
11
  import json
12
+ import logging
12
13
  import urllib.request
13
14
  import urllib.error
14
15
  from dataclasses import dataclass, field
15
16
  from typing import Optional
16
17
 
18
+ logger = logging.getLogger(__name__)
17
19
 
18
20
  ECOSYSTEM_PACKAGES = [
19
21
  "skmemory",
20
22
  "skcapstone",
21
23
  "capauth",
22
24
  "sksecurity",
23
- "skcomm",
25
+ "skcomms",
24
26
  "skchat",
25
- "cloud9-protocol",
27
+ "cloud9",
26
28
  ]
27
29
 
28
30
 
@@ -82,7 +84,8 @@ def _get_installed_version(package_name: str) -> Optional[str]:
82
84
  from importlib.metadata import version
83
85
 
84
86
  return version(package_name)
85
- except Exception:
87
+ except Exception as e:
88
+ logger.debug("version_check.py metadata lookup failed: %s", e)
86
89
  # Try import-based fallback for packages with dashes
87
90
  try:
88
91
  mod_name = package_name.replace("-", "_")
@@ -90,7 +93,8 @@ def _get_installed_version(package_name: str) -> Optional[str]:
90
93
 
91
94
  mod = importlib.import_module(mod_name)
92
95
  return getattr(mod, "__version__", None)
93
- except Exception:
96
+ except Exception as e:
97
+ logger.debug("version_check.py import fallback failed: %s", e)
94
98
  return None
95
99
 
96
100
 
@@ -287,7 +287,8 @@ def _calibrate_from_memories(home: Path, cal: AnchorCalibration) -> None:
287
287
 
288
288
  try:
289
289
  memories = list_memories(home, limit=50)
290
- except Exception:
290
+ except Exception as e:
291
+ logger.warning("warmth_anchor.py: %s", e)
291
292
  return
292
293
 
293
294
  if not memories:
@@ -314,7 +315,8 @@ def _calibrate_from_coordination(home: Path, cal: AnchorCalibration) -> None:
314
315
  board = Board(home)
315
316
  views = board.get_task_views()
316
317
  agents = board.load_agents()
317
- except Exception:
318
+ except Exception as e:
319
+ logger.warning("warmth_anchor.py: %s", e)
318
320
  return
319
321
 
320
322
  done = sum(1 for v in views if v.status.value == "done")
@@ -48,7 +48,7 @@ class IdentityCard(BaseModel):
48
48
  trust_status: Current trust state (active, degraded, missing).
49
49
  consciousness: Agent consciousness level.
50
50
  memory_count: How many memories the agent holds.
51
- contact_uris: Ways to reach this agent (skcomm, email, nostr, etc.).
51
+ contact_uris: Ways to reach this agent (skcomms, email, nostr, etc.).
52
52
  hostname: Machine the card was generated on.
53
53
  created_at: When this card was generated.
54
54
  """
@@ -246,10 +246,10 @@ def _load_capabilities(home: Path, card: IdentityCard) -> None:
246
246
  logger.debug("capauth not installed — skipping capauth:identity capability")
247
247
 
248
248
  try:
249
- import skcomm # noqa: F401
250
- caps.append("skcomm:messaging")
249
+ import skcomms # noqa: F401
250
+ caps.append("skcomms:messaging")
251
251
  except ImportError:
252
- logger.debug("skcomm not installed — skipping skcomm:messaging capability")
252
+ logger.debug("skcomms not installed — skipping skcomms:messaging capability")
253
253
 
254
254
  try:
255
255
  import skchat # noqa: F401
@@ -16,20 +16,18 @@ MemoryMax=4G
16
16
  # Keep Ollama models warm for 5 minutes between requests
17
17
  Environment=PYTHONUNBUFFERED=1
18
18
  Environment=OLLAMA_KEEP_ALIVE=5m
19
+ Environment=SKAGENT=lumina
19
20
  Environment=SKCAPSTONE_AGENT=lumina
21
+ Environment=SKMEMORY_AGENT=lumina
20
22
  # Journal logging
21
23
  StandardOutput=journal
22
24
  StandardError=journal
23
25
  SyslogIdentifier=skcapstone
24
26
 
25
- # Security hardening
27
+ # Security hardening (relaxed — ProtectHome=read-only breaks if any
28
+ # ReadWritePaths dir is missing on the host)
26
29
  NoNewPrivileges=true
27
- ProtectSystem=strict
28
- ProtectHome=read-only
29
- ReadWritePaths=%h/.skcapstone %h/.skenv %h/.capauth %h/.cloud9 %h/.skcomm %h/.skchat
30
30
  PrivateTmp=true
31
- ProtectKernelTunables=true
32
- ProtectControlGroups=true
33
31
 
34
32
  [Install]
35
33
  WantedBy=default.target