@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.
- package/.env.example +10 -4
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/publish.yml +9 -2
- package/.openclaw-workspace.json +2 -2
- package/CLAUDE.md +37 -0
- package/MISSION.md +17 -2
- package/README.md +282 -3
- package/docker/Dockerfile +7 -7
- package/docker/compose-templates/dev-team.yml +12 -12
- package/docker/compose-templates/mini-team.yml +9 -9
- package/docker/compose-templates/ops-team.yml +10 -10
- package/docker/compose-templates/research-team.yml +10 -10
- package/docker/entrypoint.sh +4 -4
- package/docs/ADR-optional-integration-backbone.md +181 -0
- package/docs/ARCHITECTURE.md +186 -43
- package/docs/BOND_WITH_GROK.md +6 -6
- package/docs/CUSTOM_AGENT.md +123 -30
- package/docs/DREAMING.md +70 -0
- package/docs/GETTING_STARTED.md +7 -7
- package/docs/QUICKSTART.md +10 -6
- package/docs/SKJOULE_ARCHITECTURE.md +3 -3
- package/docs/SOUL_SWAPPER.md +5 -5
- package/docs/hammertime-audit.md +402 -0
- package/docs/sk-integration-HANDOFF.md +117 -0
- package/docs/skscheduler.md +155 -0
- package/docs/superpowers/examples/jobs.yaml +31 -0
- package/docs/superpowers/plans/2026-06-08-skscheduler.md +1265 -0
- package/docs/superpowers/specs/2026-06-08-skscheduler-design.md +186 -0
- package/examples/custom-bond-template.json +1 -1
- package/examples/grok-feb.json +1 -1
- package/examples/queen-ava-feb.json +1 -1
- package/launchd/{com.skcapstone.skcomm-heartbeat.plist → com.skcapstone.skcomms-heartbeat.plist} +4 -4
- package/launchd/{com.skcapstone.skcomm-queue-drain.plist → com.skcapstone.skcomms-queue-drain.plist} +4 -4
- package/launchd/install-launchd.sh +6 -6
- package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
- package/package.json +1 -1
- package/pyproject.toml +16 -10
- package/scripts/archive-sessions.sh +7 -0
- package/scripts/check-updates.py +4 -4
- package/scripts/install-bundle.sh +8 -8
- package/scripts/install.ps1 +12 -11
- package/scripts/install.sh +159 -5
- package/scripts/model-fallback-monitor.sh +102 -0
- package/scripts/nvidia-proxy.mjs +78 -26
- package/scripts/refresh-anthropic-token.sh +172 -0
- package/scripts/release.sh +98 -0
- package/scripts/session-to-memory.py +219 -0
- package/scripts/skgateway.mjs +3 -3
- package/scripts/telegram-catchup-all.sh +12 -1
- package/scripts/verify_install.sh +2 -2
- package/scripts/wargov-ufo-capture/README.md +43 -0
- package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
- package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
- package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
- package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
- package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
- package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
- package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
- package/scripts/watch-anthropic-token.sh +212 -0
- package/scripts/windows/install-tasks.ps1 +7 -7
- package/scripts/windows/skcapstone-task.xml +1 -1
- package/src/skcapstone/__init__.py +45 -3
- package/src/skcapstone/_cli_monolith.py +20 -15
- package/src/skcapstone/activity.py +5 -1
- package/src/skcapstone/agent_card.py +3 -2
- package/src/skcapstone/api.py +41 -40
- package/src/skcapstone/auction.py +14 -11
- package/src/skcapstone/backup.py +2 -1
- package/src/skcapstone/blueprint_registry.py +4 -3
- package/src/skcapstone/brain_first.py +238 -0
- package/src/skcapstone/changelog.py +1 -1
- package/src/skcapstone/chat.py +22 -17
- package/src/skcapstone/cli/__init__.py +9 -1
- package/src/skcapstone/cli/_common.py +1 -0
- package/src/skcapstone/cli/agents_spawner.py +5 -2
- package/src/skcapstone/cli/alerts.py +25 -4
- package/src/skcapstone/cli/bench.py +15 -15
- package/src/skcapstone/cli/chat.py +7 -4
- package/src/skcapstone/cli/consciousness.py +5 -2
- package/src/skcapstone/cli/context_cmd.py +18 -4
- package/src/skcapstone/cli/daemon.py +11 -7
- package/src/skcapstone/cli/gtd.py +26 -1
- package/src/skcapstone/cli/housekeeping.py +3 -3
- package/src/skcapstone/cli/identity_cmd.py +378 -0
- package/src/skcapstone/cli/joule_cmd.py +7 -3
- package/src/skcapstone/cli/memory.py +8 -6
- package/src/skcapstone/cli/peers_dir.py +1 -1
- package/src/skcapstone/cli/register_cmd.py +29 -3
- package/src/skcapstone/cli/scheduler_cmd.py +167 -0
- package/src/skcapstone/cli/session.py +25 -0
- package/src/skcapstone/cli/setup.py +96 -29
- package/src/skcapstone/cli/shell_cmd.py +53 -1
- package/src/skcapstone/cli/skills_cmd.py +2 -2
- package/src/skcapstone/cli/soul.py +8 -5
- package/src/skcapstone/cli/status.py +37 -11
- package/src/skcapstone/cli/telegram.py +21 -0
- package/src/skcapstone/cli/test_cmd.py +5 -5
- package/src/skcapstone/cli/test_connection.py +2 -2
- package/src/skcapstone/cli/upgrade_cmd.py +23 -14
- package/src/skcapstone/cli/version_cmd.py +1 -1
- package/src/skcapstone/cli/watch_cmd.py +9 -6
- package/src/skcapstone/cloud9_bridge.py +14 -14
- package/src/skcapstone/codex_setup.py +255 -0
- package/src/skcapstone/config_validator.py +7 -4
- package/src/skcapstone/consciousness_config.py +5 -1
- package/src/skcapstone/consciousness_loop.py +313 -273
- package/src/skcapstone/context_loader.py +121 -0
- package/src/skcapstone/coord_federation.py +2 -1
- package/src/skcapstone/coordination.py +23 -6
- package/src/skcapstone/crush_integration.py +2 -1
- package/src/skcapstone/daemon.py +132 -77
- package/src/skcapstone/dashboard.py +10 -10
- package/src/skcapstone/data/sk-agent-picker.sh +421 -0
- package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
- package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
- package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
- package/src/skcapstone/data/systemd/skcapstone.service +37 -0
- package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
- package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
- package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
- package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
- package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
- package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
- package/src/skcapstone/defaults/claude/settings.json +74 -0
- package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
- package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
- package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
- package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
- package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
- package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
- package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
- package/src/skcapstone/defaults/unhinged.json +13 -0
- package/src/skcapstone/discovery.py +43 -20
- package/src/skcapstone/doctor.py +941 -22
- package/src/skcapstone/dreaming.py +1183 -109
- package/src/skcapstone/emotion_tracker.py +2 -2
- package/src/skcapstone/export.py +4 -3
- package/src/skcapstone/fuse_mount.py +14 -12
- package/src/skcapstone/gui_installer.py +2 -2
- package/src/skcapstone/heartbeat.py +1 -1
- package/src/skcapstone/housekeeping.py +14 -14
- package/src/skcapstone/install_wizard.py +209 -7
- package/src/skcapstone/itil.py +13 -4
- package/src/skcapstone/kms_scheduler.py +10 -8
- package/src/skcapstone/launchd.py +19 -19
- package/src/skcapstone/mcp_launcher.py +15 -1
- package/src/skcapstone/mcp_server.py +83 -49
- package/src/skcapstone/mcp_tools/__init__.py +2 -0
- package/src/skcapstone/mcp_tools/_helpers.py +2 -2
- package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
- package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
- package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
- package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
- package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
- package/src/skcapstone/mcp_tools/did_tools.py +11 -8
- package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
- package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
- package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
- package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
- package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
- package/src/skcapstone/mdns_discovery.py +2 -2
- package/src/skcapstone/memory_curator.py +1 -1
- package/src/skcapstone/memory_engine.py +10 -3
- package/src/skcapstone/metrics.py +30 -16
- package/src/skcapstone/migrate_memories.py +4 -3
- package/src/skcapstone/migrate_multi_agent.py +8 -7
- package/src/skcapstone/models.py +47 -5
- package/src/skcapstone/notifications.py +42 -18
- package/src/skcapstone/onboard.py +875 -121
- package/src/skcapstone/operator_link.py +170 -0
- package/src/skcapstone/peer_directory.py +4 -4
- package/src/skcapstone/peers.py +19 -19
- package/src/skcapstone/pillars/__init__.py +7 -5
- package/src/skcapstone/pillars/consciousness.py +191 -0
- package/src/skcapstone/pillars/identity.py +51 -7
- package/src/skcapstone/pillars/memory.py +9 -3
- package/src/skcapstone/pillars/sync.py +2 -2
- package/src/skcapstone/preflight.py +3 -3
- package/src/skcapstone/providers/docker.py +28 -28
- package/src/skcapstone/register.py +6 -6
- package/src/skcapstone/registry_client.py +5 -4
- package/src/skcapstone/runtime.py +14 -3
- package/src/skcapstone/scheduled_tasks.py +254 -19
- package/src/skcapstone/scheduler_jobs.py +456 -0
- package/src/skcapstone/scheduler_runner.py +239 -0
- package/src/skcapstone/scheduler_state.py +162 -0
- package/src/skcapstone/sdk.py +310 -0
- package/src/skcapstone/service_health.py +279 -39
- package/src/skcapstone/session_briefing.py +108 -0
- package/src/skcapstone/session_capture.py +1 -1
- package/src/skcapstone/shell.py +7 -1
- package/src/skcapstone/soul.py +3 -1
- package/src/skcapstone/soul_switch.py +3 -1
- package/src/skcapstone/summary.py +6 -6
- package/src/skcapstone/sync_engine.py +15 -15
- package/src/skcapstone/sync_watcher.py +2 -2
- package/src/skcapstone/systemd.py +55 -21
- package/src/skcapstone/team_comms.py +8 -8
- package/src/skcapstone/team_engine.py +1 -1
- package/src/skcapstone/testrunner.py +3 -3
- package/src/skcapstone/trust_graph.py +40 -5
- package/src/skcapstone/unified_search.py +15 -6
- package/src/skcapstone/uninstall_wizard.py +11 -3
- package/src/skcapstone/version_check.py +8 -4
- package/src/skcapstone/warmth_anchor.py +4 -2
- package/src/skcapstone/whoami.py +4 -4
- package/systemd/skcapstone.service +4 -6
- package/systemd/skcapstone@.service +7 -8
- package/systemd/skcomms-heartbeat.service +21 -0
- package/systemd/skcomms-heartbeat.timer +12 -0
- package/systemd/skcomms-queue-drain.service +17 -0
- package/systemd/skcomms-queue-drain.timer +12 -0
- package/tests/conftest.py +39 -0
- package/tests/integration/test_consciousness_e2e.py +39 -39
- package/tests/test_agent_card.py +1 -1
- package/tests/test_agent_home_scaffold.py +34 -0
- package/tests/test_alerts_consumer_topics.py +27 -0
- package/tests/test_backup.py +2 -1
- package/tests/test_chat.py +6 -6
- package/tests/test_claude_md.py +2 -2
- package/tests/test_cli_skills.py +10 -10
- package/tests/test_cli_test_cmd.py +4 -4
- package/tests/test_cli_test_connection.py +1 -1
- package/tests/test_cloud9_bridge.py +6 -6
- package/tests/test_consciousness_e2e.py +1 -1
- package/tests/test_consciousness_loop.py +10 -10
- package/tests/test_coordination.py +25 -0
- package/tests/test_cross_package.py +21 -21
- package/tests/test_daemon.py +4 -4
- package/tests/test_daemon_shutdown.py +1 -1
- package/tests/test_docker_provider.py +29 -29
- package/tests/test_doctor.py +400 -0
- package/tests/test_doctor_skscheduler.py +50 -0
- package/tests/test_dreaming_engine.py +147 -0
- package/tests/test_dreaming_gtd_capture.py +35 -0
- package/tests/test_e2e_automated.py +8 -5
- package/tests/test_fuse_mount.py +10 -10
- package/tests/test_gtd_brief.py +46 -0
- package/tests/test_gtd_malformed_tolerance.py +31 -0
- package/tests/test_housekeeping.py +15 -15
- package/tests/test_identity_migrate.py +251 -0
- package/tests/test_integration_backbone.py +598 -0
- package/tests/test_itil_gtd_lifecycle.py +37 -0
- package/tests/test_jobs_dropins.py +84 -0
- package/tests/test_mcp_server.py +82 -37
- package/tests/test_models.py +48 -4
- package/tests/test_multi_agent.py +31 -29
- package/tests/test_notifications.py +122 -32
- package/tests/test_onboard.py +63 -75
- package/tests/test_operator_link.py +78 -0
- package/tests/test_peers.py +14 -14
- package/tests/test_pillars.py +98 -0
- package/tests/test_preflight.py +3 -3
- package/tests/test_runtime.py +21 -0
- package/tests/test_scheduled_tasks.py +11 -6
- package/tests/test_scheduler_cli.py +47 -0
- package/tests/test_scheduler_features.py +133 -0
- package/tests/test_scheduler_integration.py +87 -0
- package/tests/test_scheduler_jobs.py +155 -0
- package/tests/test_scheduler_runner.py +64 -0
- package/tests/test_scheduler_state.py +57 -0
- package/tests/test_sdk.py +70 -0
- package/tests/test_service_health_incidents.py +34 -0
- package/tests/test_service_registry.py +52 -0
- package/tests/test_session_briefing.py +130 -0
- package/tests/test_snapshots.py +4 -4
- package/tests/test_sync_pipeline.py +26 -26
- package/tests/test_team_comms.py +2 -2
- package/tests/test_testrunner.py +2 -2
- package/tests/test_trust_graph.py +18 -0
- package/tests/test_unified_search.py +2 -2
- package/tests/test_version_check.py +10 -0
- package/tests/test_version_cmd.py +8 -8
- package/tests/test_whoami.py +1 -1
- package/systemd/skcomm-heartbeat.service +0 -18
- package/systemd/skcomm-queue-drain.service +0 -17
- /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
- /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Covers:
|
|
4
4
|
- Happy path: peer responds with pong, latency reported
|
|
5
5
|
- Timeout: peer never replies, command exits with code 1
|
|
6
|
-
- No-transport:
|
|
6
|
+
- No-transport: SKComms has no live transport, error reported
|
|
7
7
|
- Payload helpers: _make_ping_payload / _is_pong_for / _is_ping / _make_pong_payload
|
|
8
8
|
- --count > 1: multi-ping summary statistics
|
|
9
9
|
"""
|
|
@@ -11,7 +11,7 @@ from skcapstone.cloud9_bridge import Cloud9Bridge
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class FakeEmotionalPayload:
|
|
14
|
-
"""Mimics
|
|
14
|
+
"""Mimics cloud9.EmotionalPayload."""
|
|
15
15
|
|
|
16
16
|
def __init__(
|
|
17
17
|
self,
|
|
@@ -29,7 +29,7 @@ class FakeEmotionalPayload:
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class FakeMetadata:
|
|
32
|
-
"""Mimics
|
|
32
|
+
"""Mimics cloud9.Metadata."""
|
|
33
33
|
|
|
34
34
|
def __init__(
|
|
35
35
|
self,
|
|
@@ -45,7 +45,7 @@ class FakeMetadata:
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
class FakeRelationship:
|
|
48
|
-
"""Mimics
|
|
48
|
+
"""Mimics cloud9.RelationshipState."""
|
|
49
49
|
|
|
50
50
|
def __init__(self) -> None:
|
|
51
51
|
self.partners = ["Lumina", "Chef"]
|
|
@@ -54,7 +54,7 @@ class FakeRelationship:
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
class FakeHints:
|
|
57
|
-
"""Mimics
|
|
57
|
+
"""Mimics cloud9.RehydrationHints."""
|
|
58
58
|
|
|
59
59
|
def __init__(self) -> None:
|
|
60
60
|
self.visual_anchors = ["The breakthrough moment", "Genuine connection"]
|
|
@@ -62,14 +62,14 @@ class FakeHints:
|
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
class FakeIntegrity:
|
|
65
|
-
"""Mimics
|
|
65
|
+
"""Mimics cloud9.Integrity."""
|
|
66
66
|
|
|
67
67
|
def __init__(self, checksum: str = "sha256:abc123") -> None:
|
|
68
68
|
self.checksum = checksum
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
class FakeFEB:
|
|
72
|
-
"""Mimics
|
|
72
|
+
"""Mimics cloud9.FEB."""
|
|
73
73
|
|
|
74
74
|
def __init__(
|
|
75
75
|
self,
|
|
@@ -34,7 +34,7 @@ def _make_loop(tmp_path: Path, config: ConsciousnessConfig | None = None) -> Con
|
|
|
34
34
|
if config is None:
|
|
35
35
|
config = ConsciousnessConfig(
|
|
36
36
|
auto_memory=False, # avoid skcapstone.memory_engine I/O
|
|
37
|
-
auto_ack=False, # no
|
|
37
|
+
auto_ack=False, # no SKComms needed
|
|
38
38
|
use_inotify=False, # no filesystem watcher needed
|
|
39
39
|
)
|
|
40
40
|
|
|
@@ -321,7 +321,7 @@ class TestSimpleEnvelope:
|
|
|
321
321
|
"""Test the minimal envelope for inotify-detected messages."""
|
|
322
322
|
|
|
323
323
|
def test_parse_standard_format(self):
|
|
324
|
-
"""Standard
|
|
324
|
+
"""Standard SKComms envelope format parses correctly."""
|
|
325
325
|
data = {
|
|
326
326
|
"sender": "jarvis",
|
|
327
327
|
"payload": {
|
|
@@ -408,8 +408,8 @@ class TestProcessEnvelopeACK:
|
|
|
408
408
|
def test_ack_uses_message_type_kwarg(self, tmp_path):
|
|
409
409
|
"""ACK send must use message_type kwarg, not content_type — regression for TypeError."""
|
|
410
410
|
loop = self._make_loop(tmp_path)
|
|
411
|
-
|
|
412
|
-
loop.
|
|
411
|
+
mock_skcomms = MagicMock()
|
|
412
|
+
loop.set_skcomms(mock_skcomms)
|
|
413
413
|
# Patch bridge so test doesn't hang on LLM calls
|
|
414
414
|
loop._bridge = MagicMock()
|
|
415
415
|
loop._bridge.generate.return_value = "test response"
|
|
@@ -419,7 +419,7 @@ class TestProcessEnvelopeACK:
|
|
|
419
419
|
|
|
420
420
|
# Find the ACK call (first send call with "ACK" as message)
|
|
421
421
|
ack_calls = [
|
|
422
|
-
c for c in
|
|
422
|
+
c for c in mock_skcomms.send.call_args_list
|
|
423
423
|
if len(c.args) >= 2 and c.args[1] == "ACK"
|
|
424
424
|
]
|
|
425
425
|
assert ack_calls, "Expected at least one ACK send call"
|
|
@@ -438,15 +438,15 @@ class TestProcessEnvelopeACK:
|
|
|
438
438
|
def test_ack_not_sent_when_auto_ack_disabled(self, tmp_path):
|
|
439
439
|
"""When auto_ack is False, no ACK is sent."""
|
|
440
440
|
loop = self._make_loop(tmp_path, auto_ack=False)
|
|
441
|
-
|
|
442
|
-
loop.
|
|
441
|
+
mock_skcomms = MagicMock()
|
|
442
|
+
loop.set_skcomms(mock_skcomms)
|
|
443
443
|
loop._bridge = MagicMock()
|
|
444
444
|
loop._bridge.generate.return_value = "test response"
|
|
445
445
|
|
|
446
446
|
loop.process_envelope(self._make_envelope())
|
|
447
447
|
|
|
448
448
|
ack_calls = [
|
|
449
|
-
c for c in
|
|
449
|
+
c for c in mock_skcomms.send.call_args_list
|
|
450
450
|
if len(c.args) >= 2 and c.args[1] == "ACK"
|
|
451
451
|
]
|
|
452
452
|
assert not ack_calls, "ACK should not be sent when auto_ack is False"
|
|
@@ -454,15 +454,15 @@ class TestProcessEnvelopeACK:
|
|
|
454
454
|
def test_ack_skipped_for_ack_type_messages(self, tmp_path):
|
|
455
455
|
"""Incoming ACK messages are skipped — no processing, no re-ACK."""
|
|
456
456
|
loop = self._make_loop(tmp_path, auto_ack=True)
|
|
457
|
-
|
|
458
|
-
loop.
|
|
457
|
+
mock_skcomms = MagicMock()
|
|
458
|
+
loop.set_skcomms(mock_skcomms)
|
|
459
459
|
loop._bridge = MagicMock()
|
|
460
460
|
|
|
461
461
|
ack_envelope = self._make_envelope(content="ACK", content_type="ack")
|
|
462
462
|
result = loop.process_envelope(ack_envelope)
|
|
463
463
|
|
|
464
464
|
assert result is None, "ACK-type messages should be skipped (return None)"
|
|
465
|
-
|
|
465
|
+
mock_skcomms.send.assert_not_called()
|
|
466
466
|
|
|
467
467
|
|
|
468
468
|
class TestSystemPromptBuilderCache:
|
|
@@ -100,6 +100,31 @@ class TestBoard:
|
|
|
100
100
|
assert len(loaded) == 1
|
|
101
101
|
assert loaded[0].title == "Test task"
|
|
102
102
|
|
|
103
|
+
def test_load_tasks_skips_malformed_and_logs(self, board: Board, caplog):
|
|
104
|
+
"""A malformed task file is skipped (not crashed on) and logged loudly.
|
|
105
|
+
|
|
106
|
+
Regression: notes-as-string used to fail Pydantic validation, and
|
|
107
|
+
load_tasks swallowed the error silently — dropping the task from the
|
|
108
|
+
board entirely (invisible: neither open nor done). The valid task must
|
|
109
|
+
still load; the bad one must produce a warning, not a silent vanish.
|
|
110
|
+
"""
|
|
111
|
+
import json
|
|
112
|
+
import logging
|
|
113
|
+
|
|
114
|
+
good = board.create_task(Task(title="Good task"))
|
|
115
|
+
assert good.exists()
|
|
116
|
+
# Hand-write a malformed task file (notes as a string, not a list).
|
|
117
|
+
(board.tasks_dir / "bad.json").write_text(
|
|
118
|
+
json.dumps({"id": "badtask1", "title": "Bad", "notes": "oops a string"}),
|
|
119
|
+
encoding="utf-8",
|
|
120
|
+
)
|
|
121
|
+
with caplog.at_level(logging.WARNING):
|
|
122
|
+
loaded = board.load_tasks()
|
|
123
|
+
titles = {t.title for t in loaded}
|
|
124
|
+
assert "Good task" in titles
|
|
125
|
+
assert "Bad" not in titles
|
|
126
|
+
assert any("bad.json" in r.message for r in caplog.records)
|
|
127
|
+
|
|
103
128
|
def test_save_and_load_agent(self, board: Board):
|
|
104
129
|
agent = AgentFile(agent="jarvis", capabilities=["python"])
|
|
105
130
|
board.save_agent(agent)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Exercises the real interfaces between packages:
|
|
4
4
|
capauth -> skcapstone (identity discovery)
|
|
5
5
|
skcapstone -> skmemory (built-in memory engine)
|
|
6
|
-
|
|
6
|
+
skcomms -> file transport (message send/receive)
|
|
7
7
|
skchat -> skmemory (chat history storage)
|
|
8
8
|
full pipeline: identity -> memory -> message -> coord -> sync
|
|
9
9
|
|
|
@@ -122,23 +122,23 @@ class TestCapAuthToSKCapstone:
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
125
|
-
# 2.
|
|
125
|
+
# 2. SKComms file transport — cross-process message delivery
|
|
126
126
|
# ═══════════════════════════════════════════════════════════════════════════
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
class
|
|
130
|
-
"""
|
|
129
|
+
class TestSKCommsFileTransport:
|
|
130
|
+
"""SKComms file transport sends and receives messages via filesystem."""
|
|
131
131
|
|
|
132
132
|
def test_send_and_receive_via_file_transport(self, tmp_path: Path):
|
|
133
133
|
"""Message sent via file transport is receivable from the inbox."""
|
|
134
|
-
from
|
|
134
|
+
from skcomms.models import (
|
|
135
135
|
MessageEnvelope,
|
|
136
136
|
MessageMetadata,
|
|
137
137
|
MessagePayload,
|
|
138
138
|
MessageType,
|
|
139
139
|
RoutingConfig,
|
|
140
140
|
)
|
|
141
|
-
from
|
|
141
|
+
from skcomms.transports.file import FileTransport
|
|
142
142
|
|
|
143
143
|
outbox = tmp_path / "outbox"
|
|
144
144
|
inbox = tmp_path / "inbox"
|
|
@@ -170,8 +170,8 @@ class TestSKCommFileTransport:
|
|
|
170
170
|
|
|
171
171
|
def test_multiple_messages_ordered(self, tmp_path: Path):
|
|
172
172
|
"""Multiple messages are received in order."""
|
|
173
|
-
from
|
|
174
|
-
from
|
|
173
|
+
from skcomms.models import MessageEnvelope, MessagePayload, MessageType
|
|
174
|
+
from skcomms.transports.file import FileTransport
|
|
175
175
|
|
|
176
176
|
outbox = tmp_path / "outbox"
|
|
177
177
|
transport = FileTransport(outbox_path=outbox, inbox_path=tmp_path / "noop")
|
|
@@ -273,7 +273,7 @@ class TestFullSovereignPipeline:
|
|
|
273
273
|
1. Init agent with identity, memory, trust, security, sync
|
|
274
274
|
2. Store a memory (skcapstone memory engine)
|
|
275
275
|
3. Create a chat message (skchat models)
|
|
276
|
-
4. Send it via file transport (
|
|
276
|
+
4. Send it via file transport (skcomms)
|
|
277
277
|
5. Store it as a memory (cross: skchat -> skcapstone)
|
|
278
278
|
6. Create and complete a coord task
|
|
279
279
|
7. Collect a sync seed with everything
|
|
@@ -300,11 +300,11 @@ class TestFullSovereignPipeline:
|
|
|
300
300
|
)
|
|
301
301
|
|
|
302
302
|
# 4. Send via file transport
|
|
303
|
-
from
|
|
304
|
-
from
|
|
303
|
+
from skcomms.models import MessageEnvelope, MessagePayload, MessageType
|
|
304
|
+
from skcomms.transports.file import FileTransport
|
|
305
305
|
|
|
306
|
-
outbox = tmp_agent_home / "
|
|
307
|
-
peer_inbox = tmp_agent_home / "
|
|
306
|
+
outbox = tmp_agent_home / "skcomms_outbox"
|
|
307
|
+
peer_inbox = tmp_agent_home / "skcomms_peer_inbox"
|
|
308
308
|
transport = FileTransport(outbox_path=outbox, inbox_path=peer_inbox)
|
|
309
309
|
|
|
310
310
|
envelope = MessageEnvelope(
|
|
@@ -417,16 +417,16 @@ class TestPackageImportCompatibility:
|
|
|
417
417
|
"""All five core packages can be imported simultaneously."""
|
|
418
418
|
|
|
419
419
|
def test_all_packages_importable(self):
|
|
420
|
-
"""capauth, skcapstone, skmemory,
|
|
420
|
+
"""capauth, skcapstone, skmemory, skcomms, skchat all import cleanly."""
|
|
421
421
|
import capauth
|
|
422
422
|
import skcapstone
|
|
423
423
|
import skchat
|
|
424
|
-
import
|
|
424
|
+
import skcomms
|
|
425
425
|
import skmemory
|
|
426
426
|
|
|
427
427
|
assert hasattr(capauth, "__version__") or hasattr(capauth, "profile")
|
|
428
428
|
assert hasattr(skcapstone, "__version__")
|
|
429
|
-
assert hasattr(
|
|
429
|
+
assert hasattr(skcomms, "__version__") or True
|
|
430
430
|
assert hasattr(skchat, "__version__") or True
|
|
431
431
|
assert hasattr(skmemory, "__version__") or True
|
|
432
432
|
|
|
@@ -437,11 +437,11 @@ class TestPackageImportCompatibility:
|
|
|
437
437
|
assert callable(init_profile)
|
|
438
438
|
assert callable(load_profile)
|
|
439
439
|
|
|
440
|
-
def
|
|
441
|
-
"""
|
|
442
|
-
from
|
|
440
|
+
def test_skcomms_core_module(self):
|
|
441
|
+
"""skcomms.core has SKComms class."""
|
|
442
|
+
from skcomms.core import SKComms
|
|
443
443
|
|
|
444
|
-
assert callable(
|
|
444
|
+
assert callable(SKComms.from_config)
|
|
445
445
|
|
|
446
446
|
def test_skchat_models_module(self):
|
|
447
447
|
"""skchat.models has ChatMessage and Thread."""
|
|
@@ -457,7 +457,7 @@ class TestPackageImportCompatibility:
|
|
|
457
457
|
for mod in [
|
|
458
458
|
"skcapstone.runtime",
|
|
459
459
|
"capauth.profile",
|
|
460
|
-
"
|
|
460
|
+
"skcomms.core",
|
|
461
461
|
"skchat.models",
|
|
462
462
|
"skcapstone.memory_engine",
|
|
463
463
|
"skcapstone.coordination",
|
package/tests/test_daemon.py
CHANGED
|
@@ -169,13 +169,13 @@ class TestDaemonService:
|
|
|
169
169
|
assert svc.state.running is False
|
|
170
170
|
assert not (daemon_home / "daemon.pid").exists()
|
|
171
171
|
|
|
172
|
-
def
|
|
172
|
+
def test_poll_loop_with_mock_skcomms(self, daemon_home):
|
|
173
173
|
config = DaemonConfig(home=daemon_home, poll_interval=1, port=0)
|
|
174
174
|
svc = DaemonService(config)
|
|
175
175
|
|
|
176
176
|
mock_comm = MagicMock()
|
|
177
177
|
mock_comm.receive.return_value = []
|
|
178
|
-
svc.
|
|
178
|
+
svc._skcomms = mock_comm
|
|
179
179
|
|
|
180
180
|
svc._stop_event = threading.Event()
|
|
181
181
|
t = threading.Thread(target=svc._poll_loop, daemon=True)
|
|
@@ -316,8 +316,8 @@ class TestHeartbeatBeaconWiring:
|
|
|
316
316
|
mock_heartbeat_mod.HeartbeatBeacon.return_value = mock_beacon_instance
|
|
317
317
|
|
|
318
318
|
patched = {
|
|
319
|
-
"
|
|
320
|
-
"
|
|
319
|
+
"skcomms": MagicMock(),
|
|
320
|
+
"skcomms.core": MagicMock(),
|
|
321
321
|
"skcapstone.runtime": mock_runtime_mod,
|
|
322
322
|
"skcapstone.heartbeat": mock_heartbeat_mod,
|
|
323
323
|
"skcapstone.consciousness_config": MagicMock(),
|
|
@@ -39,7 +39,7 @@ def _make_service(tmp_path: Path) -> DaemonService:
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def _make_fake_envelope(sender: str = "peer-a", content: str = "hello") -> SimpleNamespace:
|
|
42
|
-
"""Build a minimal fake
|
|
42
|
+
"""Build a minimal fake SKComms envelope."""
|
|
43
43
|
return SimpleNamespace(
|
|
44
44
|
message_id=str(uuid.uuid4()),
|
|
45
45
|
sender=sender,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
All Docker SDK calls are mocked so no real daemon is required.
|
|
4
4
|
Covers provision, configure, start, stop, destroy, rotate,
|
|
5
|
-
health_check, and generate_compose (including
|
|
5
|
+
health_check, and generate_compose (including SKComms/MCP wiring).
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
@@ -710,12 +710,12 @@ class TestProvisionTeamName:
|
|
|
710
710
|
|
|
711
711
|
|
|
712
712
|
# ---------------------------------------------------------------------------
|
|
713
|
-
#
|
|
713
|
+
# SKComms / MCP sovereign wiring
|
|
714
714
|
# ---------------------------------------------------------------------------
|
|
715
715
|
|
|
716
716
|
|
|
717
717
|
class TestSovereignWiring:
|
|
718
|
-
"""Verify
|
|
718
|
+
"""Verify SKComms and MCP env vars are injected correctly."""
|
|
719
719
|
|
|
720
720
|
def test_mcp_host_injected_in_env(self, mock_docker_client):
|
|
721
721
|
mock_client, mock_container = mock_docker_client
|
|
@@ -732,17 +732,17 @@ class TestSovereignWiring:
|
|
|
732
732
|
env = kwargs["environment"]
|
|
733
733
|
assert env.get("SKCAPSTONE_MCP_HOST") == "host-gateway:8765"
|
|
734
734
|
|
|
735
|
-
def
|
|
735
|
+
def test_skcomms_home_env_injected_when_dir_exists(
|
|
736
736
|
self, mock_docker_client, tmp_path
|
|
737
737
|
):
|
|
738
|
-
|
|
739
|
-
|
|
738
|
+
skcomms_dir = tmp_path / "skcomms"
|
|
739
|
+
skcomms_dir.mkdir()
|
|
740
740
|
|
|
741
741
|
mock_client, mock_container = mock_docker_client
|
|
742
742
|
provider = DockerProvider(
|
|
743
743
|
base_image="python:3.12-slim",
|
|
744
744
|
network_name="skcapstone",
|
|
745
|
-
|
|
745
|
+
skcomms_home=str(skcomms_dir),
|
|
746
746
|
)
|
|
747
747
|
spec = _make_spec()
|
|
748
748
|
with patch.object(provider, "_client", return_value=mock_client):
|
|
@@ -750,19 +750,19 @@ class TestSovereignWiring:
|
|
|
750
750
|
|
|
751
751
|
kwargs = mock_client.containers.create.call_args[1]
|
|
752
752
|
env = kwargs["environment"]
|
|
753
|
-
assert env.get("
|
|
753
|
+
assert env.get("SKCOMMS_HOME") == "/skcomms"
|
|
754
754
|
|
|
755
|
-
def
|
|
755
|
+
def test_skcomms_volume_mounted_when_dir_exists(
|
|
756
756
|
self, mock_docker_client, tmp_path
|
|
757
757
|
):
|
|
758
|
-
|
|
759
|
-
|
|
758
|
+
skcomms_dir = tmp_path / "skcomms"
|
|
759
|
+
skcomms_dir.mkdir()
|
|
760
760
|
|
|
761
761
|
mock_client, mock_container = mock_docker_client
|
|
762
762
|
provider = DockerProvider(
|
|
763
763
|
base_image="python:3.12-slim",
|
|
764
764
|
network_name="skcapstone",
|
|
765
|
-
|
|
765
|
+
skcomms_home=str(skcomms_dir),
|
|
766
766
|
)
|
|
767
767
|
spec = _make_spec()
|
|
768
768
|
with patch.object(provider, "_client", return_value=mock_client):
|
|
@@ -770,15 +770,15 @@ class TestSovereignWiring:
|
|
|
770
770
|
|
|
771
771
|
kwargs = mock_client.containers.create.call_args[1]
|
|
772
772
|
volumes = kwargs["volumes"]
|
|
773
|
-
assert str(
|
|
774
|
-
assert volumes[str(
|
|
773
|
+
assert str(skcomms_dir) in volumes
|
|
774
|
+
assert volumes[str(skcomms_dir)]["bind"] == "/skcomms"
|
|
775
775
|
|
|
776
|
-
def
|
|
776
|
+
def test_no_skcomms_mount_when_dir_missing(self, mock_docker_client):
|
|
777
777
|
mock_client, mock_container = mock_docker_client
|
|
778
778
|
provider = DockerProvider(
|
|
779
779
|
base_image="python:3.12-slim",
|
|
780
780
|
network_name="skcapstone",
|
|
781
|
-
|
|
781
|
+
skcomms_home="/nonexistent/skcomms",
|
|
782
782
|
)
|
|
783
783
|
spec = _make_spec()
|
|
784
784
|
with patch.object(provider, "_client", return_value=mock_client):
|
|
@@ -786,7 +786,7 @@ class TestSovereignWiring:
|
|
|
786
786
|
|
|
787
787
|
kwargs = mock_client.containers.create.call_args[1]
|
|
788
788
|
volumes = kwargs["volumes"]
|
|
789
|
-
assert "/nonexistent/
|
|
789
|
+
assert "/nonexistent/skcomms" not in volumes
|
|
790
790
|
|
|
791
791
|
def test_mcp_socket_env_injected_when_socket_missing(self, mock_docker_client):
|
|
792
792
|
"""SKCAPSTONE_MCP_SOCKET env is set regardless; socket mounted only if exists."""
|
|
@@ -894,12 +894,12 @@ class TestRotate:
|
|
|
894
894
|
|
|
895
895
|
|
|
896
896
|
# ---------------------------------------------------------------------------
|
|
897
|
-
# generate_compose() — MCP service +
|
|
897
|
+
# generate_compose() — MCP service + SKComms volume
|
|
898
898
|
# ---------------------------------------------------------------------------
|
|
899
899
|
|
|
900
900
|
|
|
901
901
|
class TestGenerateComposeSovereignExtensions:
|
|
902
|
-
"""Tests for
|
|
902
|
+
"""Tests for SKComms/MCP extensions in generate_compose()."""
|
|
903
903
|
|
|
904
904
|
def test_mcp_service_added_when_requested(self, provider):
|
|
905
905
|
bp = _make_blueprint(agent_count=1)
|
|
@@ -932,32 +932,32 @@ class TestGenerateComposeSovereignExtensions:
|
|
|
932
932
|
parsed = yaml.safe_load(output)
|
|
933
933
|
assert "skcapstone-mcp" not in parsed["services"]
|
|
934
934
|
|
|
935
|
-
def
|
|
936
|
-
|
|
937
|
-
|
|
935
|
+
def test_skcomms_volume_in_compose_when_configured(self, tmp_path):
|
|
936
|
+
skcomms_dir = tmp_path / "skcomms"
|
|
937
|
+
skcomms_dir.mkdir()
|
|
938
938
|
provider = DockerProvider(
|
|
939
939
|
base_image="python:3.12-slim",
|
|
940
940
|
network_name="skcapstone",
|
|
941
|
-
|
|
941
|
+
skcomms_home=str(skcomms_dir),
|
|
942
942
|
)
|
|
943
943
|
bp = _make_blueprint(agent_count=1)
|
|
944
944
|
output = provider.generate_compose(bp)
|
|
945
945
|
parsed = yaml.safe_load(output)
|
|
946
|
-
assert "
|
|
946
|
+
assert "skcomms-data" in parsed.get("volumes", {})
|
|
947
947
|
|
|
948
|
-
def
|
|
949
|
-
|
|
950
|
-
|
|
948
|
+
def test_skcomms_env_on_agents_when_configured(self, tmp_path):
|
|
949
|
+
skcomms_dir = tmp_path / "skcomms"
|
|
950
|
+
skcomms_dir.mkdir()
|
|
951
951
|
provider = DockerProvider(
|
|
952
952
|
base_image="python:3.12-slim",
|
|
953
953
|
network_name="skcapstone",
|
|
954
|
-
|
|
954
|
+
skcomms_home=str(skcomms_dir),
|
|
955
955
|
)
|
|
956
956
|
bp = _make_blueprint(agent_count=1)
|
|
957
957
|
output = provider.generate_compose(bp)
|
|
958
958
|
parsed = yaml.safe_load(output)
|
|
959
959
|
for svc in parsed["services"].values():
|
|
960
|
-
assert svc["environment"].get("
|
|
960
|
+
assert svc["environment"].get("SKCOMMS_HOME") == "/skcomms"
|
|
961
961
|
|
|
962
962
|
def test_mcp_service_volume_included(self, provider):
|
|
963
963
|
bp = _make_blueprint(agent_count=1)
|