@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,90 @@
|
|
|
1
|
+
"""Brain-First Protocol MCP tools.
|
|
2
|
+
|
|
3
|
+
Exposes the brain-first memory consultation as an MCP tool so that
|
|
4
|
+
any MCP client can ask "what do I already know about this?" before
|
|
5
|
+
acting on a task.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from mcp.types import TextContent, Tool
|
|
11
|
+
|
|
12
|
+
from ._helpers import _json_response
|
|
13
|
+
|
|
14
|
+
TOOLS: list[Tool] = [
|
|
15
|
+
Tool(
|
|
16
|
+
name="brain_first_check",
|
|
17
|
+
description=(
|
|
18
|
+
"Brain-First Protocol: consult the agent's memory before "
|
|
19
|
+
"acting on a task. Extracts keywords from the given context, "
|
|
20
|
+
"searches memory for relevant prior knowledge, and returns "
|
|
21
|
+
"any matching memories. Use this before starting new work to "
|
|
22
|
+
"avoid duplicating effort or missing prior decisions."
|
|
23
|
+
),
|
|
24
|
+
inputSchema={
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"context": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": (
|
|
30
|
+
"The task description, prompt, or action context "
|
|
31
|
+
"to search memory for"
|
|
32
|
+
),
|
|
33
|
+
},
|
|
34
|
+
"tags": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"items": {"type": "string"},
|
|
37
|
+
"description": "Optional tag filter for the memory search",
|
|
38
|
+
},
|
|
39
|
+
"max_results": {
|
|
40
|
+
"type": "integer",
|
|
41
|
+
"description": "Max memories to return (default: from config, usually 5)",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
"required": ["context"],
|
|
45
|
+
},
|
|
46
|
+
),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def _handle_brain_first_check(args: dict) -> list[TextContent]:
|
|
51
|
+
"""Run a brain-first memory consultation."""
|
|
52
|
+
from ..brain_first import BrainFirstConfig, brain_first_check, _load_config
|
|
53
|
+
|
|
54
|
+
context = args.get("context", "")
|
|
55
|
+
if not context:
|
|
56
|
+
return _json_response({"error": "context is required"})
|
|
57
|
+
|
|
58
|
+
config = _load_config()
|
|
59
|
+
|
|
60
|
+
# Allow per-call override of max_results
|
|
61
|
+
max_results = args.get("max_results")
|
|
62
|
+
if max_results is not None:
|
|
63
|
+
config.max_results = max_results
|
|
64
|
+
|
|
65
|
+
result = brain_first_check(
|
|
66
|
+
context=context,
|
|
67
|
+
config=config,
|
|
68
|
+
tags=args.get("tags"),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
response = {
|
|
72
|
+
"enabled": result.enabled,
|
|
73
|
+
"query": result.query,
|
|
74
|
+
"keywords": result.keywords,
|
|
75
|
+
"memories_found": len(result.memories),
|
|
76
|
+
"memories": result.memories,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if result.error:
|
|
80
|
+
response["warning"] = result.error
|
|
81
|
+
|
|
82
|
+
if result.has_memories:
|
|
83
|
+
response["context_block"] = result.as_context()
|
|
84
|
+
|
|
85
|
+
return _json_response(response)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
HANDLERS: dict = {
|
|
89
|
+
"brain_first_check": _handle_brain_first_check,
|
|
90
|
+
}
|
|
@@ -8,11 +8,14 @@ Exposes two tools:
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import json as _json
|
|
11
|
+
import logging
|
|
11
12
|
|
|
12
13
|
from mcp.types import TextContent, Tool
|
|
13
14
|
|
|
14
15
|
from ._helpers import _error_response, _home, _json_response
|
|
15
16
|
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
16
19
|
TOOLS: list[Tool] = [
|
|
17
20
|
Tool(
|
|
18
21
|
name="capauth_status",
|
|
@@ -82,8 +85,8 @@ async def _handle_capauth_status(_args: dict) -> list[TextContent]:
|
|
|
82
85
|
if did_key_file.exists():
|
|
83
86
|
try:
|
|
84
87
|
result["did_key_file"] = did_key_file.read_text(encoding="utf-8").strip()
|
|
85
|
-
except Exception:
|
|
86
|
-
|
|
88
|
+
except Exception as exc:
|
|
89
|
+
logger.warning("Failed to read DID key file: %s", exc)
|
|
87
90
|
|
|
88
91
|
# Check identity file
|
|
89
92
|
identity_file = home / "identity" / "identity.json"
|
|
@@ -92,8 +95,8 @@ async def _handle_capauth_status(_args: dict) -> list[TextContent]:
|
|
|
92
95
|
ident = _json.loads(identity_file.read_text(encoding="utf-8"))
|
|
93
96
|
result["identity_name"] = ident.get("name")
|
|
94
97
|
result["identity_fingerprint"] = ident.get("fingerprint")
|
|
95
|
-
except Exception:
|
|
96
|
-
|
|
98
|
+
except Exception as exc:
|
|
99
|
+
logger.warning("Failed to read identity.json for capauth status: %s", exc)
|
|
97
100
|
|
|
98
101
|
return _json_response(result)
|
|
99
102
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""SKComms send/receive messaging tools."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ TOOLS: list[Tool] = [
|
|
|
10
10
|
Tool(
|
|
11
11
|
name="send_message",
|
|
12
12
|
description=(
|
|
13
|
-
"Send a message to another agent via
|
|
13
|
+
"Send a message to another agent via SKComms. "
|
|
14
14
|
"Routes through available transports (Syncthing, file)."
|
|
15
15
|
),
|
|
16
16
|
inputSchema={
|
|
@@ -36,7 +36,7 @@ TOOLS: list[Tool] = [
|
|
|
36
36
|
Tool(
|
|
37
37
|
name="check_inbox",
|
|
38
38
|
description=(
|
|
39
|
-
"Check for new incoming messages across all
|
|
39
|
+
"Check for new incoming messages across all SKComms transports. "
|
|
40
40
|
"Returns any unread message envelopes."
|
|
41
41
|
),
|
|
42
42
|
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
@@ -45,15 +45,15 @@ TOOLS: list[Tool] = [
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
async def _handle_send_message(args: dict) -> list[TextContent]:
|
|
48
|
-
"""Send a message via
|
|
48
|
+
"""Send a message via SKComms."""
|
|
49
49
|
recipient = args.get("recipient", "")
|
|
50
50
|
message = args.get("message", "")
|
|
51
51
|
if not recipient or not message:
|
|
52
52
|
return _error_response("recipient and message are required")
|
|
53
53
|
|
|
54
54
|
try:
|
|
55
|
-
from
|
|
56
|
-
comm =
|
|
55
|
+
from skcomms.core import SKComms
|
|
56
|
+
comm = SKComms.from_config()
|
|
57
57
|
report = comm.send(recipient, message)
|
|
58
58
|
return _json_response({
|
|
59
59
|
"sent": report.success,
|
|
@@ -68,7 +68,7 @@ async def _handle_send_message(args: dict) -> list[TextContent]:
|
|
|
68
68
|
],
|
|
69
69
|
})
|
|
70
70
|
except ImportError:
|
|
71
|
-
return _error_response("
|
|
71
|
+
return _error_response("SKComms not installed. Run: pip install skcomms")
|
|
72
72
|
except Exception as exc:
|
|
73
73
|
return _error_response(f"Send failed: {exc}")
|
|
74
74
|
|
|
@@ -76,8 +76,8 @@ async def _handle_send_message(args: dict) -> list[TextContent]:
|
|
|
76
76
|
async def _handle_check_inbox(_args: dict) -> list[TextContent]:
|
|
77
77
|
"""Check for incoming messages."""
|
|
78
78
|
try:
|
|
79
|
-
from
|
|
80
|
-
comm =
|
|
79
|
+
from skcomms.core import SKComms
|
|
80
|
+
comm = SKComms.from_config()
|
|
81
81
|
envelopes = comm.receive()
|
|
82
82
|
return _json_response([
|
|
83
83
|
{
|
|
@@ -93,7 +93,7 @@ async def _handle_check_inbox(_args: dict) -> list[TextContent]:
|
|
|
93
93
|
for e in envelopes
|
|
94
94
|
])
|
|
95
95
|
except ImportError:
|
|
96
|
-
return _error_response("
|
|
96
|
+
return _error_response("SKComms not installed. Run: pip install skcomms")
|
|
97
97
|
except Exception as exc:
|
|
98
98
|
return _error_response(f"Inbox check failed: {exc}")
|
|
99
99
|
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
6
|
+
|
|
5
7
|
from mcp.types import TextContent, Tool
|
|
6
8
|
|
|
7
9
|
from ._helpers import _error_response, _home, _json_response, _shared_root
|
|
8
10
|
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
9
13
|
TOOLS: list[Tool] = [
|
|
10
14
|
Tool(
|
|
11
15
|
name="coord_status",
|
|
@@ -149,8 +153,8 @@ async def _handle_coord_claim(args: dict) -> list[TextContent]:
|
|
|
149
153
|
try:
|
|
150
154
|
from .. import activity
|
|
151
155
|
activity.push("task.claimed", {"task_id": task_id, "agent": agent_name})
|
|
152
|
-
except Exception:
|
|
153
|
-
|
|
156
|
+
except Exception as exc:
|
|
157
|
+
logger.warning("Failed to push task.claimed activity for %s: %s", task_id, exc)
|
|
154
158
|
return _json_response({
|
|
155
159
|
"claimed": True,
|
|
156
160
|
"task_id": task_id,
|
|
@@ -175,8 +179,8 @@ async def _handle_coord_complete(args: dict) -> list[TextContent]:
|
|
|
175
179
|
try:
|
|
176
180
|
from .. import activity
|
|
177
181
|
activity.push("task.completed", {"task_id": task_id, "agent": agent_name})
|
|
178
|
-
except Exception:
|
|
179
|
-
|
|
182
|
+
except Exception as exc:
|
|
183
|
+
logger.warning("Failed to push task.completed activity for %s: %s", task_id, exc)
|
|
180
184
|
return _json_response({
|
|
181
185
|
"completed": True,
|
|
182
186
|
"task_id": task_id,
|
|
@@ -10,6 +10,7 @@ Exposes four tools:
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import json as _json
|
|
13
|
+
import logging
|
|
13
14
|
import os as _os
|
|
14
15
|
import socket as _socket
|
|
15
16
|
from pathlib import Path
|
|
@@ -18,6 +19,8 @@ from mcp.types import TextContent, Tool
|
|
|
18
19
|
|
|
19
20
|
from ._helpers import _error_response, _home, _json_response
|
|
20
21
|
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
21
24
|
TOOLS: list[Tool] = [
|
|
22
25
|
Tool(
|
|
23
26
|
name="did_show",
|
|
@@ -165,8 +168,8 @@ def _load_policy() -> dict:
|
|
|
165
168
|
if p.exists():
|
|
166
169
|
try:
|
|
167
170
|
return _json.loads(p.read_text(encoding="utf-8"))
|
|
168
|
-
except Exception:
|
|
169
|
-
|
|
171
|
+
except Exception as exc:
|
|
172
|
+
logger.warning("Failed to load DID policy from %s: %s", p, exc)
|
|
170
173
|
return dict(_POLICY_DEFAULT)
|
|
171
174
|
|
|
172
175
|
|
|
@@ -191,8 +194,8 @@ def _resolve_tailnet(tailnet_hostname: str, tailnet_name: str) -> tuple[str, str
|
|
|
191
194
|
if not tailnet_hostname:
|
|
192
195
|
try:
|
|
193
196
|
tailnet_hostname = _socket.gethostname()
|
|
194
|
-
except Exception:
|
|
195
|
-
|
|
197
|
+
except Exception as exc:
|
|
198
|
+
logger.warning("Failed to resolve hostname for tailnet DID: %s", exc)
|
|
196
199
|
if not tailnet_name:
|
|
197
200
|
tailnet_name = _os.environ.get("SKWORLD_TAILNET", "")
|
|
198
201
|
return tailnet_hostname, tailnet_name
|
|
@@ -297,8 +300,8 @@ async def _handle_did_verify_peer(args: dict) -> list[TextContent]:
|
|
|
297
300
|
_json.dumps(peer_data, indent=2, default=str),
|
|
298
301
|
encoding="utf-8",
|
|
299
302
|
)
|
|
300
|
-
except Exception:
|
|
301
|
-
|
|
303
|
+
except Exception as exc:
|
|
304
|
+
logger.warning("Failed to cache did:key back to peer file %s: %s", peer_file, exc)
|
|
302
305
|
|
|
303
306
|
return _json_response({
|
|
304
307
|
"name": name,
|
|
@@ -361,11 +364,11 @@ async def _handle_did_publish(args: dict) -> list[TextContent]:
|
|
|
361
364
|
except Exception as exc:
|
|
362
365
|
errors.append(f"{path}: {exc}")
|
|
363
366
|
|
|
364
|
-
|
|
367
|
+
skcomms_home = Path(_os.environ.get("SKCOMMS_HOME", str(Path.home() / ".skcomms")))
|
|
365
368
|
did_dir = _home() / "did"
|
|
366
369
|
|
|
367
370
|
# Tier 2 (mesh) — always written; used by Tailscale Serve
|
|
368
|
-
_write(
|
|
371
|
+
_write(skcomms_home / "well-known" / "did.json", _json.dumps(docs[DIDTier.WEB_MESH], indent=2))
|
|
369
372
|
# Tier 1 (did:key) — always written; self-contained anchor
|
|
370
373
|
_write(did_dir / "key.json", _json.dumps(docs[DIDTier.KEY], indent=2))
|
|
371
374
|
_write(did_dir / "did_key.txt", gen._ctx.did_key_id)
|
|
@@ -382,7 +382,7 @@ async def _handle_gtd_capture(args: dict) -> list[TextContent]:
|
|
|
382
382
|
return _json_response({
|
|
383
383
|
"captured": True,
|
|
384
384
|
"id": item["id"],
|
|
385
|
-
"text": item
|
|
385
|
+
"text": item.get("text") or item.get("title") or "",
|
|
386
386
|
"source": item["source"],
|
|
387
387
|
"privacy": item["privacy"],
|
|
388
388
|
"context": item["context"],
|
|
@@ -498,7 +498,7 @@ async def _handle_gtd_clarify(args: dict) -> list[TextContent]:
|
|
|
498
498
|
return _json_response({
|
|
499
499
|
"clarified": True,
|
|
500
500
|
"id": item["id"],
|
|
501
|
-
"text": item
|
|
501
|
+
"text": item.get("text") or item.get("title") or "",
|
|
502
502
|
"destination": dest_name,
|
|
503
503
|
"status": item["status"],
|
|
504
504
|
"priority": item.get("priority"),
|
|
@@ -550,7 +550,7 @@ async def _handle_gtd_move(args: dict) -> list[TextContent]:
|
|
|
550
550
|
return _json_response({
|
|
551
551
|
"moved": True,
|
|
552
552
|
"id": item["id"],
|
|
553
|
-
"text": item
|
|
553
|
+
"text": item.get("text") or item.get("title") or "",
|
|
554
554
|
"from": source_list,
|
|
555
555
|
"to": dest_name,
|
|
556
556
|
"status": item["status"],
|
|
@@ -582,7 +582,7 @@ async def _handle_gtd_done(args: dict) -> list[TextContent]:
|
|
|
582
582
|
return _json_response({
|
|
583
583
|
"done": True,
|
|
584
584
|
"id": item["id"],
|
|
585
|
-
"text": item
|
|
585
|
+
"text": item.get("text") or item.get("title") or "",
|
|
586
586
|
"from": source_list,
|
|
587
587
|
"completed_at": item["completed_at"],
|
|
588
588
|
"archive_count": len(archive),
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
6
|
+
|
|
5
7
|
from mcp.types import TextContent, Tool
|
|
6
8
|
|
|
7
9
|
from ._helpers import _error_response, _home, _json_response
|
|
8
10
|
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
9
13
|
TOOLS: list[Tool] = [
|
|
10
14
|
Tool(
|
|
11
15
|
name="memory_store",
|
|
@@ -130,8 +134,8 @@ async def _handle_memory_store(args: dict) -> list[TextContent]:
|
|
|
130
134
|
"importance": entry.importance,
|
|
131
135
|
"tags": entry.tags,
|
|
132
136
|
})
|
|
133
|
-
except Exception:
|
|
134
|
-
|
|
137
|
+
except Exception as exc:
|
|
138
|
+
logger.warning("Failed to push memory.stored activity for %s: %s", entry.memory_id, exc)
|
|
135
139
|
return _json_response({
|
|
136
140
|
"memory_id": entry.memory_id,
|
|
137
141
|
"layer": entry.layer.value,
|
|
@@ -54,10 +54,17 @@ async def _handle_send_notification(args: dict) -> list[TextContent]:
|
|
|
54
54
|
if urgency not in {"low", "normal", "critical"}:
|
|
55
55
|
return _error_response("urgency must be one of: low, normal, critical")
|
|
56
56
|
|
|
57
|
+
from ..notifications import desktop_notifications_enabled
|
|
58
|
+
|
|
59
|
+
if not desktop_notifications_enabled():
|
|
60
|
+
timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
|
61
|
+
return _json_response({"sent": False, "suppressed": True, "timestamp": timestamp})
|
|
62
|
+
|
|
57
63
|
# Run notify-send in a subprocess (non-blocking).
|
|
58
64
|
proc = await asyncio.create_subprocess_exec(
|
|
59
65
|
"notify-send",
|
|
60
|
-
"--urgency",
|
|
66
|
+
"--urgency",
|
|
67
|
+
urgency,
|
|
61
68
|
title,
|
|
62
69
|
body,
|
|
63
70
|
stdout=asyncio.subprocess.DEVNULL,
|
|
@@ -71,16 +78,25 @@ async def _handle_send_notification(args: dict) -> list[TextContent]:
|
|
|
71
78
|
|
|
72
79
|
timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
|
73
80
|
|
|
74
|
-
# Log notification to
|
|
81
|
+
# Log notification to skcomms/notifications/ (not memory/).
|
|
75
82
|
try:
|
|
76
83
|
import json as _j
|
|
77
84
|
import uuid
|
|
85
|
+
|
|
78
86
|
home = _home()
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
from .. import active_agent_name
|
|
88
|
+
|
|
89
|
+
agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
|
|
90
|
+
notif_dir = home / "agents" / agent_name / "skcomms" / "notifications"
|
|
81
91
|
notif_dir.mkdir(parents=True, exist_ok=True)
|
|
82
|
-
entry = {
|
|
83
|
-
|
|
92
|
+
entry = {
|
|
93
|
+
"id": uuid.uuid4().hex[:12],
|
|
94
|
+
"type": "notification-sent",
|
|
95
|
+
"title": title,
|
|
96
|
+
"body": body,
|
|
97
|
+
"urgency": urgency,
|
|
98
|
+
"timestamp": timestamp,
|
|
99
|
+
}
|
|
84
100
|
(notif_dir / f"{entry['id']}.json").write_text(_j.dumps(entry, indent=2))
|
|
85
101
|
except Exception:
|
|
86
102
|
pass # notification log failure must not block the response
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""SKComms notification and status tools.
|
|
2
2
|
|
|
3
3
|
Exposes two tools:
|
|
4
|
-
comm_notify — Send a notification via
|
|
5
|
-
comm_status — Show
|
|
4
|
+
comm_notify — Send a notification via SKComms transports
|
|
5
|
+
comm_status — Show SKComms subsystem status
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
@@ -15,7 +15,7 @@ TOOLS: list[Tool] = [
|
|
|
15
15
|
Tool(
|
|
16
16
|
name="comm_notify",
|
|
17
17
|
description=(
|
|
18
|
-
"Send a notification message via
|
|
18
|
+
"Send a notification message via SKComms. Routes through "
|
|
19
19
|
"available transports (Syncthing, file, Tailscale). "
|
|
20
20
|
"Supports urgency levels for priority routing."
|
|
21
21
|
),
|
|
@@ -46,7 +46,7 @@ TOOLS: list[Tool] = [
|
|
|
46
46
|
Tool(
|
|
47
47
|
name="comm_status",
|
|
48
48
|
description=(
|
|
49
|
-
"Show
|
|
49
|
+
"Show SKComms subsystem status: installed version, "
|
|
50
50
|
"available transports, connection state, and recent "
|
|
51
51
|
"delivery statistics."
|
|
52
52
|
),
|
|
@@ -56,16 +56,16 @@ TOOLS: list[Tool] = [
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
async def _handle_comm_notify(args: dict) -> list[TextContent]:
|
|
59
|
-
"""Send a notification via
|
|
59
|
+
"""Send a notification via SKComms."""
|
|
60
60
|
recipient = args.get("recipient", "")
|
|
61
61
|
message = args.get("message", "")
|
|
62
62
|
if not recipient or not message:
|
|
63
63
|
return _error_response("recipient and message are required")
|
|
64
64
|
|
|
65
65
|
try:
|
|
66
|
-
from
|
|
66
|
+
from skcomms.core import SKComms # type: ignore[import]
|
|
67
67
|
|
|
68
|
-
comm =
|
|
68
|
+
comm = SKComms.from_config()
|
|
69
69
|
report = comm.send(recipient, message)
|
|
70
70
|
return _json_response({
|
|
71
71
|
"sent": report.success,
|
|
@@ -81,29 +81,29 @@ async def _handle_comm_notify(args: dict) -> list[TextContent]:
|
|
|
81
81
|
],
|
|
82
82
|
})
|
|
83
83
|
except ImportError:
|
|
84
|
-
return _error_response("
|
|
84
|
+
return _error_response("SKComms not installed. Run: pip install skcomms")
|
|
85
85
|
except Exception as exc:
|
|
86
86
|
return _error_response(f"Notification send failed: {exc}")
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
async def _handle_comm_status(_args: dict) -> list[TextContent]:
|
|
90
|
-
"""Show
|
|
90
|
+
"""Show SKComms subsystem status."""
|
|
91
91
|
result: dict = {}
|
|
92
92
|
|
|
93
93
|
try:
|
|
94
|
-
import
|
|
94
|
+
import skcomms # type: ignore[import]
|
|
95
95
|
|
|
96
96
|
result["installed"] = True
|
|
97
|
-
result["version"] = getattr(
|
|
97
|
+
result["version"] = getattr(skcomms, "__version__", "unknown")
|
|
98
98
|
except ImportError:
|
|
99
99
|
result["installed"] = False
|
|
100
100
|
result["version"] = None
|
|
101
101
|
return _json_response(result)
|
|
102
102
|
|
|
103
103
|
try:
|
|
104
|
-
from
|
|
104
|
+
from skcomms.core import SKComms # type: ignore[import]
|
|
105
105
|
|
|
106
|
-
comm =
|
|
106
|
+
comm = SKComms.from_config()
|
|
107
107
|
result["transports"] = [
|
|
108
108
|
t.name for t in getattr(comm, "transports", [])
|
|
109
109
|
]
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
6
|
+
|
|
5
7
|
from mcp.types import TextContent, Tool
|
|
6
8
|
|
|
7
9
|
from ._helpers import _error_response, _home, _json_response, _text_response
|
|
8
10
|
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
9
13
|
TOOLS: list[Tool] = [
|
|
10
14
|
Tool(
|
|
11
15
|
name="soul_list",
|
|
@@ -179,6 +183,8 @@ async def _handle_ritual(args: dict) -> list[TextContent]:
|
|
|
179
183
|
"journal_entries": result.journal_entries,
|
|
180
184
|
"germination_prompts": result.germination_prompts,
|
|
181
185
|
"strongest_memories": result.strongest_memories,
|
|
186
|
+
"song_anchors_loaded": result.song_anchors_loaded,
|
|
187
|
+
"song_anchor_ids": result.song_anchor_ids,
|
|
182
188
|
"context_prompt": result.context_prompt,
|
|
183
189
|
})
|
|
184
190
|
except ImportError:
|
|
@@ -250,8 +256,8 @@ async def _handle_soul_list(args: dict) -> list[TextContent]:
|
|
|
250
256
|
"source": "installed",
|
|
251
257
|
"active": name == state.active_soul,
|
|
252
258
|
})
|
|
253
|
-
except Exception:
|
|
254
|
-
|
|
259
|
+
except Exception as exc:
|
|
260
|
+
logger.warning("Failed to list installed soul blueprints: %s", exc)
|
|
255
261
|
|
|
256
262
|
# 2) Blueprints repo
|
|
257
263
|
blueprints_repo = Path.home() / "clawd" / "soul-blueprints" / "blueprints"
|
|
@@ -238,8 +238,8 @@ class MDNSDiscovery:
|
|
|
238
238
|
agent_name,
|
|
239
239
|
)
|
|
240
240
|
return
|
|
241
|
-
except Exception:
|
|
242
|
-
|
|
241
|
+
except Exception as exc:
|
|
242
|
+
logger.warning("Failed to read existing mDNS heartbeat for %s: %s", agent_name, exc)
|
|
243
243
|
|
|
244
244
|
heartbeat = {
|
|
245
245
|
"agent_name": agent_name,
|
|
@@ -59,7 +59,7 @@ _TAG_PATTERNS: list[tuple[re.Pattern, str]] = [
|
|
|
59
59
|
(re.compile(r"\bcapauth\b", re.I), "capauth"),
|
|
60
60
|
(re.compile(r"\bskcapstone\b", re.I), "skcapstone"),
|
|
61
61
|
(re.compile(r"\bskmemory\b", re.I), "skmemory"),
|
|
62
|
-
(re.compile(r"\
|
|
62
|
+
(re.compile(r"\bskcomms\b", re.I), "skcomms"),
|
|
63
63
|
(re.compile(r"\bskchat\b", re.I), "skchat"),
|
|
64
64
|
(re.compile(r"\bsyncthing\b", re.I), "syncthing"),
|
|
65
65
|
(re.compile(r"\bMCP\b", re.I), "mcp"),
|
|
@@ -25,6 +25,7 @@ from datetime import datetime, timezone
|
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
from typing import Optional
|
|
27
27
|
|
|
28
|
+
from . import active_agent_name
|
|
28
29
|
from .models import MemoryEntry, MemoryLayer, MemoryState, PillarStatus
|
|
29
30
|
|
|
30
31
|
logger = logging.getLogger("skcapstone.memory")
|
|
@@ -48,9 +49,15 @@ def _get_unified():
|
|
|
48
49
|
|
|
49
50
|
def _memory_dir(home: Path) -> Path:
|
|
50
51
|
"""Resolve the memory directory, creating it if needed."""
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
# Accept either the shared root (~/.skcapstone) or an agent home
|
|
53
|
+
# (~/.skcapstone/agents/<agent>) and resolve to the active memory dir.
|
|
54
|
+
agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
|
|
55
|
+
if home.parent.name == "agents":
|
|
56
|
+
mem = home / "memory"
|
|
57
|
+
elif agent_name:
|
|
58
|
+
mem = home / "agents" / agent_name / "memory"
|
|
59
|
+
else:
|
|
60
|
+
mem = home / "memory"
|
|
54
61
|
mem.mkdir(parents=True, exist_ok=True)
|
|
55
62
|
for layer in MemoryLayer:
|
|
56
63
|
(mem / layer.value).mkdir(parents=True, exist_ok=True)
|