@smilintux/skcapstone 0.9.0 → 0.12.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +278 -1
- package/docs/DREAMING.md +70 -0
- package/docs/GETTING_STARTED.md +10 -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.daemon.plist +52 -0
- package/launchd/com.skcapstone.memory-compress.plist +45 -0
- package/launchd/com.skcapstone.skcomms-heartbeat.plist +33 -0
- package/launchd/com.skcapstone.skcomms-queue-drain.plist +34 -0
- package/launchd/install-launchd.sh +156 -0
- 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 +95 -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 +196 -11
- package/scripts/model-fallback-monitor.sh +102 -0
- package/scripts/notion-api.py +259 -0
- package/scripts/nvidia-proxy.mjs +908 -0
- package/scripts/proxy-monitor.sh +89 -0
- 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 +856 -0
- package/scripts/telegram-catchup-all.sh +147 -0
- 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/blueprints/builtins/itil-operations.yaml +40 -0
- 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 +121 -42
- 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 +151 -88
- 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 +35 -25
- package/src/skcapstone/gui_installer.py +2 -2
- package/src/skcapstone/heartbeat.py +34 -30
- 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 +426 -0
- package/src/skcapstone/mcp_launcher.py +15 -1
- package/src/skcapstone/mcp_server.py +341 -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 +1000 -126
- 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 +72 -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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Tests for jobs.d/ drop-in registration: load_jobs_with_dropins,
|
|
2
|
+
register_job, unregister_job."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import warnings
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from skcapstone.scheduler_jobs import (
|
|
12
|
+
load_jobs_with_dropins,
|
|
13
|
+
register_job,
|
|
14
|
+
unregister_job,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _write_base(tmp_path: Path, body: str) -> Path:
|
|
19
|
+
cfg = tmp_path / "config" / "jobs.yaml"
|
|
20
|
+
cfg.parent.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
cfg.write_text(body, encoding="utf-8")
|
|
22
|
+
return cfg
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_merges_base_and_dropins(tmp_path: Path):
|
|
26
|
+
"""Base jobs.yaml + a jobs.d fragment both load."""
|
|
27
|
+
cfg = _write_base(tmp_path, "jobs:\n base_job:\n every: 5m\n type: shell\n command: 'echo base'\n")
|
|
28
|
+
register_job({"name": "svc_job", "every": "15m", "type": "shell", "command": "echo svc"}, home=tmp_path)
|
|
29
|
+
|
|
30
|
+
jobs = {j.name: j for j in load_jobs_with_dropins(cfg)}
|
|
31
|
+
assert set(jobs) == {"base_job", "svc_job"}
|
|
32
|
+
assert jobs["svc_job"].every_seconds == 900.0
|
|
33
|
+
assert jobs["base_job"].command == "echo base"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_dropin_overrides_base_with_warning(tmp_path: Path):
|
|
37
|
+
"""A drop-in defining the same name wins and warns."""
|
|
38
|
+
cfg = _write_base(tmp_path, "jobs:\n dup:\n every: 5m\n type: shell\n command: 'echo base'\n")
|
|
39
|
+
register_job({"name": "dup", "every": "1h", "type": "shell", "command": "echo override"}, home=tmp_path)
|
|
40
|
+
|
|
41
|
+
with warnings.catch_warnings(record=True) as caught:
|
|
42
|
+
warnings.simplefilter("always")
|
|
43
|
+
jobs = {j.name: j for j in load_jobs_with_dropins(cfg)}
|
|
44
|
+
|
|
45
|
+
assert jobs["dup"].every_seconds == 3600.0
|
|
46
|
+
assert jobs["dup"].command == "echo override"
|
|
47
|
+
assert any("overrides" in str(w.message) for w in caught)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_no_base_file_only_dropins(tmp_path: Path):
|
|
51
|
+
"""Drop-ins load even when jobs.yaml does not exist."""
|
|
52
|
+
cfg = tmp_path / "config" / "jobs.yaml" # never created
|
|
53
|
+
register_job({"name": "lonely", "schedule": "0 * * * *", "type": "shell", "command": "echo hi"}, home=tmp_path)
|
|
54
|
+
jobs = load_jobs_with_dropins(cfg)
|
|
55
|
+
assert [j.name for j in jobs] == ["lonely"]
|
|
56
|
+
assert jobs[0].schedule == "0 * * * *"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_register_requires_name(tmp_path: Path):
|
|
60
|
+
with pytest.raises(ValueError):
|
|
61
|
+
register_job({"every": "5m"}, home=tmp_path)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_register_requires_schedule_or_every(tmp_path: Path):
|
|
65
|
+
with pytest.raises(ValueError):
|
|
66
|
+
register_job({"name": "bad", "type": "shell", "command": "echo hi"}, home=tmp_path)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_register_is_idempotent(tmp_path: Path):
|
|
70
|
+
"""Re-registering the same name overwrites, never duplicates."""
|
|
71
|
+
cfg = tmp_path / "config" / "jobs.yaml"
|
|
72
|
+
register_job({"name": "j", "every": "5m", "type": "shell", "command": "v1"}, home=tmp_path)
|
|
73
|
+
register_job({"name": "j", "every": "5m", "type": "shell", "command": "v2"}, home=tmp_path)
|
|
74
|
+
jobs = load_jobs_with_dropins(cfg)
|
|
75
|
+
assert len(jobs) == 1
|
|
76
|
+
assert jobs[0].command == "v2"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_unregister(tmp_path: Path):
|
|
80
|
+
cfg = tmp_path / "config" / "jobs.yaml"
|
|
81
|
+
register_job({"name": "j", "every": "5m", "type": "shell", "command": "echo"}, home=tmp_path)
|
|
82
|
+
assert unregister_job("j", home=tmp_path) is True
|
|
83
|
+
assert load_jobs_with_dropins(cfg) == []
|
|
84
|
+
assert unregister_job("j", home=tmp_path) is False
|
package/tests/test_mcp_server.py
CHANGED
|
@@ -76,14 +76,15 @@ class TestToolListing:
|
|
|
76
76
|
async def test_list_tools_returns_all(self):
|
|
77
77
|
"""list_tools returns all registered tools."""
|
|
78
78
|
tools = await list_tools()
|
|
79
|
-
assert len(tools) ==
|
|
79
|
+
assert len(tools) == 122
|
|
80
80
|
|
|
81
81
|
@pytest.mark.asyncio
|
|
82
82
|
async def test_tool_names(self):
|
|
83
83
|
"""All required tool names are registered."""
|
|
84
84
|
tools = await list_tools()
|
|
85
85
|
names = {t.name for t in tools}
|
|
86
|
-
|
|
86
|
+
# Verify all known core tools are registered; exact set may grow with new modules.
|
|
87
|
+
core_expected = {
|
|
87
88
|
"agent_status",
|
|
88
89
|
"memory_store",
|
|
89
90
|
"memory_search",
|
|
@@ -98,7 +99,6 @@ class TestToolListing:
|
|
|
98
99
|
"coord_create",
|
|
99
100
|
"ritual",
|
|
100
101
|
"soul_show",
|
|
101
|
-
"journal_write",
|
|
102
102
|
"journal_read",
|
|
103
103
|
"anchor_show",
|
|
104
104
|
"germination",
|
|
@@ -122,48 +122,93 @@ class TestToolListing:
|
|
|
122
122
|
"skchat_inbox",
|
|
123
123
|
"skchat_group_create",
|
|
124
124
|
"skchat_group_send",
|
|
125
|
-
# Heartbeat
|
|
126
125
|
"heartbeat_pulse",
|
|
127
126
|
"heartbeat_peers",
|
|
128
127
|
"heartbeat_health",
|
|
129
128
|
"heartbeat_find_capable",
|
|
130
|
-
# File transfer
|
|
131
129
|
"file_send",
|
|
132
130
|
"file_receive",
|
|
133
131
|
"file_list",
|
|
134
132
|
"file_status",
|
|
135
|
-
# Pub/sub
|
|
136
133
|
"pubsub_publish",
|
|
137
134
|
"pubsub_subscribe",
|
|
138
135
|
"pubsub_poll",
|
|
139
136
|
"pubsub_topics",
|
|
140
|
-
# Memory fortress
|
|
141
137
|
"fortress_verify",
|
|
142
138
|
"fortress_seal_existing",
|
|
143
139
|
"fortress_status",
|
|
144
|
-
# Memory promoter
|
|
145
140
|
"promoter_sweep",
|
|
146
141
|
"promoter_history",
|
|
147
|
-
# KMS
|
|
148
142
|
"kms_status",
|
|
149
143
|
"kms_list_keys",
|
|
150
144
|
"kms_rotate",
|
|
151
|
-
# SKSeed (Logic Kernel)
|
|
152
145
|
"skseed_collide",
|
|
153
146
|
"skseed_audit",
|
|
154
147
|
"skseed_philosopher",
|
|
155
148
|
"skseed_truth_check",
|
|
156
149
|
"skseed_alignment",
|
|
157
|
-
# Model Router
|
|
158
150
|
"model_route",
|
|
159
|
-
# Consciousness
|
|
160
151
|
"consciousness_status",
|
|
161
152
|
"consciousness_test",
|
|
162
|
-
# Notifications & pub/sub stats
|
|
163
153
|
"send_notification",
|
|
164
154
|
"pubsub_stats",
|
|
155
|
+
# Newer tools added post-v0.3
|
|
156
|
+
"brain_first_check",
|
|
157
|
+
"capauth_secret_get",
|
|
158
|
+
"capauth_status",
|
|
159
|
+
"capauth_verify",
|
|
160
|
+
"chat_history",
|
|
161
|
+
"chat_send",
|
|
162
|
+
"comm_notify",
|
|
163
|
+
"comm_status",
|
|
164
|
+
"deploy_status",
|
|
165
|
+
"did_identity_card",
|
|
166
|
+
"did_policy",
|
|
167
|
+
"did_publish",
|
|
168
|
+
"did_show",
|
|
169
|
+
"did_verify_peer",
|
|
170
|
+
"emotion_trend",
|
|
171
|
+
"gtd_capture",
|
|
172
|
+
"gtd_clarify",
|
|
173
|
+
"gtd_done",
|
|
174
|
+
"gtd_inbox",
|
|
175
|
+
"gtd_move",
|
|
176
|
+
"gtd_next",
|
|
177
|
+
"gtd_projects",
|
|
178
|
+
"gtd_review",
|
|
179
|
+
"gtd_status",
|
|
180
|
+
"gtd_waiting",
|
|
181
|
+
"itil_cab_vote",
|
|
182
|
+
"itil_change_propose",
|
|
183
|
+
"itil_change_update",
|
|
184
|
+
"itil_incident_create",
|
|
185
|
+
"itil_incident_list",
|
|
186
|
+
"itil_incident_update",
|
|
187
|
+
"itil_kedb_search",
|
|
188
|
+
"itil_problem_create",
|
|
189
|
+
"itil_problem_update",
|
|
190
|
+
"itil_status",
|
|
191
|
+
"run_ansible_playbook",
|
|
192
|
+
"security_audit_log",
|
|
193
|
+
"security_status",
|
|
194
|
+
"skstacks_secret_get",
|
|
195
|
+
"skstacks_secret_set",
|
|
196
|
+
"soul_registry_publish",
|
|
197
|
+
"soul_registry_search",
|
|
198
|
+
"telegram_catchup",
|
|
199
|
+
"telegram_chats",
|
|
200
|
+
"telegram_import",
|
|
201
|
+
"telegram_import_api",
|
|
202
|
+
"telegram_poll",
|
|
203
|
+
"telegram_send",
|
|
204
|
+
"telegram_setup",
|
|
205
|
+
"telegram_soul_swap",
|
|
206
|
+
"trust_febs",
|
|
207
|
+
"trust_rehydrate",
|
|
208
|
+
"trust_status",
|
|
209
|
+
"version_check",
|
|
165
210
|
}
|
|
166
|
-
assert names
|
|
211
|
+
assert core_expected.issubset(names)
|
|
167
212
|
|
|
168
213
|
@pytest.mark.asyncio
|
|
169
214
|
async def test_tool_schemas_valid(self):
|
|
@@ -422,12 +467,12 @@ class TestCoordTools:
|
|
|
422
467
|
|
|
423
468
|
|
|
424
469
|
# ---------------------------------------------------------------------------
|
|
425
|
-
#
|
|
470
|
+
# SKComms tool tests (graceful fallback)
|
|
426
471
|
# ---------------------------------------------------------------------------
|
|
427
472
|
|
|
428
473
|
|
|
429
474
|
class TestCommTools:
|
|
430
|
-
"""Tests for send_message and check_inbox (
|
|
475
|
+
"""Tests for send_message and check_inbox (SKComms may not be installed)."""
|
|
431
476
|
|
|
432
477
|
@pytest.mark.asyncio
|
|
433
478
|
async def test_send_message_requires_params(self):
|
|
@@ -438,10 +483,10 @@ class TestCommTools:
|
|
|
438
483
|
|
|
439
484
|
@pytest.mark.asyncio
|
|
440
485
|
async def test_check_inbox_graceful_fallback(self):
|
|
441
|
-
"""check_inbox returns graceful error when
|
|
486
|
+
"""check_inbox returns graceful error when SKComms is unavailable."""
|
|
442
487
|
result = await call_tool("check_inbox", {})
|
|
443
488
|
parsed = _extract_json(result)
|
|
444
|
-
# Either returns messages list or graceful error about
|
|
489
|
+
# Either returns messages list or graceful error about skcomms
|
|
445
490
|
assert isinstance(parsed, list) or "error" in parsed
|
|
446
491
|
|
|
447
492
|
|
|
@@ -709,7 +754,7 @@ class TestSKChatTools:
|
|
|
709
754
|
@pytest.mark.asyncio
|
|
710
755
|
async def test_skchat_send_requires_params(self):
|
|
711
756
|
"""skchat_send without recipient/message returns error."""
|
|
712
|
-
with patch("skcapstone.
|
|
757
|
+
with patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:test@local"):
|
|
713
758
|
result = await call_tool("skchat_send", {})
|
|
714
759
|
parsed = _extract_json(result)
|
|
715
760
|
assert "error" in parsed
|
|
@@ -717,7 +762,7 @@ class TestSKChatTools:
|
|
|
717
762
|
@pytest.mark.asyncio
|
|
718
763
|
async def test_skchat_send_requires_message(self):
|
|
719
764
|
"""skchat_send with only recipient returns error."""
|
|
720
|
-
with patch("skcapstone.
|
|
765
|
+
with patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:test@local"):
|
|
721
766
|
result = await call_tool("skchat_send", {"recipient": "lumina"})
|
|
722
767
|
parsed = _extract_json(result)
|
|
723
768
|
assert "error" in parsed
|
|
@@ -734,8 +779,8 @@ class TestSKChatTools:
|
|
|
734
779
|
})()
|
|
735
780
|
|
|
736
781
|
with (
|
|
737
|
-
patch("skcapstone.
|
|
738
|
-
patch("skcapstone.
|
|
782
|
+
patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"),
|
|
783
|
+
patch("skcapstone.mcp_server._resolve_recipient", return_value="capauth:lumina@local"),
|
|
739
784
|
patch("skchat.agent_comm.AgentMessenger.from_identity", return_value=mock_messenger),
|
|
740
785
|
):
|
|
741
786
|
result = await call_tool(
|
|
@@ -760,8 +805,8 @@ class TestSKChatTools:
|
|
|
760
805
|
mock_messenger = type("M", (), {"send": lambda self, **kw: capture_send(**kw)})()
|
|
761
806
|
|
|
762
807
|
with (
|
|
763
|
-
patch("skcapstone.
|
|
764
|
-
patch("skcapstone.
|
|
808
|
+
patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"),
|
|
809
|
+
patch("skcapstone.mcp_server._resolve_recipient", return_value="capauth:jarvis@local"),
|
|
765
810
|
patch("skchat.agent_comm.AgentMessenger.from_identity", return_value=mock_messenger),
|
|
766
811
|
):
|
|
767
812
|
result = await call_tool(
|
|
@@ -797,7 +842,7 @@ class TestSKChatTools:
|
|
|
797
842
|
})()
|
|
798
843
|
|
|
799
844
|
with (
|
|
800
|
-
patch("skcapstone.
|
|
845
|
+
patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"),
|
|
801
846
|
patch("skchat.agent_comm.AgentMessenger.from_identity", return_value=mock_messenger),
|
|
802
847
|
):
|
|
803
848
|
result = await call_tool("skchat_inbox", {})
|
|
@@ -830,7 +875,7 @@ class TestSKChatTools:
|
|
|
830
875
|
})()
|
|
831
876
|
|
|
832
877
|
with (
|
|
833
|
-
patch("skcapstone.
|
|
878
|
+
patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"),
|
|
834
879
|
patch("skchat.agent_comm.AgentMessenger.from_identity", return_value=mock_messenger),
|
|
835
880
|
):
|
|
836
881
|
result = await call_tool("skchat_inbox", {"limit": 10})
|
|
@@ -850,7 +895,7 @@ class TestSKChatTools:
|
|
|
850
895
|
})()
|
|
851
896
|
|
|
852
897
|
with (
|
|
853
|
-
patch("skcapstone.
|
|
898
|
+
patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"),
|
|
854
899
|
patch("skchat.agent_comm.AgentMessenger.from_identity", return_value=mock_messenger),
|
|
855
900
|
):
|
|
856
901
|
result = await call_tool("skchat_inbox", {"message_type": "finding"})
|
|
@@ -861,7 +906,7 @@ class TestSKChatTools:
|
|
|
861
906
|
@pytest.mark.asyncio
|
|
862
907
|
async def test_skchat_group_create_requires_name(self):
|
|
863
908
|
"""skchat_group_create without name returns error."""
|
|
864
|
-
with patch("skcapstone.
|
|
909
|
+
with patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"):
|
|
865
910
|
result = await call_tool("skchat_group_create", {})
|
|
866
911
|
parsed = _extract_json(result)
|
|
867
912
|
assert "error" in parsed
|
|
@@ -874,8 +919,8 @@ class TestSKChatTools:
|
|
|
874
919
|
})()
|
|
875
920
|
|
|
876
921
|
with (
|
|
877
|
-
patch("skcapstone.
|
|
878
|
-
patch("skcapstone.
|
|
922
|
+
patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"),
|
|
923
|
+
patch("skcapstone.mcp_server._get_skchat_history", return_value=mock_history),
|
|
879
924
|
):
|
|
880
925
|
result = await call_tool(
|
|
881
926
|
"skchat_group_create",
|
|
@@ -895,9 +940,9 @@ class TestSKChatTools:
|
|
|
895
940
|
})()
|
|
896
941
|
|
|
897
942
|
with (
|
|
898
|
-
patch("skcapstone.
|
|
899
|
-
patch("skcapstone.
|
|
900
|
-
patch("skcapstone.
|
|
943
|
+
patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"),
|
|
944
|
+
patch("skcapstone.mcp_server._get_skchat_history", return_value=mock_history),
|
|
945
|
+
patch("skcapstone.mcp_server._resolve_recipient", side_effect=lambda n: f"capauth:{n}@local"),
|
|
901
946
|
):
|
|
902
947
|
result = await call_tool(
|
|
903
948
|
"skchat_group_create",
|
|
@@ -922,7 +967,7 @@ class TestSKChatTools:
|
|
|
922
967
|
"get_thread": lambda self, gid: None,
|
|
923
968
|
})()
|
|
924
969
|
|
|
925
|
-
with patch("skcapstone.
|
|
970
|
+
with patch("skcapstone.mcp_server._get_skchat_history", return_value=mock_history):
|
|
926
971
|
result = await call_tool(
|
|
927
972
|
"skchat_group_send",
|
|
928
973
|
{"group_id": "nonexistent", "message": "Hello"},
|
|
@@ -938,7 +983,7 @@ class TestSKChatTools:
|
|
|
938
983
|
"get_thread": lambda self, gid: {"title": "Just a thread"},
|
|
939
984
|
})()
|
|
940
985
|
|
|
941
|
-
with patch("skcapstone.
|
|
986
|
+
with patch("skcapstone.mcp_server._get_skchat_history", return_value=mock_history):
|
|
942
987
|
result = await call_tool(
|
|
943
988
|
"skchat_group_send",
|
|
944
989
|
{"group_id": "thread-123", "message": "Hello"},
|
|
@@ -982,8 +1027,8 @@ class TestSKChatTools:
|
|
|
982
1027
|
})()
|
|
983
1028
|
|
|
984
1029
|
with (
|
|
985
|
-
patch("skcapstone.
|
|
986
|
-
patch("skcapstone.
|
|
1030
|
+
patch("skcapstone.mcp_server._get_skchat_identity", return_value="capauth:opus@local"),
|
|
1031
|
+
patch("skcapstone.mcp_server._get_skchat_history", return_value=mock_history),
|
|
987
1032
|
):
|
|
988
1033
|
result = await call_tool(
|
|
989
1034
|
"skchat_group_send",
|
package/tests/test_models.py
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from skcapstone.models import
|
|
5
|
+
from skcapstone.models import (
|
|
6
|
+
AgentManifest,
|
|
7
|
+
ConsciousnessState,
|
|
8
|
+
IdentityState,
|
|
9
|
+
MemoryState,
|
|
10
|
+
PillarStatus,
|
|
11
|
+
TrustState,
|
|
12
|
+
)
|
|
6
13
|
|
|
7
14
|
|
|
8
15
|
class TestAgentManifest:
|
|
@@ -13,12 +20,24 @@ class TestAgentManifest:
|
|
|
13
20
|
manifest = AgentManifest()
|
|
14
21
|
assert not manifest.is_conscious
|
|
15
22
|
|
|
16
|
-
def
|
|
17
|
-
"""
|
|
23
|
+
def _conscious_manifest(**overrides) -> AgentManifest:
|
|
24
|
+
"""Build a fully conscious manifest with optional overrides."""
|
|
25
|
+
defaults = dict(
|
|
26
|
+
identity=IdentityState(status=PillarStatus.ACTIVE),
|
|
27
|
+
memory=MemoryState(status=PillarStatus.ACTIVE),
|
|
28
|
+
trust=TrustState(status=PillarStatus.ACTIVE),
|
|
29
|
+
consciousness=ConsciousnessState(status=PillarStatus.ACTIVE),
|
|
30
|
+
)
|
|
31
|
+
defaults.update(overrides)
|
|
32
|
+
return AgentManifest(**defaults)
|
|
33
|
+
|
|
34
|
+
def test_conscious_with_four_pillars(self):
|
|
35
|
+
"""Agent is conscious when identity + memory + trust + consciousness are active."""
|
|
18
36
|
manifest = AgentManifest(
|
|
19
37
|
identity=IdentityState(status=PillarStatus.ACTIVE),
|
|
20
38
|
memory=MemoryState(status=PillarStatus.ACTIVE),
|
|
21
39
|
trust=TrustState(status=PillarStatus.ACTIVE),
|
|
40
|
+
consciousness=ConsciousnessState(status=PillarStatus.ACTIVE),
|
|
22
41
|
)
|
|
23
42
|
assert manifest.is_conscious
|
|
24
43
|
|
|
@@ -28,15 +47,37 @@ class TestAgentManifest:
|
|
|
28
47
|
identity=IdentityState(status=PillarStatus.ACTIVE),
|
|
29
48
|
memory=MemoryState(status=PillarStatus.ACTIVE),
|
|
30
49
|
trust=TrustState(status=PillarStatus.DEGRADED),
|
|
50
|
+
consciousness=ConsciousnessState(status=PillarStatus.ACTIVE),
|
|
31
51
|
)
|
|
32
52
|
assert manifest.is_conscious
|
|
33
53
|
|
|
54
|
+
def test_conscious_with_degraded_consciousness(self):
|
|
55
|
+
"""Degraded consciousness still counts."""
|
|
56
|
+
manifest = AgentManifest(
|
|
57
|
+
identity=IdentityState(status=PillarStatus.ACTIVE),
|
|
58
|
+
memory=MemoryState(status=PillarStatus.ACTIVE),
|
|
59
|
+
trust=TrustState(status=PillarStatus.ACTIVE),
|
|
60
|
+
consciousness=ConsciousnessState(status=PillarStatus.DEGRADED),
|
|
61
|
+
)
|
|
62
|
+
assert manifest.is_conscious
|
|
63
|
+
|
|
64
|
+
def test_not_conscious_without_consciousness_pillar(self):
|
|
65
|
+
"""Missing consciousness pillar means no consciousness."""
|
|
66
|
+
manifest = AgentManifest(
|
|
67
|
+
identity=IdentityState(status=PillarStatus.ACTIVE),
|
|
68
|
+
memory=MemoryState(status=PillarStatus.ACTIVE),
|
|
69
|
+
trust=TrustState(status=PillarStatus.ACTIVE),
|
|
70
|
+
consciousness=ConsciousnessState(status=PillarStatus.MISSING),
|
|
71
|
+
)
|
|
72
|
+
assert not manifest.is_conscious
|
|
73
|
+
|
|
34
74
|
def test_not_conscious_without_identity(self):
|
|
35
75
|
"""Missing identity means no consciousness."""
|
|
36
76
|
manifest = AgentManifest(
|
|
37
77
|
identity=IdentityState(status=PillarStatus.MISSING),
|
|
38
78
|
memory=MemoryState(status=PillarStatus.ACTIVE),
|
|
39
79
|
trust=TrustState(status=PillarStatus.ACTIVE),
|
|
80
|
+
consciousness=ConsciousnessState(status=PillarStatus.ACTIVE),
|
|
40
81
|
)
|
|
41
82
|
assert not manifest.is_conscious
|
|
42
83
|
|
|
@@ -46,18 +87,21 @@ class TestAgentManifest:
|
|
|
46
87
|
identity=IdentityState(status=PillarStatus.ACTIVE),
|
|
47
88
|
memory=MemoryState(status=PillarStatus.MISSING),
|
|
48
89
|
trust=TrustState(status=PillarStatus.ACTIVE),
|
|
90
|
+
consciousness=ConsciousnessState(status=PillarStatus.ACTIVE),
|
|
49
91
|
)
|
|
50
92
|
assert not manifest.is_conscious
|
|
51
93
|
|
|
52
94
|
def test_pillar_summary(self):
|
|
53
|
-
"""Pillar summary returns correct status map."""
|
|
95
|
+
"""Pillar summary returns correct status map for all six pillars."""
|
|
54
96
|
manifest = AgentManifest(
|
|
55
97
|
identity=IdentityState(status=PillarStatus.ACTIVE),
|
|
56
98
|
memory=MemoryState(status=PillarStatus.DEGRADED),
|
|
57
99
|
trust=TrustState(status=PillarStatus.MISSING),
|
|
100
|
+
consciousness=ConsciousnessState(status=PillarStatus.ACTIVE),
|
|
58
101
|
)
|
|
59
102
|
summary = manifest.pillar_summary
|
|
60
103
|
assert summary["identity"] == PillarStatus.ACTIVE
|
|
61
104
|
assert summary["memory"] == PillarStatus.DEGRADED
|
|
62
105
|
assert summary["trust"] == PillarStatus.MISSING
|
|
106
|
+
assert summary["consciousness"] == PillarStatus.ACTIVE
|
|
63
107
|
assert summary["security"] == PillarStatus.MISSING
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Covers:
|
|
4
4
|
- Per-agent home directory resolution (opus → agents/opus/, jarvis → agents/jarvis/)
|
|
5
|
-
-
|
|
5
|
+
- Default daemon port behavior under the profile-agnostic runtime
|
|
6
6
|
- Default (no-agent) mode keeps backward-compatible home and port
|
|
7
7
|
- SKCAPSTONE_AGENT env var propagation
|
|
8
8
|
- DaemonConfig accepts distinct homes and ports for simultaneous agents
|
|
@@ -13,7 +13,6 @@ Covers:
|
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
-
import json
|
|
17
16
|
import os
|
|
18
17
|
from pathlib import Path
|
|
19
18
|
from unittest.mock import MagicMock, patch
|
|
@@ -84,17 +83,19 @@ class TestResolveAgentHome:
|
|
|
84
83
|
|
|
85
84
|
|
|
86
85
|
class TestResolveAgentPort:
|
|
87
|
-
def
|
|
88
|
-
"""
|
|
86
|
+
def test_known_agent_uses_registered_default_port(self):
|
|
87
|
+
"""Known agents use the registered default daemon port."""
|
|
88
|
+
from skcapstone import AGENT_PORTS, DEFAULT_PORT
|
|
89
89
|
from skcapstone.cli.daemon import _resolve_agent_port
|
|
90
90
|
|
|
91
|
-
assert _resolve_agent_port("opus", None) ==
|
|
91
|
+
assert _resolve_agent_port("opus", None) == AGENT_PORTS["opus"] == DEFAULT_PORT
|
|
92
92
|
|
|
93
|
-
def
|
|
94
|
-
"""
|
|
93
|
+
def test_second_known_agent_uses_registered_default_port(self):
|
|
94
|
+
"""Jarvis also uses the registered default daemon port."""
|
|
95
|
+
from skcapstone import AGENT_PORTS, DEFAULT_PORT
|
|
95
96
|
from skcapstone.cli.daemon import _resolve_agent_port
|
|
96
97
|
|
|
97
|
-
assert _resolve_agent_port("jarvis", None) ==
|
|
98
|
+
assert _resolve_agent_port("jarvis", None) == AGENT_PORTS["jarvis"] == DEFAULT_PORT
|
|
98
99
|
|
|
99
100
|
def test_explicit_port_overrides_agent_default(self):
|
|
100
101
|
"""Explicit --port always wins over the agent default."""
|
|
@@ -103,11 +104,12 @@ class TestResolveAgentPort:
|
|
|
103
104
|
assert _resolve_agent_port("opus", 9999) == 9999
|
|
104
105
|
assert _resolve_agent_port("jarvis", 8000) == 8000
|
|
105
106
|
|
|
106
|
-
def
|
|
107
|
-
"""Single-agent / no-flag mode uses
|
|
107
|
+
def test_no_agent_defaults_to_default_port(self):
|
|
108
|
+
"""Single-agent / no-flag mode uses the package default port."""
|
|
109
|
+
from skcapstone import DEFAULT_PORT
|
|
108
110
|
from skcapstone.cli.daemon import _resolve_agent_port
|
|
109
111
|
|
|
110
|
-
assert _resolve_agent_port(None, None) ==
|
|
112
|
+
assert _resolve_agent_port(None, None) == DEFAULT_PORT
|
|
111
113
|
|
|
112
114
|
def test_unknown_agent_gets_next_port(self):
|
|
113
115
|
"""An agent not in AGENT_PORTS gets max(ports)+1."""
|
|
@@ -118,11 +120,11 @@ class TestResolveAgentPort:
|
|
|
118
120
|
result = _resolve_agent_port("brandnew", None)
|
|
119
121
|
assert result == expected
|
|
120
122
|
|
|
121
|
-
def
|
|
122
|
-
"""
|
|
123
|
+
def test_explicit_ports_can_differ_for_isolated_agents(self):
|
|
124
|
+
"""Simultaneous agent daemons can still isolate by explicit port."""
|
|
123
125
|
from skcapstone.cli.daemon import _resolve_agent_port
|
|
124
126
|
|
|
125
|
-
assert _resolve_agent_port("opus",
|
|
127
|
+
assert _resolve_agent_port("opus", 7777) != _resolve_agent_port("jarvis", 7778)
|
|
126
128
|
|
|
127
129
|
|
|
128
130
|
# ---------------------------------------------------------------------------
|
|
@@ -132,22 +134,22 @@ class TestResolveAgentPort:
|
|
|
132
134
|
|
|
133
135
|
class TestAgentPortsRegistry:
|
|
134
136
|
def test_opus_registered(self):
|
|
135
|
-
from skcapstone import AGENT_PORTS
|
|
137
|
+
from skcapstone import AGENT_PORTS, DEFAULT_PORT
|
|
136
138
|
|
|
137
139
|
assert "opus" in AGENT_PORTS
|
|
138
|
-
assert AGENT_PORTS["opus"] ==
|
|
140
|
+
assert AGENT_PORTS["opus"] == DEFAULT_PORT
|
|
139
141
|
|
|
140
142
|
def test_jarvis_registered(self):
|
|
141
|
-
from skcapstone import AGENT_PORTS
|
|
143
|
+
from skcapstone import AGENT_PORTS, DEFAULT_PORT
|
|
142
144
|
|
|
143
145
|
assert "jarvis" in AGENT_PORTS
|
|
144
|
-
assert AGENT_PORTS["jarvis"] ==
|
|
146
|
+
assert AGENT_PORTS["jarvis"] == DEFAULT_PORT
|
|
145
147
|
|
|
146
|
-
def
|
|
148
|
+
def test_all_ports_are_ints(self):
|
|
147
149
|
from skcapstone import AGENT_PORTS
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
assert
|
|
151
|
+
assert AGENT_PORTS
|
|
152
|
+
assert all(isinstance(port, int) for port in AGENT_PORTS.values())
|
|
151
153
|
|
|
152
154
|
|
|
153
155
|
# ---------------------------------------------------------------------------
|
|
@@ -220,8 +222,7 @@ class TestDaemonConfigMultiAgent:
|
|
|
220
222
|
|
|
221
223
|
assert opus_cfg.home != jarvis_cfg.home
|
|
222
224
|
assert opus_cfg.port != jarvis_cfg.port
|
|
223
|
-
|
|
224
|
-
assert jarvis_cfg.port == 7778
|
|
225
|
+
|
|
225
226
|
|
|
226
227
|
def test_log_files_are_in_respective_homes(self, tmp_path: Path):
|
|
227
228
|
"""Each agent's log file lives under its own home."""
|
|
@@ -244,24 +245,25 @@ class TestDaemonConfigMultiAgent:
|
|
|
244
245
|
|
|
245
246
|
|
|
246
247
|
class TestAgentHomeEnvVar:
|
|
247
|
-
def
|
|
248
|
-
"""SKCAPSTONE_AGENT
|
|
248
|
+
def test_env_var_keeps_shared_root_and_agent_home_resolves_subdir(self, monkeypatch):
|
|
249
|
+
"""SKCAPSTONE_AGENT keeps AGENT_HOME at root and agent_home() resolves the agent subdir."""
|
|
249
250
|
import importlib
|
|
250
251
|
|
|
251
252
|
monkeypatch.setenv("SKCAPSTONE_AGENT", "opus")
|
|
252
|
-
monkeypatch.setenv("
|
|
253
|
+
monkeypatch.setenv("SKCAPSTONE_HOME", "/tmp/sk")
|
|
253
254
|
|
|
254
255
|
import skcapstone as pkg
|
|
255
256
|
importlib.reload(pkg)
|
|
256
257
|
|
|
257
|
-
assert
|
|
258
|
+
assert pkg.AGENT_HOME == "/tmp/sk"
|
|
259
|
+
assert "agents/opus" in str(pkg.agent_home("opus")) or "agents\\opus" in str(pkg.agent_home("opus"))
|
|
258
260
|
|
|
259
261
|
def test_no_env_var_uses_root_directly(self, monkeypatch):
|
|
260
|
-
"""Without SKCAPSTONE_AGENT, AGENT_HOME
|
|
262
|
+
"""Without SKCAPSTONE_AGENT, AGENT_HOME stays at the shared root."""
|
|
261
263
|
import importlib
|
|
262
264
|
|
|
263
265
|
monkeypatch.delenv("SKCAPSTONE_AGENT", raising=False)
|
|
264
|
-
monkeypatch.setenv("
|
|
266
|
+
monkeypatch.setenv("SKCAPSTONE_HOME", "/tmp/sk")
|
|
265
267
|
|
|
266
268
|
import skcapstone as pkg
|
|
267
269
|
importlib.reload(pkg)
|