@smilintux/skcapstone 0.4.6 → 0.5.0
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/.github/workflows/publish.yml +8 -1
- package/docs/CUSTOM_AGENT.md +184 -0
- package/docs/GETTING_STARTED.md +3 -0
- package/launchd/com.skcapstone.daemon.plist +52 -0
- package/launchd/com.skcapstone.memory-compress.plist +45 -0
- package/launchd/com.skcapstone.skcomm-heartbeat.plist +33 -0
- package/launchd/com.skcapstone.skcomm-queue-drain.plist +34 -0
- package/launchd/install-launchd.sh +156 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/archive-sessions.sh +88 -0
- package/scripts/install.sh +39 -8
- package/scripts/notion-api.py +259 -0
- package/scripts/nvidia-proxy.mjs +878 -0
- package/scripts/proxy-monitor.sh +89 -0
- package/scripts/refresh-anthropic-token.sh +94 -0
- package/scripts/skgateway.mjs +856 -0
- package/scripts/telegram-catchup-all.sh +136 -0
- package/scripts/watch-anthropic-token.sh +117 -0
- package/src/skcapstone/__init__.py +1 -1
- package/src/skcapstone/_cli_monolith.py +4 -4
- 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/blueprints/builtins/itil-operations.yaml +40 -0
- package/src/skcapstone/brain_first.py +238 -0
- package/src/skcapstone/chat.py +4 -4
- package/src/skcapstone/cli/__init__.py +2 -0
- 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/daemon.py +116 -41
- package/src/skcapstone/cli/itil.py +434 -0
- 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/upgrade_cmd.py +7 -4
- package/src/skcapstone/cli/watch_cmd.py +9 -6
- package/src/skcapstone/config_validator.py +7 -4
- package/src/skcapstone/consciousness_config.py +27 -0
- package/src/skcapstone/consciousness_loop.py +20 -18
- package/src/skcapstone/coordination.py +6 -2
- package/src/skcapstone/daemon.py +51 -42
- package/src/skcapstone/dashboard.py +8 -8
- package/src/skcapstone/defaults/lumina/config/claude-hooks.md +42 -0
- package/src/skcapstone/doctor.py +5 -2
- package/src/skcapstone/dreaming.py +1440 -0
- package/src/skcapstone/emotion_tracker.py +2 -2
- package/src/skcapstone/export.py +2 -2
- package/src/skcapstone/fuse_mount.py +21 -13
- package/src/skcapstone/heartbeat.py +33 -29
- package/src/skcapstone/itil.py +1104 -0
- package/src/skcapstone/launchd.py +426 -0
- package/src/skcapstone/mcp_server.py +306 -4
- package/src/skcapstone/mcp_tools/__init__.py +4 -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/gtd_tools.py +1 -1
- package/src/skcapstone/mcp_tools/itil_tools.py +657 -0
- 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 +137 -14
- package/src/skcapstone/peer_directory.py +2 -2
- package/src/skcapstone/providers/docker.py +2 -2
- package/src/skcapstone/scheduled_tasks.py +107 -0
- package/src/skcapstone/service_health.py +83 -4
- package/src/skcapstone/sync_watcher.py +2 -2
- package/src/skcapstone/systemd.py +17 -0
|
@@ -55,6 +55,16 @@ Tools:
|
|
|
55
55
|
kms_list_keys — List all KMS keys
|
|
56
56
|
kms_rotate — Rotate a KMS key
|
|
57
57
|
model_route — Route task to optimal model tier/name
|
|
58
|
+
itil_incident_create — Create ITIL incident
|
|
59
|
+
itil_incident_update — Update incident status/severity
|
|
60
|
+
itil_incident_list — List/filter incidents
|
|
61
|
+
itil_problem_create — Create problem record
|
|
62
|
+
itil_problem_update — Update problem/root cause
|
|
63
|
+
itil_change_propose — Propose change (RFC)
|
|
64
|
+
itil_change_update — Update change status
|
|
65
|
+
itil_cab_vote — Submit CAB vote
|
|
66
|
+
itil_status — ITIL dashboard
|
|
67
|
+
itil_kedb_search — Search Known Error Database
|
|
58
68
|
|
|
59
69
|
Invocation (all equivalent):
|
|
60
70
|
skcapstone mcp serve # CLI entry point
|
|
@@ -132,8 +142,8 @@ def _get_agent_name(home: Path) -> str:
|
|
|
132
142
|
try:
|
|
133
143
|
data = json.loads(identity_path.read_text(encoding="utf-8"))
|
|
134
144
|
return data.get("name", "anonymous")
|
|
135
|
-
except Exception:
|
|
136
|
-
|
|
145
|
+
except Exception as exc:
|
|
146
|
+
logger.warning("Failed to read agent name from identity.json: %s", exc)
|
|
137
147
|
return "anonymous"
|
|
138
148
|
|
|
139
149
|
|
|
@@ -2470,6 +2480,213 @@ async def list_tools() -> list[Tool]:
|
|
|
2470
2480
|
"required": ["name"],
|
|
2471
2481
|
},
|
|
2472
2482
|
),
|
|
2483
|
+
# ── ITIL Service Management ─────────────────────────────
|
|
2484
|
+
Tool(
|
|
2485
|
+
name="itil_incident_create",
|
|
2486
|
+
description=(
|
|
2487
|
+
"Create a new ITIL incident for a service disruption. "
|
|
2488
|
+
"Auto-creates a linked GTD item (next-action for sev1/sev2, inbox for sev3/sev4)."
|
|
2489
|
+
),
|
|
2490
|
+
inputSchema={
|
|
2491
|
+
"type": "object",
|
|
2492
|
+
"properties": {
|
|
2493
|
+
"title": {"type": "string", "description": "Brief description of the incident"},
|
|
2494
|
+
"severity": {"type": "string", "enum": ["sev1", "sev2", "sev3", "sev4"], "description": "Severity level (default: sev3)"},
|
|
2495
|
+
"source": {"type": "string", "enum": ["service_health", "dreaming", "manual", "daemon_error", "heartbeat"], "description": "Detection source (default: manual)"},
|
|
2496
|
+
"affected_services": {"type": "array", "items": {"type": "string"}, "description": "List of affected service names"},
|
|
2497
|
+
"impact": {"type": "string", "description": "Business impact description"},
|
|
2498
|
+
"managed_by": {"type": "string", "description": "Agent responsible for managing this incident"},
|
|
2499
|
+
"tags": {"type": "array", "items": {"type": "string"}, "description": "Tags for categorization"},
|
|
2500
|
+
},
|
|
2501
|
+
"required": ["title"],
|
|
2502
|
+
},
|
|
2503
|
+
),
|
|
2504
|
+
Tool(
|
|
2505
|
+
name="itil_incident_update",
|
|
2506
|
+
description=(
|
|
2507
|
+
"Update an incident: transition status, escalate severity, "
|
|
2508
|
+
"add timeline notes, or resolve. Valid status transitions: "
|
|
2509
|
+
"detected->acknowledged->investigating->resolved->closed."
|
|
2510
|
+
),
|
|
2511
|
+
inputSchema={
|
|
2512
|
+
"type": "object",
|
|
2513
|
+
"properties": {
|
|
2514
|
+
"incident_id": {"type": "string", "description": "Incident ID (e.g. inc-a1b2c3d4)"},
|
|
2515
|
+
"agent": {"type": "string", "description": "Agent making the update"},
|
|
2516
|
+
"new_status": {"type": "string", "enum": ["acknowledged", "investigating", "escalated", "resolved", "closed"], "description": "New status"},
|
|
2517
|
+
"severity": {"type": "string", "enum": ["sev1", "sev2", "sev3", "sev4"], "description": "New severity"},
|
|
2518
|
+
"note": {"type": "string", "description": "Timeline note"},
|
|
2519
|
+
"resolution_summary": {"type": "string", "description": "Resolution summary (when resolving)"},
|
|
2520
|
+
"related_problem_id": {"type": "string", "description": "Link to a related problem record"},
|
|
2521
|
+
},
|
|
2522
|
+
"required": ["incident_id", "agent"],
|
|
2523
|
+
},
|
|
2524
|
+
),
|
|
2525
|
+
Tool(
|
|
2526
|
+
name="itil_incident_list",
|
|
2527
|
+
description="List ITIL incidents filtered by status, severity, or affected service.",
|
|
2528
|
+
inputSchema={
|
|
2529
|
+
"type": "object",
|
|
2530
|
+
"properties": {
|
|
2531
|
+
"status": {"type": "string", "enum": ["detected", "acknowledged", "investigating", "escalated", "resolved", "closed"], "description": "Filter by status"},
|
|
2532
|
+
"severity": {"type": "string", "enum": ["sev1", "sev2", "sev3", "sev4"], "description": "Filter by severity"},
|
|
2533
|
+
"service": {"type": "string", "description": "Filter by affected service name"},
|
|
2534
|
+
},
|
|
2535
|
+
"required": [],
|
|
2536
|
+
},
|
|
2537
|
+
),
|
|
2538
|
+
Tool(
|
|
2539
|
+
name="itil_problem_create",
|
|
2540
|
+
description=(
|
|
2541
|
+
"Create a new ITIL problem record to investigate root cause. "
|
|
2542
|
+
"Links to related incidents and auto-creates a GTD project."
|
|
2543
|
+
),
|
|
2544
|
+
inputSchema={
|
|
2545
|
+
"type": "object",
|
|
2546
|
+
"properties": {
|
|
2547
|
+
"title": {"type": "string", "description": "Problem title"},
|
|
2548
|
+
"managed_by": {"type": "string", "description": "Agent responsible for investigation"},
|
|
2549
|
+
"related_incident_ids": {"type": "array", "items": {"type": "string"}, "description": "Related incident IDs"},
|
|
2550
|
+
"workaround": {"type": "string", "description": "Known workaround if any"},
|
|
2551
|
+
"tags": {"type": "array", "items": {"type": "string"}, "description": "Tags for categorization"},
|
|
2552
|
+
},
|
|
2553
|
+
"required": ["title"],
|
|
2554
|
+
},
|
|
2555
|
+
),
|
|
2556
|
+
Tool(
|
|
2557
|
+
name="itil_problem_update",
|
|
2558
|
+
description=(
|
|
2559
|
+
"Update a problem record: transition status, set root cause, "
|
|
2560
|
+
"add workaround, optionally create a KEDB entry. "
|
|
2561
|
+
"Valid transitions: identified->analyzing->known_error->resolved."
|
|
2562
|
+
),
|
|
2563
|
+
inputSchema={
|
|
2564
|
+
"type": "object",
|
|
2565
|
+
"properties": {
|
|
2566
|
+
"problem_id": {"type": "string", "description": "Problem ID (e.g. prb-e5f6g7h8)"},
|
|
2567
|
+
"agent": {"type": "string", "description": "Agent making the update"},
|
|
2568
|
+
"new_status": {"type": "string", "enum": ["analyzing", "known_error", "resolved"], "description": "New status"},
|
|
2569
|
+
"root_cause": {"type": "string", "description": "Root cause description"},
|
|
2570
|
+
"workaround": {"type": "string", "description": "Workaround description"},
|
|
2571
|
+
"note": {"type": "string", "description": "Timeline note"},
|
|
2572
|
+
"create_kedb": {"type": "boolean", "description": "Create a KEDB entry from this problem"},
|
|
2573
|
+
},
|
|
2574
|
+
"required": ["problem_id", "agent"],
|
|
2575
|
+
},
|
|
2576
|
+
),
|
|
2577
|
+
Tool(
|
|
2578
|
+
name="itil_change_propose",
|
|
2579
|
+
description=(
|
|
2580
|
+
"Propose a change (RFC). Standard changes auto-approve. "
|
|
2581
|
+
"Normal changes require CAB approval. Emergency changes have a "
|
|
2582
|
+
"15-min timeout before auto-approval."
|
|
2583
|
+
),
|
|
2584
|
+
inputSchema={
|
|
2585
|
+
"type": "object",
|
|
2586
|
+
"properties": {
|
|
2587
|
+
"title": {"type": "string", "description": "Change title"},
|
|
2588
|
+
"change_type": {"type": "string", "enum": ["standard", "normal", "emergency"], "description": "Type of change (default: normal)"},
|
|
2589
|
+
"risk": {"type": "string", "enum": ["low", "medium", "high"], "description": "Risk level (default: medium)"},
|
|
2590
|
+
"rollback_plan": {"type": "string", "description": "How to roll back if the change fails"},
|
|
2591
|
+
"test_plan": {"type": "string", "description": "How to verify the change works"},
|
|
2592
|
+
"managed_by": {"type": "string", "description": "Agent managing the change"},
|
|
2593
|
+
"implementer": {"type": "string", "description": "Agent who will implement the change"},
|
|
2594
|
+
"related_problem_id": {"type": "string", "description": "Related problem ID if applicable"},
|
|
2595
|
+
"tags": {"type": "array", "items": {"type": "string"}, "description": "Tags for categorization"},
|
|
2596
|
+
},
|
|
2597
|
+
"required": ["title"],
|
|
2598
|
+
},
|
|
2599
|
+
),
|
|
2600
|
+
Tool(
|
|
2601
|
+
name="itil_change_update",
|
|
2602
|
+
description=(
|
|
2603
|
+
"Update a change: transition status (implementing, deployed, "
|
|
2604
|
+
"verified, failed, closed) or add timeline notes."
|
|
2605
|
+
),
|
|
2606
|
+
inputSchema={
|
|
2607
|
+
"type": "object",
|
|
2608
|
+
"properties": {
|
|
2609
|
+
"change_id": {"type": "string", "description": "Change ID (e.g. chg-i1j2k3l4)"},
|
|
2610
|
+
"agent": {"type": "string", "description": "Agent making the update"},
|
|
2611
|
+
"new_status": {"type": "string", "enum": ["reviewing", "approved", "rejected", "implementing", "deployed", "verified", "failed", "closed"], "description": "New status"},
|
|
2612
|
+
"note": {"type": "string", "description": "Timeline note"},
|
|
2613
|
+
},
|
|
2614
|
+
"required": ["change_id", "agent"],
|
|
2615
|
+
},
|
|
2616
|
+
),
|
|
2617
|
+
Tool(
|
|
2618
|
+
name="itil_cab_vote",
|
|
2619
|
+
description=(
|
|
2620
|
+
"Submit a CAB (Change Advisory Board) vote for a proposed change. "
|
|
2621
|
+
"Each agent writes its own vote file (conflict-free). "
|
|
2622
|
+
"A human rejection blocks the change; a human approval unblocks it."
|
|
2623
|
+
),
|
|
2624
|
+
inputSchema={
|
|
2625
|
+
"type": "object",
|
|
2626
|
+
"properties": {
|
|
2627
|
+
"change_id": {"type": "string", "description": "Change ID to vote on"},
|
|
2628
|
+
"agent": {"type": "string", "description": "Voting agent name"},
|
|
2629
|
+
"decision": {"type": "string", "enum": ["approved", "rejected", "abstain"], "description": "Vote decision (default: abstain)"},
|
|
2630
|
+
"conditions": {"type": "string", "description": "Conditions for approval"},
|
|
2631
|
+
},
|
|
2632
|
+
"required": ["change_id", "agent"],
|
|
2633
|
+
},
|
|
2634
|
+
),
|
|
2635
|
+
Tool(
|
|
2636
|
+
name="itil_status",
|
|
2637
|
+
description=(
|
|
2638
|
+
"ITIL dashboard: open incidents by severity, active problems, "
|
|
2639
|
+
"pending changes, and KEDB count."
|
|
2640
|
+
),
|
|
2641
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
2642
|
+
),
|
|
2643
|
+
Tool(
|
|
2644
|
+
name="itil_kedb_search",
|
|
2645
|
+
description=(
|
|
2646
|
+
"Search the Known Error Database by symptoms, service name, "
|
|
2647
|
+
"or keywords. Returns matching entries with workarounds."
|
|
2648
|
+
),
|
|
2649
|
+
inputSchema={
|
|
2650
|
+
"type": "object",
|
|
2651
|
+
"properties": {
|
|
2652
|
+
"query": {"type": "string", "description": "Search query (matches title, symptoms, root cause, tags)"},
|
|
2653
|
+
},
|
|
2654
|
+
"required": ["query"],
|
|
2655
|
+
},
|
|
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
|
+
),
|
|
2473
2690
|
]
|
|
2474
2691
|
|
|
2475
2692
|
|
|
@@ -2618,6 +2835,19 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
|
|
2618
2835
|
# Soul Blueprint Registry
|
|
2619
2836
|
"soul_registry_search": _handle_soul_registry_search,
|
|
2620
2837
|
"soul_registry_publish": _handle_soul_registry_publish,
|
|
2838
|
+
# ITIL Service Management
|
|
2839
|
+
"itil_incident_create": _handle_itil_incident_create,
|
|
2840
|
+
"itil_incident_update": _handle_itil_incident_update,
|
|
2841
|
+
"itil_incident_list": _handle_itil_incident_list,
|
|
2842
|
+
"itil_problem_create": _handle_itil_problem_create,
|
|
2843
|
+
"itil_problem_update": _handle_itil_problem_update,
|
|
2844
|
+
"itil_change_propose": _handle_itil_change_propose,
|
|
2845
|
+
"itil_change_update": _handle_itil_change_update,
|
|
2846
|
+
"itil_cab_vote": _handle_itil_cab_vote,
|
|
2847
|
+
"itil_status": _handle_itil_status,
|
|
2848
|
+
"itil_kedb_search": _handle_itil_kedb_search,
|
|
2849
|
+
# Brain-First Protocol
|
|
2850
|
+
"brain_first_check": _handle_brain_first_check,
|
|
2621
2851
|
}
|
|
2622
2852
|
handler = handlers.get(name)
|
|
2623
2853
|
if handler is None:
|
|
@@ -2959,8 +3189,8 @@ async def _handle_coord_complete(args: dict) -> list[TextContent]:
|
|
|
2959
3189
|
t.priority.value, ("community", "support_ticket", 50)
|
|
2960
3190
|
)
|
|
2961
3191
|
break
|
|
2962
|
-
except Exception:
|
|
2963
|
-
|
|
3192
|
+
except Exception as exc:
|
|
3193
|
+
logger.warning("Failed to calculate joules for completed task %s: %s", task_id, exc)
|
|
2964
3194
|
|
|
2965
3195
|
return _json_response({
|
|
2966
3196
|
"completed": True,
|
|
@@ -4679,6 +4909,78 @@ async def _handle_soul_registry_publish(args: dict) -> list[TextContent]:
|
|
|
4679
4909
|
return _error_response(f"Registry publish failed: {exc}")
|
|
4680
4910
|
|
|
4681
4911
|
|
|
4912
|
+
# ── ITIL handlers ─────────────────────────────────────────
|
|
4913
|
+
|
|
4914
|
+
|
|
4915
|
+
async def _handle_itil_incident_create(args: dict) -> list[TextContent]:
|
|
4916
|
+
"""Create a new ITIL incident."""
|
|
4917
|
+
from .mcp_tools.itil_tools import _handle_itil_incident_create as _impl
|
|
4918
|
+
return await _impl(args)
|
|
4919
|
+
|
|
4920
|
+
|
|
4921
|
+
async def _handle_itil_incident_update(args: dict) -> list[TextContent]:
|
|
4922
|
+
"""Update an ITIL incident."""
|
|
4923
|
+
from .mcp_tools.itil_tools import _handle_itil_incident_update as _impl
|
|
4924
|
+
return await _impl(args)
|
|
4925
|
+
|
|
4926
|
+
|
|
4927
|
+
async def _handle_itil_incident_list(args: dict) -> list[TextContent]:
|
|
4928
|
+
"""List ITIL incidents."""
|
|
4929
|
+
from .mcp_tools.itil_tools import _handle_itil_incident_list as _impl
|
|
4930
|
+
return await _impl(args)
|
|
4931
|
+
|
|
4932
|
+
|
|
4933
|
+
async def _handle_itil_problem_create(args: dict) -> list[TextContent]:
|
|
4934
|
+
"""Create a new ITIL problem."""
|
|
4935
|
+
from .mcp_tools.itil_tools import _handle_itil_problem_create as _impl
|
|
4936
|
+
return await _impl(args)
|
|
4937
|
+
|
|
4938
|
+
|
|
4939
|
+
async def _handle_itil_problem_update(args: dict) -> list[TextContent]:
|
|
4940
|
+
"""Update an ITIL problem."""
|
|
4941
|
+
from .mcp_tools.itil_tools import _handle_itil_problem_update as _impl
|
|
4942
|
+
return await _impl(args)
|
|
4943
|
+
|
|
4944
|
+
|
|
4945
|
+
async def _handle_itil_change_propose(args: dict) -> list[TextContent]:
|
|
4946
|
+
"""Propose a change (RFC)."""
|
|
4947
|
+
from .mcp_tools.itil_tools import _handle_itil_change_propose as _impl
|
|
4948
|
+
return await _impl(args)
|
|
4949
|
+
|
|
4950
|
+
|
|
4951
|
+
async def _handle_itil_change_update(args: dict) -> list[TextContent]:
|
|
4952
|
+
"""Update a change status."""
|
|
4953
|
+
from .mcp_tools.itil_tools import _handle_itil_change_update as _impl
|
|
4954
|
+
return await _impl(args)
|
|
4955
|
+
|
|
4956
|
+
|
|
4957
|
+
async def _handle_itil_cab_vote(args: dict) -> list[TextContent]:
|
|
4958
|
+
"""Submit a CAB vote."""
|
|
4959
|
+
from .mcp_tools.itil_tools import _handle_itil_cab_vote as _impl
|
|
4960
|
+
return await _impl(args)
|
|
4961
|
+
|
|
4962
|
+
|
|
4963
|
+
async def _handle_itil_status(args: dict) -> list[TextContent]:
|
|
4964
|
+
"""ITIL dashboard status."""
|
|
4965
|
+
from .mcp_tools.itil_tools import _handle_itil_status as _impl
|
|
4966
|
+
return await _impl(args)
|
|
4967
|
+
|
|
4968
|
+
|
|
4969
|
+
async def _handle_itil_kedb_search(args: dict) -> list[TextContent]:
|
|
4970
|
+
"""Search the Known Error Database."""
|
|
4971
|
+
from .mcp_tools.itil_tools import _handle_itil_kedb_search as _impl
|
|
4972
|
+
return await _impl(args)
|
|
4973
|
+
|
|
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
|
+
|
|
4682
4984
|
# ═══════════════════════════════════════════════════════════
|
|
4683
4985
|
# Entry Point
|
|
4684
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,
|
|
@@ -29,6 +30,7 @@ from . import (
|
|
|
29
30
|
gtd_tools,
|
|
30
31
|
health_tools,
|
|
31
32
|
heartbeat_tools,
|
|
33
|
+
itil_tools,
|
|
32
34
|
kms_tools,
|
|
33
35
|
memory_tools,
|
|
34
36
|
model_tools,
|
|
@@ -48,6 +50,7 @@ from . import (
|
|
|
48
50
|
# Ordered list of all tool-group modules.
|
|
49
51
|
_MODULES = [
|
|
50
52
|
agent_tools,
|
|
53
|
+
brain_first_tools,
|
|
51
54
|
memory_tools,
|
|
52
55
|
comm_tools,
|
|
53
56
|
sync_tools,
|
|
@@ -63,6 +66,7 @@ _MODULES = [
|
|
|
63
66
|
heartbeat_tools,
|
|
64
67
|
file_tools,
|
|
65
68
|
gtd_tools,
|
|
69
|
+
itil_tools,
|
|
66
70
|
pubsub_tools,
|
|
67
71
|
fortress_tools,
|
|
68
72
|
promoter_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,
|
|
@@ -21,7 +21,7 @@ _GTD_LISTS = {
|
|
|
21
21
|
"someday-maybe": "someday-maybe.json",
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
_VALID_SOURCES = {"manual", "telegram", "email", "voice"}
|
|
24
|
+
_VALID_SOURCES = {"manual", "telegram", "email", "voice", "itil"}
|
|
25
25
|
_VALID_PRIVACY = {"private", "team", "community", "public"}
|
|
26
26
|
_VALID_STATUSES = {"inbox", "next", "project", "waiting", "someday", "reference", "done"}
|
|
27
27
|
_VALID_PRIORITIES = {"critical", "high", "medium", "low"}
|