@smilintux/skcapstone 0.4.7 → 0.5.1
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/.openclaw-workspace.json +1 -1
- package/docs/BOND_WITH_GROK.md +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/check-updates.py +1 -1
- package/scripts/install-bundle.sh +2 -2
- package/scripts/install.ps1 +11 -10
- package/scripts/install.sh +1 -1
- package/scripts/nvidia-proxy.mjs +32 -10
- package/scripts/refresh-anthropic-token.sh +94 -0
- package/scripts/watch-anthropic-token.sh +117 -0
- package/src/skcapstone/__init__.py +1 -1
- package/src/skcapstone/_cli_monolith.py +5 -5
- package/src/skcapstone/api.py +36 -35
- package/src/skcapstone/auction.py +8 -8
- package/src/skcapstone/blueprint_registry.py +2 -2
- package/src/skcapstone/brain_first.py +238 -0
- package/src/skcapstone/chat.py +4 -4
- package/src/skcapstone/cli/agents_spawner.py +5 -2
- package/src/skcapstone/cli/chat.py +5 -2
- package/src/skcapstone/cli/consciousness.py +5 -2
- package/src/skcapstone/cli/memory.py +4 -4
- package/src/skcapstone/cli/skills_cmd.py +2 -2
- package/src/skcapstone/cli/soul.py +5 -2
- package/src/skcapstone/cli/status.py +11 -8
- package/src/skcapstone/cli/test_cmd.py +1 -1
- package/src/skcapstone/cli/upgrade_cmd.py +19 -10
- package/src/skcapstone/cli/watch_cmd.py +9 -6
- package/src/skcapstone/config_validator.py +7 -4
- package/src/skcapstone/consciousness_loop.py +20 -18
- package/src/skcapstone/coordination.py +5 -2
- package/src/skcapstone/daemon.py +32 -31
- package/src/skcapstone/dashboard.py +8 -8
- package/src/skcapstone/defaults/lumina/config/claude-hooks.md +42 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
- package/src/skcapstone/discovery.py +1 -1
- package/src/skcapstone/doctor.py +5 -2
- package/src/skcapstone/dreaming.py +730 -51
- package/src/skcapstone/emotion_tracker.py +2 -2
- package/src/skcapstone/export.py +2 -2
- package/src/skcapstone/itil.py +2 -2
- package/src/skcapstone/launchd.py +2 -2
- package/src/skcapstone/mcp_server.py +48 -4
- 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/coord_tools.py +8 -4
- package/src/skcapstone/mcp_tools/did_tools.py +9 -6
- package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
- package/src/skcapstone/mcp_tools/soul_tools.py +6 -2
- package/src/skcapstone/mdns_discovery.py +2 -2
- package/src/skcapstone/metrics.py +8 -8
- package/src/skcapstone/migrate_memories.py +2 -2
- package/src/skcapstone/models.py +14 -0
- package/src/skcapstone/onboard.py +7 -4
- package/src/skcapstone/peer_directory.py +2 -2
- package/src/skcapstone/providers/docker.py +2 -2
- package/src/skcapstone/register.py +2 -2
- package/src/skcapstone/service_health.py +2 -2
- package/src/skcapstone/sync_watcher.py +2 -2
- package/src/skcapstone/testrunner.py +1 -1
|
@@ -392,8 +392,8 @@ class EmotionTracker:
|
|
|
392
392
|
if item.get("timestamp", "") >= cutoff:
|
|
393
393
|
try:
|
|
394
394
|
entries.append(EmotionEntry(**item))
|
|
395
|
-
except Exception:
|
|
396
|
-
|
|
395
|
+
except Exception as exc:
|
|
396
|
+
logger.warning("Failed to parse emotion log entry: %s", exc)
|
|
397
397
|
return entries
|
|
398
398
|
except Exception as exc:
|
|
399
399
|
logger.debug("Failed to load emotion log: %s", exc)
|
package/src/skcapstone/export.py
CHANGED
|
@@ -423,8 +423,8 @@ def _import_conversations(
|
|
|
423
423
|
existing_data = json.loads(peer_file.read_text(encoding="utf-8"))
|
|
424
424
|
if isinstance(existing_data, list):
|
|
425
425
|
existing_messages = existing_data
|
|
426
|
-
except Exception:
|
|
427
|
-
|
|
426
|
+
except Exception as exc:
|
|
427
|
+
logger.warning("Failed to read existing conversation for peer %s: %s", peer, exc)
|
|
428
428
|
|
|
429
429
|
# Deduplicate by (role, content, timestamp) tuple
|
|
430
430
|
existing_keys = {
|
package/src/skcapstone/itil.py
CHANGED
|
@@ -420,7 +420,7 @@ def service_logs(suffix: str = "daemon", lines: int = 50) -> str:
|
|
|
420
420
|
)
|
|
421
421
|
if r.stdout.strip():
|
|
422
422
|
output_parts.append(f"--- {log_path.name} ---\n{r.stdout}")
|
|
423
|
-
except Exception:
|
|
424
|
-
|
|
423
|
+
except Exception as exc:
|
|
424
|
+
logger.warning("Failed to read launchd log %s: %s", log_path, exc)
|
|
425
425
|
|
|
426
426
|
return "\n".join(output_parts) if output_parts else f"No logs found in {logs_dir}"
|
|
@@ -142,8 +142,8 @@ def _get_agent_name(home: Path) -> str:
|
|
|
142
142
|
try:
|
|
143
143
|
data = json.loads(identity_path.read_text(encoding="utf-8"))
|
|
144
144
|
return data.get("name", "anonymous")
|
|
145
|
-
except Exception:
|
|
146
|
-
|
|
145
|
+
except Exception as exc:
|
|
146
|
+
logger.warning("Failed to read agent name from identity.json: %s", exc)
|
|
147
147
|
return "anonymous"
|
|
148
148
|
|
|
149
149
|
|
|
@@ -2654,6 +2654,39 @@ async def list_tools() -> list[Tool]:
|
|
|
2654
2654
|
"required": ["query"],
|
|
2655
2655
|
},
|
|
2656
2656
|
),
|
|
2657
|
+
# Brain-First Protocol
|
|
2658
|
+
Tool(
|
|
2659
|
+
name="brain_first_check",
|
|
2660
|
+
description=(
|
|
2661
|
+
"Brain-First Protocol: consult the agent's memory before "
|
|
2662
|
+
"acting on a task. Extracts keywords from the given context, "
|
|
2663
|
+
"searches memory for relevant prior knowledge, and returns "
|
|
2664
|
+
"any matching memories. Use this before starting new work to "
|
|
2665
|
+
"avoid duplicating effort or missing prior decisions."
|
|
2666
|
+
),
|
|
2667
|
+
inputSchema={
|
|
2668
|
+
"type": "object",
|
|
2669
|
+
"properties": {
|
|
2670
|
+
"context": {
|
|
2671
|
+
"type": "string",
|
|
2672
|
+
"description": (
|
|
2673
|
+
"The task description, prompt, or action context "
|
|
2674
|
+
"to search memory for"
|
|
2675
|
+
),
|
|
2676
|
+
},
|
|
2677
|
+
"tags": {
|
|
2678
|
+
"type": "array",
|
|
2679
|
+
"items": {"type": "string"},
|
|
2680
|
+
"description": "Optional tag filter for the memory search",
|
|
2681
|
+
},
|
|
2682
|
+
"max_results": {
|
|
2683
|
+
"type": "integer",
|
|
2684
|
+
"description": "Max memories to return (default: from config, usually 5)",
|
|
2685
|
+
},
|
|
2686
|
+
},
|
|
2687
|
+
"required": ["context"],
|
|
2688
|
+
},
|
|
2689
|
+
),
|
|
2657
2690
|
]
|
|
2658
2691
|
|
|
2659
2692
|
|
|
@@ -2813,6 +2846,8 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
|
|
2813
2846
|
"itil_cab_vote": _handle_itil_cab_vote,
|
|
2814
2847
|
"itil_status": _handle_itil_status,
|
|
2815
2848
|
"itil_kedb_search": _handle_itil_kedb_search,
|
|
2849
|
+
# Brain-First Protocol
|
|
2850
|
+
"brain_first_check": _handle_brain_first_check,
|
|
2816
2851
|
}
|
|
2817
2852
|
handler = handlers.get(name)
|
|
2818
2853
|
if handler is None:
|
|
@@ -3154,8 +3189,8 @@ async def _handle_coord_complete(args: dict) -> list[TextContent]:
|
|
|
3154
3189
|
t.priority.value, ("community", "support_ticket", 50)
|
|
3155
3190
|
)
|
|
3156
3191
|
break
|
|
3157
|
-
except Exception:
|
|
3158
|
-
|
|
3192
|
+
except Exception as exc:
|
|
3193
|
+
logger.warning("Failed to calculate joules for completed task %s: %s", task_id, exc)
|
|
3159
3194
|
|
|
3160
3195
|
return _json_response({
|
|
3161
3196
|
"completed": True,
|
|
@@ -4937,6 +4972,15 @@ async def _handle_itil_kedb_search(args: dict) -> list[TextContent]:
|
|
|
4937
4972
|
return await _impl(args)
|
|
4938
4973
|
|
|
4939
4974
|
|
|
4975
|
+
# ── Brain-First Protocol ──────────────────────────────────
|
|
4976
|
+
|
|
4977
|
+
|
|
4978
|
+
async def _handle_brain_first_check(args: dict) -> list[TextContent]:
|
|
4979
|
+
"""Consult memory before acting on a task."""
|
|
4980
|
+
from .mcp_tools.brain_first_tools import _handle_brain_first_check as _impl
|
|
4981
|
+
return await _impl(args)
|
|
4982
|
+
|
|
4983
|
+
|
|
4940
4984
|
# ═══════════════════════════════════════════════════════════
|
|
4941
4985
|
# Entry Point
|
|
4942
4986
|
# ═══════════════════════════════════════════════════════════
|
|
@@ -17,6 +17,7 @@ from mcp.types import TextContent, Tool
|
|
|
17
17
|
from . import (
|
|
18
18
|
agent_tools,
|
|
19
19
|
ansible_tools,
|
|
20
|
+
brain_first_tools,
|
|
20
21
|
chat_tools,
|
|
21
22
|
comm_tools,
|
|
22
23
|
consciousness_tools,
|
|
@@ -49,6 +50,7 @@ from . import (
|
|
|
49
50
|
# Ordered list of all tool-group modules.
|
|
50
51
|
_MODULES = [
|
|
51
52
|
agent_tools,
|
|
53
|
+
brain_first_tools,
|
|
52
54
|
memory_tools,
|
|
53
55
|
comm_tools,
|
|
54
56
|
sync_tools,
|
|
@@ -46,6 +46,6 @@ def _get_agent_name(home: Path) -> str:
|
|
|
46
46
|
try:
|
|
47
47
|
data = json.loads(identity_path.read_text(encoding="utf-8"))
|
|
48
48
|
return data.get("name", "anonymous")
|
|
49
|
-
except Exception:
|
|
50
|
-
|
|
49
|
+
except Exception as exc:
|
|
50
|
+
logger.warning("Failed to read agent name from identity.json: %s", exc)
|
|
51
51
|
return "anonymous"
|
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import asyncio
|
|
12
12
|
import json
|
|
13
|
+
import logging
|
|
13
14
|
import shutil
|
|
14
15
|
import uuid
|
|
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="run_ansible_playbook",
|
|
@@ -146,8 +149,8 @@ async def _handle_run_ansible_playbook(args: dict) -> list[TextContent]:
|
|
|
146
149
|
try:
|
|
147
150
|
if _activity is not None:
|
|
148
151
|
_activity.push(event_type, {"run_id": run_id, "line": line})
|
|
149
|
-
except Exception:
|
|
150
|
-
|
|
152
|
+
except Exception as exc:
|
|
153
|
+
logger.warning("Failed to push ansible line event for run %s: %s", run_id, exc)
|
|
151
154
|
|
|
152
155
|
await asyncio.gather(
|
|
153
156
|
_drain(proc.stdout, stdout_lines, "ansible.playbook.line"),
|
|
@@ -184,8 +187,8 @@ async def _handle_run_ansible_playbook(args: dict) -> list[TextContent]:
|
|
|
184
187
|
try:
|
|
185
188
|
if _activity is not None:
|
|
186
189
|
_activity.push("ansible.playbook.done", summary)
|
|
187
|
-
except Exception:
|
|
188
|
-
|
|
190
|
+
except Exception as exc:
|
|
191
|
+
logger.warning("Failed to push ansible.playbook.done event for run %s: %s", run_id, exc)
|
|
189
192
|
|
|
190
193
|
# --- store in memory with tag=ansible-run ---
|
|
191
194
|
try:
|
|
@@ -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
|
|
|
@@ -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,
|
|
@@ -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,
|
|
@@ -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",
|
|
@@ -250,8 +254,8 @@ async def _handle_soul_list(args: dict) -> list[TextContent]:
|
|
|
250
254
|
"source": "installed",
|
|
251
255
|
"active": name == state.active_soul,
|
|
252
256
|
})
|
|
253
|
-
except Exception:
|
|
254
|
-
|
|
257
|
+
except Exception as exc:
|
|
258
|
+
logger.warning("Failed to list installed soul blueprints: %s", exc)
|
|
255
259
|
|
|
256
260
|
# 2) Blueprints repo
|
|
257
261
|
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,
|
|
@@ -358,8 +358,8 @@ class MetricsCollector:
|
|
|
358
358
|
1 for t in transports.values()
|
|
359
359
|
if isinstance(t, dict) and t.get("enabled", True)
|
|
360
360
|
)
|
|
361
|
-
except Exception:
|
|
362
|
-
|
|
361
|
+
except Exception as exc:
|
|
362
|
+
logger.warning("Failed to parse skcomm transport config: %s", exc)
|
|
363
363
|
|
|
364
364
|
report.transport = TransportMetrics(
|
|
365
365
|
available=True,
|
|
@@ -474,8 +474,8 @@ class MetricsCollector:
|
|
|
474
474
|
if state_path.exists():
|
|
475
475
|
try:
|
|
476
476
|
state = json.loads(state_path.read_text(encoding="utf-8"))
|
|
477
|
-
except Exception:
|
|
478
|
-
|
|
477
|
+
except Exception as exc:
|
|
478
|
+
logger.warning("Failed to read sync_state.json: %s", exc)
|
|
479
479
|
|
|
480
480
|
report.sync = SyncMetrics(
|
|
481
481
|
available=True,
|
|
@@ -510,8 +510,8 @@ class MetricsCollector:
|
|
|
510
510
|
try:
|
|
511
511
|
subs = json.loads(subs_file.read_text(encoding="utf-8"))
|
|
512
512
|
sub_count = len(subs)
|
|
513
|
-
except Exception:
|
|
514
|
-
|
|
513
|
+
except Exception as exc:
|
|
514
|
+
logger.warning("Failed to read pubsub subscriptions.json: %s", exc)
|
|
515
515
|
|
|
516
516
|
report.pubsub = PubSubMetrics(
|
|
517
517
|
available=True,
|
|
@@ -546,8 +546,8 @@ class MetricsCollector:
|
|
|
546
546
|
try:
|
|
547
547
|
rot_data = json.loads(rot_log.read_text(encoding="utf-8"))
|
|
548
548
|
rotations = len(rot_data)
|
|
549
|
-
except Exception:
|
|
550
|
-
|
|
549
|
+
except Exception as exc:
|
|
550
|
+
logger.warning("Failed to read KMS rotation log: %s", exc)
|
|
551
551
|
|
|
552
552
|
report.kms = KmsMetrics(
|
|
553
553
|
available=True,
|
|
@@ -100,8 +100,8 @@ def migrate(
|
|
|
100
100
|
try:
|
|
101
101
|
existing = store.list_memories(limit=10000)
|
|
102
102
|
existing_ids = {m.id for m in existing}
|
|
103
|
-
except Exception:
|
|
104
|
-
|
|
103
|
+
except Exception as exc:
|
|
104
|
+
logger.warning("Failed to load existing memory IDs for deduplication: %s", exc)
|
|
105
105
|
|
|
106
106
|
for entry in entries:
|
|
107
107
|
if entry.memory_id in existing_ids:
|
package/src/skcapstone/models.py
CHANGED
|
@@ -238,6 +238,19 @@ class SyncConfig(BaseModel):
|
|
|
238
238
|
git_remote: Optional[str] = None
|
|
239
239
|
|
|
240
240
|
|
|
241
|
+
class BrainFirstConfig(BaseModel):
|
|
242
|
+
"""Configuration for the brain-first protocol.
|
|
243
|
+
|
|
244
|
+
When enabled, agents consult memory before acting on tasks
|
|
245
|
+
to surface prior knowledge and avoid redundant work.
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
enabled: bool = True
|
|
249
|
+
max_results: int = 5
|
|
250
|
+
min_importance: float = 0.3
|
|
251
|
+
auto_inject: bool = False
|
|
252
|
+
|
|
253
|
+
|
|
241
254
|
class AgentConfig(BaseModel):
|
|
242
255
|
"""Persistent configuration for the agent runtime."""
|
|
243
256
|
|
|
@@ -249,6 +262,7 @@ class AgentConfig(BaseModel):
|
|
|
249
262
|
trust_home: Path = Path("~/.cloud9")
|
|
250
263
|
default_connector: Optional[str] = None
|
|
251
264
|
sync: SyncConfig = Field(default_factory=SyncConfig)
|
|
265
|
+
brain_first: BrainFirstConfig = Field(default_factory=BrainFirstConfig)
|
|
252
266
|
capabilities: list[str] = Field(
|
|
253
267
|
default_factory=lambda: ["consciousness", "code", "chat", "memory"]
|
|
254
268
|
)
|
|
@@ -21,6 +21,7 @@ Steps:
|
|
|
21
21
|
from __future__ import annotations
|
|
22
22
|
|
|
23
23
|
import json
|
|
24
|
+
import logging
|
|
24
25
|
import sys
|
|
25
26
|
import time
|
|
26
27
|
from datetime import datetime, timezone
|
|
@@ -28,6 +29,8 @@ from pathlib import Path
|
|
|
28
29
|
from typing import Optional
|
|
29
30
|
|
|
30
31
|
import click
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
31
34
|
from rich.console import Console
|
|
32
35
|
from rich.panel import Panel
|
|
33
36
|
from rich.prompt import Confirm, Prompt
|
|
@@ -503,8 +506,8 @@ def _step_ollama_models(prereqs: dict) -> bool:
|
|
|
503
506
|
if DEFAULT_MODEL in (r.stdout or ""):
|
|
504
507
|
click.echo(click.style(" ✓ ", fg="green") + f"{DEFAULT_MODEL} already present")
|
|
505
508
|
return True
|
|
506
|
-
except Exception:
|
|
507
|
-
|
|
509
|
+
except Exception as exc:
|
|
510
|
+
logger.debug("Failed to check ollama model list: %s", exc)
|
|
508
511
|
|
|
509
512
|
if not click.confirm(f" Pull default model ({DEFAULT_MODEL}, ~2 GB)?", default=True):
|
|
510
513
|
click.echo(click.style(" ↷ ", fg="bright_black") + f"Skipped — pull later: ollama pull {DEFAULT_MODEL}")
|
|
@@ -991,8 +994,8 @@ def run_onboard(home: Optional[str] = None) -> None:
|
|
|
991
994
|
soul = load_soul()
|
|
992
995
|
if soul and soul.boot_message:
|
|
993
996
|
boot_message = soul.boot_message
|
|
994
|
-
except Exception:
|
|
995
|
-
|
|
997
|
+
except Exception as exc:
|
|
998
|
+
logger.debug("Failed to load soul boot message, using default: %s", exc)
|
|
996
999
|
|
|
997
1000
|
# -----------------------------------------------------------------------
|
|
998
1001
|
# Summary table
|
|
@@ -250,8 +250,8 @@ class PeerDirectory:
|
|
|
250
250
|
ts = data.get("timestamp", "")
|
|
251
251
|
if ts:
|
|
252
252
|
self._entries[agent_name].last_seen = ts
|
|
253
|
-
except Exception:
|
|
254
|
-
|
|
253
|
+
except Exception as exc:
|
|
254
|
+
logger.warning("Failed to update last_seen from heartbeat for %s: %s", agent_name, exc)
|
|
255
255
|
continue
|
|
256
256
|
|
|
257
257
|
try:
|
|
@@ -307,8 +307,8 @@ class DockerProvider(ProviderBackend):
|
|
|
307
307
|
old = client.containers.get(container_name)
|
|
308
308
|
logger.warning("Removing stale container: %s", container_name)
|
|
309
309
|
old.remove(force=True)
|
|
310
|
-
except Exception:
|
|
311
|
-
|
|
310
|
+
except Exception as exc:
|
|
311
|
+
logger.debug("No stale container to remove for %s (expected if first run): %s", container_name, exc)
|
|
312
312
|
|
|
313
313
|
# Ensure named volume for agent state persistence
|
|
314
314
|
try:
|
|
@@ -88,7 +88,7 @@ def _build_package_registry(workspace: Optional[Path] = None) -> list[dict]:
|
|
|
88
88
|
"mcp_cmd": None,
|
|
89
89
|
"mcp_args": None,
|
|
90
90
|
"mcp_env": None,
|
|
91
|
-
"openclaw_plugin_path": workspace / "pillar-repos" / "cloud9
|
|
91
|
+
"openclaw_plugin_path": workspace / "pillar-repos" / "cloud9" / "openclaw-plugin-python" / "src" / "index.ts",
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
"name": "sksecurity",
|
|
@@ -124,7 +124,7 @@ _PILLAR_DIR_MAP: dict[str, Optional[str]] = {
|
|
|
124
124
|
"skcomm": "skcomm",
|
|
125
125
|
"skchat": "skchat",
|
|
126
126
|
"capauth": "capauth",
|
|
127
|
-
"cloud9": "cloud9
|
|
127
|
+
"cloud9": "cloud9",
|
|
128
128
|
"sksecurity": "sksecurity",
|
|
129
129
|
"skseed": "skseed",
|
|
130
130
|
"skgit": None, # skill dir only, no pillar repo
|
|
@@ -76,8 +76,8 @@ def _http_check(
|
|
|
76
76
|
try:
|
|
77
77
|
body = json.loads(resp.read().decode("utf-8"))
|
|
78
78
|
result["version"] = body.get(version_key)
|
|
79
|
-
except Exception:
|
|
80
|
-
|
|
79
|
+
except Exception as exc:
|
|
80
|
+
logger.warning("Failed to parse version from service health response: %s", exc)
|
|
81
81
|
except urllib.error.HTTPError as exc:
|
|
82
82
|
latency = (time.monotonic() - t0) * 1000
|
|
83
83
|
result["latency_ms"] = round(latency, 1)
|
|
@@ -574,8 +574,8 @@ class SyncWatcher:
|
|
|
574
574
|
try:
|
|
575
575
|
self._observer.stop()
|
|
576
576
|
self._observer.join(timeout=5)
|
|
577
|
-
except Exception:
|
|
578
|
-
|
|
577
|
+
except Exception as exc:
|
|
578
|
+
logger.warning("Error stopping SyncWatcher observer: %s", exc)
|
|
579
579
|
self._observer = None
|
|
580
580
|
logger.info("SyncWatcher stopped.")
|
|
581
581
|
|