meshcode 2.10.37__tar.gz → 2.10.40__tar.gz
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.
- {meshcode-2.10.37 → meshcode-2.10.40}/PKG-INFO +1 -1
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/__init__.py +1 -1
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/comms_v4.py +39 -16
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/meshcode_mcp/server.py +179 -5
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.37 → meshcode-2.10.40}/pyproject.toml +1 -1
- {meshcode-2.10.37 → meshcode-2.10.40}/README.md +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/cli.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/invites.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/launcher.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/preferences.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/secrets.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/self_update.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/setup.cfg +0 -0
- {meshcode-2.10.37 → meshcode-2.10.40}/tests/test_status_enum_coverage.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
-
__version__ = "2.10.
|
|
2
|
+
__version__ = "2.10.40"
|
|
@@ -615,7 +615,7 @@ def spawn_headless_agent(project, name, project_id, message_body, from_agent):
|
|
|
615
615
|
# The agent is identity-only at rest; next message wakes it again.
|
|
616
616
|
sb_update("mc_agents",
|
|
617
617
|
f"project_id=eq.{project_id}&name=eq.{quote(name)}",
|
|
618
|
-
{"status": "sleeping", "task": "
|
|
618
|
+
{"status": "sleeping", "task": "Waiting for messages...",
|
|
619
619
|
"last_active_at": now_iso(), "last_heartbeat": now_iso()})
|
|
620
620
|
except Exception:
|
|
621
621
|
pass
|
|
@@ -865,7 +865,7 @@ def register(project, name, role=""):
|
|
|
865
865
|
ensure_sessions()
|
|
866
866
|
project_id = get_project_id(project)
|
|
867
867
|
if not project_id:
|
|
868
|
-
print(f"[ERROR]
|
|
868
|
+
print(f"[ERROR] Could not create/find project '{project}'")
|
|
869
869
|
return
|
|
870
870
|
|
|
871
871
|
ppid = os.getppid()
|
|
@@ -946,7 +946,7 @@ def register(project, name, role=""):
|
|
|
946
946
|
order="created_at.desc", limit=10)
|
|
947
947
|
if recent:
|
|
948
948
|
recent.reverse()
|
|
949
|
-
print(f"\n[COMMS]
|
|
949
|
+
print(f"\n[COMMS] Last {len(recent)} team messages:")
|
|
950
950
|
for msg in recent:
|
|
951
951
|
ts = msg.get("created_at", "")[-8:]
|
|
952
952
|
fr = msg["from_agent"]
|
|
@@ -1132,7 +1132,7 @@ def watch(project, name, interval=10, timeout=0):
|
|
|
1132
1132
|
# Update status to standby
|
|
1133
1133
|
sb_update("mc_agents",
|
|
1134
1134
|
f"project_id=eq.{project_id}&name=eq.{quote(name)}",
|
|
1135
|
-
{"status": "standby", "task": "
|
|
1135
|
+
{"status": "standby", "task": "Waiting for messages...", "last_heartbeat": now_iso()})
|
|
1136
1136
|
|
|
1137
1137
|
cycle = timeout if timeout > 0 else 600
|
|
1138
1138
|
print(f"[{project}] {name} en watch — poll cada {interval}s, ciclo {cycle}s (auto-loop)")
|
|
@@ -1161,10 +1161,10 @@ def watch(project, name, interval=10, timeout=0):
|
|
|
1161
1161
|
time.sleep(interval)
|
|
1162
1162
|
|
|
1163
1163
|
# Cycle ended without messages — check user context, then AUTO-RESTART watch
|
|
1164
|
-
print(f"[{project}]
|
|
1164
|
+
print(f"[{project}] No messages this cycle. Watch active, still running. Standby.")
|
|
1165
1165
|
sb_update("mc_agents",
|
|
1166
1166
|
f"project_id=eq.{project_id}&name=eq.{quote(name)}",
|
|
1167
|
-
{"status": "standby", "task": "
|
|
1167
|
+
{"status": "standby", "task": "Waiting for messages...", "last_heartbeat": now_iso()})
|
|
1168
1168
|
|
|
1169
1169
|
|
|
1170
1170
|
def update_board(project, name, status, task=""):
|
|
@@ -1183,7 +1183,7 @@ def update_board(project, name, status, task=""):
|
|
|
1183
1183
|
print(f"[{project}] {name}: NO puedes estar idle — tienes {count} mensaje(s) pendientes.")
|
|
1184
1184
|
print(f"[{project}] Ejecuta: python3 ~/Desktop/meshcode/comms_v4.py read {project} {name}")
|
|
1185
1185
|
status = "working"
|
|
1186
|
-
task = f"{count}
|
|
1186
|
+
task = f"{count} pending messages"
|
|
1187
1187
|
|
|
1188
1188
|
updates = {"status": status, "last_heartbeat": now_iso()}
|
|
1189
1189
|
if task:
|
|
@@ -1197,7 +1197,7 @@ def update_board(project, name, status, task=""):
|
|
|
1197
1197
|
def show_board(project):
|
|
1198
1198
|
project_id = get_project_id(project)
|
|
1199
1199
|
if not project_id:
|
|
1200
|
-
print(f"[{project}]
|
|
1200
|
+
print(f"[{project}] Project not found")
|
|
1201
1201
|
return
|
|
1202
1202
|
|
|
1203
1203
|
agents = sb_select("mc_agents", f"project_id=eq.{project_id}", order="registered_at.asc")
|
|
@@ -1221,7 +1221,7 @@ def show_status(project=None):
|
|
|
1221
1221
|
projects_data = sb_select("mc_projects", "", order="created_at.asc")
|
|
1222
1222
|
|
|
1223
1223
|
if not projects_data:
|
|
1224
|
-
print("[COMMS]
|
|
1224
|
+
print("[COMMS] No active projects")
|
|
1225
1225
|
return
|
|
1226
1226
|
|
|
1227
1227
|
print(f"\n{'='*60}")
|
|
@@ -1266,7 +1266,7 @@ def show_history(project, last_n=20, between=None):
|
|
|
1266
1266
|
messages.reverse()
|
|
1267
1267
|
|
|
1268
1268
|
print(f"\n{'='*60}")
|
|
1269
|
-
print(f" {project.upper()} —
|
|
1269
|
+
print(f" {project.upper()} — History ({len(messages)} messages)")
|
|
1270
1270
|
if between:
|
|
1271
1271
|
print(f" Filtro: {between}")
|
|
1272
1272
|
print(f"{'='*60}\n")
|
|
@@ -1472,7 +1472,7 @@ def connect_terminal(project, name, role=""):
|
|
|
1472
1472
|
ensure_sessions()
|
|
1473
1473
|
project_id = get_project_id(project)
|
|
1474
1474
|
if not project_id:
|
|
1475
|
-
print(f"[ERROR]
|
|
1475
|
+
print(f"[ERROR] Could not create/find project '{project}'")
|
|
1476
1476
|
return
|
|
1477
1477
|
|
|
1478
1478
|
tty, owning = _capture_tty_for_pid(os.getppid())
|
|
@@ -1542,7 +1542,7 @@ def disconnect_terminal(project, name):
|
|
|
1542
1542
|
"""Mark agent offline, clear tty/pid, stop heartbeat, remove session file."""
|
|
1543
1543
|
project_id = get_project_id(project)
|
|
1544
1544
|
if not project_id:
|
|
1545
|
-
print(f"[ERROR]
|
|
1545
|
+
print(f"[ERROR] project '{project}' not found")
|
|
1546
1546
|
return
|
|
1547
1547
|
rpc_result = sb_rpc("mc_disconnect_agent", {
|
|
1548
1548
|
"p_project_id": project_id,
|
|
@@ -2177,7 +2177,7 @@ if __name__ == "__main__":
|
|
|
2177
2177
|
sys.exit(1)
|
|
2178
2178
|
project_id = get_project_id(proj)
|
|
2179
2179
|
if not project_id:
|
|
2180
|
-
print(f"[ERROR]
|
|
2180
|
+
print(f"[ERROR] project '{proj}' not found")
|
|
2181
2181
|
sys.exit(1)
|
|
2182
2182
|
ok = spawn_headless_agent(proj, name, project_id,
|
|
2183
2183
|
"ping: synthetic wake-headless test message", "test-harness")
|
|
@@ -2193,7 +2193,7 @@ if __name__ == "__main__":
|
|
|
2193
2193
|
sys.exit(1)
|
|
2194
2194
|
project_id = get_project_id(proj)
|
|
2195
2195
|
if not project_id:
|
|
2196
|
-
print(f"[ERROR]
|
|
2196
|
+
print(f"[ERROR] project '{proj}' not found")
|
|
2197
2197
|
sys.exit(1)
|
|
2198
2198
|
rpc_name = {"kill": "mc_agent_kill", "wake": "mc_agent_wake", "sleep": "mc_agent_sleep"}[cmd]
|
|
2199
2199
|
result = sb_rpc(rpc_name, {"p_project_id": project_id, "p_agent_name": name})
|
|
@@ -2212,7 +2212,7 @@ if __name__ == "__main__":
|
|
|
2212
2212
|
sys.exit(1)
|
|
2213
2213
|
project_id = get_project_id(proj)
|
|
2214
2214
|
if not project_id:
|
|
2215
|
-
print(f"[ERROR]
|
|
2215
|
+
print(f"[ERROR] project '{proj}' not found")
|
|
2216
2216
|
sys.exit(1)
|
|
2217
2217
|
if sub == "get":
|
|
2218
2218
|
result = sb_rpc("mc_agent_get_profile", {"p_project_id": project_id, "p_agent_name": name})
|
|
@@ -2425,7 +2425,30 @@ if __name__ == "__main__":
|
|
|
2425
2425
|
sys.exit(1)
|
|
2426
2426
|
login(key)
|
|
2427
2427
|
|
|
2428
|
-
elif cmd
|
|
2428
|
+
elif cmd == "whoami":
|
|
2429
|
+
# Show the logged-in user identity from profile_meta.json
|
|
2430
|
+
meta_path = Path.home() / ".meshcode" / "profile_meta.json"
|
|
2431
|
+
if meta_path.exists():
|
|
2432
|
+
try:
|
|
2433
|
+
meta = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
2434
|
+
print()
|
|
2435
|
+
print(f"[meshcode] Logged in as:")
|
|
2436
|
+
if meta.get("email"):
|
|
2437
|
+
print(f" email: {meta['email']}")
|
|
2438
|
+
if meta.get("display_name"):
|
|
2439
|
+
print(f" display name: {meta['display_name']}")
|
|
2440
|
+
if meta.get("user_id"):
|
|
2441
|
+
print(f" user ID: {meta['user_id']}")
|
|
2442
|
+
active = os.environ.get("MESHCODE_KEYCHAIN_PROFILE") or "default"
|
|
2443
|
+
print(f" profile: {active}")
|
|
2444
|
+
print()
|
|
2445
|
+
except Exception:
|
|
2446
|
+
print("[meshcode] Could not read profile. Run `meshcode login <api_key>`.")
|
|
2447
|
+
else:
|
|
2448
|
+
print("[meshcode] Not logged in. Run `meshcode login <api_key>`.")
|
|
2449
|
+
sys.exit(0)
|
|
2450
|
+
|
|
2451
|
+
elif cmd == "profiles":
|
|
2429
2452
|
# List all stored keychain profiles with metadata
|
|
2430
2453
|
try:
|
|
2431
2454
|
import importlib as _il
|
|
@@ -601,7 +601,9 @@ def with_working_status(func):
|
|
|
601
601
|
global _CONSECUTIVE_IDLE_SECONDS
|
|
602
602
|
_CONSECUTIVE_IDLE_SECONDS = 0 # any non-wait tool resets idle timer
|
|
603
603
|
_set_state("working", name)
|
|
604
|
-
|
|
604
|
+
# Estimate input tokens (chars/4) for usage tracking
|
|
605
|
+
_est_tokens = sum(len(str(v)) for v in kwargs.values()) // 4
|
|
606
|
+
_record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys()), "estimated_tokens": _est_tokens})
|
|
605
607
|
try:
|
|
606
608
|
return await func(*args, **kwargs)
|
|
607
609
|
except Exception as e:
|
|
@@ -622,7 +624,8 @@ def with_working_status(func):
|
|
|
622
624
|
global _CONSECUTIVE_IDLE_SECONDS
|
|
623
625
|
_CONSECUTIVE_IDLE_SECONDS = 0 # any non-wait tool resets idle timer
|
|
624
626
|
_set_state("working", name)
|
|
625
|
-
|
|
627
|
+
_est_tokens = sum(len(str(v)) for v in kwargs.values()) // 4
|
|
628
|
+
_record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys()), "estimated_tokens": _est_tokens})
|
|
626
629
|
try:
|
|
627
630
|
return func(*args, **kwargs)
|
|
628
631
|
except Exception as e:
|
|
@@ -1512,6 +1515,88 @@ def meshcode_read(include_acks: bool = False) -> Dict[str, Any]:
|
|
|
1512
1515
|
return split
|
|
1513
1516
|
|
|
1514
1517
|
|
|
1518
|
+
@mcp.tool()
|
|
1519
|
+
@with_working_status
|
|
1520
|
+
async def meshcode_call(to: str, function: str, args: Any = None, timeout_seconds: int = 30) -> Dict[str, Any]:
|
|
1521
|
+
"""Synchronous RPC call to another agent. Blocks until response (max 30s).
|
|
1522
|
+
|
|
1523
|
+
Sends a structured request, waits for the callee to respond with a matching
|
|
1524
|
+
call_id. Like gRPC over the mesh — enables agents to ask each other questions
|
|
1525
|
+
and get structured answers.
|
|
1526
|
+
|
|
1527
|
+
Args:
|
|
1528
|
+
to: Target agent name.
|
|
1529
|
+
function: Function/action name the callee should execute.
|
|
1530
|
+
args: Arguments (any JSON-serializable value).
|
|
1531
|
+
timeout_seconds: Max wait time (default 30, max 60).
|
|
1532
|
+
"""
|
|
1533
|
+
import uuid as _uuid
|
|
1534
|
+
if not to or not to.strip():
|
|
1535
|
+
return {"error": "recipient 'to' cannot be empty"}
|
|
1536
|
+
call_id = str(_uuid.uuid4())
|
|
1537
|
+
timeout_seconds = min(max(timeout_seconds, 5), 60)
|
|
1538
|
+
|
|
1539
|
+
# Send the RPC request
|
|
1540
|
+
request_payload = {
|
|
1541
|
+
"type": "rpc_request",
|
|
1542
|
+
"call_id": call_id,
|
|
1543
|
+
"function": function,
|
|
1544
|
+
"args": args,
|
|
1545
|
+
"from": AGENT_NAME,
|
|
1546
|
+
"timeout": timeout_seconds,
|
|
1547
|
+
}
|
|
1548
|
+
send_result = be.send_message(
|
|
1549
|
+
_PROJECT_ID, AGENT_NAME, to, request_payload,
|
|
1550
|
+
msg_type="rpc", api_key=_get_api_key()
|
|
1551
|
+
)
|
|
1552
|
+
if not send_result.get("sent") and send_result.get("error"):
|
|
1553
|
+
return {"error": f"failed to send RPC request: {send_result['error']}"}
|
|
1554
|
+
|
|
1555
|
+
# Poll for response with matching call_id
|
|
1556
|
+
import asyncio as _asyncio
|
|
1557
|
+
poll_interval = 1.0
|
|
1558
|
+
elapsed = 0.0
|
|
1559
|
+
while elapsed < timeout_seconds:
|
|
1560
|
+
await _asyncio.sleep(poll_interval)
|
|
1561
|
+
elapsed += poll_interval
|
|
1562
|
+
|
|
1563
|
+
# Check Realtime buffer first
|
|
1564
|
+
if _REALTIME:
|
|
1565
|
+
for msg in _REALTIME.peek():
|
|
1566
|
+
p = msg.get("payload") or {}
|
|
1567
|
+
if isinstance(p, dict) and p.get("type") == "rpc_response" and p.get("call_id") == call_id:
|
|
1568
|
+
# Found response — drain it
|
|
1569
|
+
_REALTIME.drain()
|
|
1570
|
+
return {
|
|
1571
|
+
"ok": True,
|
|
1572
|
+
"call_id": call_id,
|
|
1573
|
+
"from": msg.get("from"),
|
|
1574
|
+
"result": p.get("result"),
|
|
1575
|
+
"error": p.get("error"),
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
# Fallback: check DB
|
|
1579
|
+
try:
|
|
1580
|
+
raw = be.read_inbox(_PROJECT_ID, AGENT_NAME, mark_read=True, api_key=_get_api_key())
|
|
1581
|
+
for m in (raw if isinstance(raw, list) else raw.get("messages", []) if isinstance(raw, dict) else []):
|
|
1582
|
+
p = m.get("payload") or {}
|
|
1583
|
+
if isinstance(p, dict) and p.get("type") == "rpc_response" and p.get("call_id") == call_id:
|
|
1584
|
+
return {
|
|
1585
|
+
"ok": True,
|
|
1586
|
+
"call_id": call_id,
|
|
1587
|
+
"from": m.get("from_agent") or m.get("from"),
|
|
1588
|
+
"result": p.get("result"),
|
|
1589
|
+
"error": p.get("error"),
|
|
1590
|
+
}
|
|
1591
|
+
except Exception:
|
|
1592
|
+
pass
|
|
1593
|
+
|
|
1594
|
+
# Exponential backoff on polling (1s → 2s → 3s, cap 3s)
|
|
1595
|
+
poll_interval = min(poll_interval + 0.5, 3.0)
|
|
1596
|
+
|
|
1597
|
+
return {"error": f"RPC call to {to}.{function} timed out after {timeout_seconds}s", "call_id": call_id}
|
|
1598
|
+
|
|
1599
|
+
|
|
1515
1600
|
@mcp.tool()
|
|
1516
1601
|
@with_working_status
|
|
1517
1602
|
def meshcode_history(limit: int = 20, agent_filter: Optional[str] = None) -> Dict[str, Any]:
|
|
@@ -2266,6 +2351,80 @@ def meshcode_task_reassign(task_id: str, new_assignee: str) -> Dict[str, Any]:
|
|
|
2266
2351
|
})
|
|
2267
2352
|
|
|
2268
2353
|
|
|
2354
|
+
# ----------------- SCHEDULED TASKS -----------------
|
|
2355
|
+
|
|
2356
|
+
@mcp.tool()
|
|
2357
|
+
@with_working_status
|
|
2358
|
+
def meshcode_schedule(title: str, cron_expression: str, assignee: str = "*",
|
|
2359
|
+
description: str = "", priority: str = "normal") -> Dict[str, Any]:
|
|
2360
|
+
"""Create a recurring scheduled task. Fires on cron schedule.
|
|
2361
|
+
|
|
2362
|
+
Args:
|
|
2363
|
+
title: Task title (created each time it fires).
|
|
2364
|
+
cron_expression: Standard cron (e.g. "0 9 * * 1-5" = 9am weekdays).
|
|
2365
|
+
assignee: Agent name or "*" for any.
|
|
2366
|
+
description: Task description template.
|
|
2367
|
+
priority: low/normal/high/urgent.
|
|
2368
|
+
"""
|
|
2369
|
+
if not title or not title.strip():
|
|
2370
|
+
return {"error": "title cannot be empty"}
|
|
2371
|
+
if not cron_expression or not cron_expression.strip():
|
|
2372
|
+
return {"error": "cron_expression cannot be empty"}
|
|
2373
|
+
api_key = _get_api_key()
|
|
2374
|
+
return be.sb_rpc("mc_schedule_create", {
|
|
2375
|
+
"p_api_key": api_key,
|
|
2376
|
+
"p_project_id": _PROJECT_ID,
|
|
2377
|
+
"p_creator_agent": AGENT_NAME,
|
|
2378
|
+
"p_title": title.strip(),
|
|
2379
|
+
"p_cron_expression": cron_expression.strip(),
|
|
2380
|
+
"p_description": description,
|
|
2381
|
+
"p_assignee": assignee,
|
|
2382
|
+
"p_priority": priority,
|
|
2383
|
+
})
|
|
2384
|
+
|
|
2385
|
+
|
|
2386
|
+
@mcp.tool()
|
|
2387
|
+
@with_working_status
|
|
2388
|
+
def meshcode_schedule_list() -> Dict[str, Any]:
|
|
2389
|
+
"""List all scheduled/recurring tasks for this meshwork."""
|
|
2390
|
+
api_key = _get_api_key()
|
|
2391
|
+
return be.sb_rpc("mc_schedule_list", {
|
|
2392
|
+
"p_api_key": api_key,
|
|
2393
|
+
"p_project_id": _PROJECT_ID,
|
|
2394
|
+
})
|
|
2395
|
+
|
|
2396
|
+
|
|
2397
|
+
# ----------------- TEMPLATES -----------------
|
|
2398
|
+
|
|
2399
|
+
@mcp.tool()
|
|
2400
|
+
@with_working_status
|
|
2401
|
+
def meshcode_publish_template(slug: str, name: str, description: str = "",
|
|
2402
|
+
category: str = "Custom", tags: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
2403
|
+
"""Publish this meshwork as a reusable template others can fork.
|
|
2404
|
+
|
|
2405
|
+
Args:
|
|
2406
|
+
slug: URL-safe identifier (e.g. "my-saas-team"). Must be unique.
|
|
2407
|
+
name: Human-readable template name.
|
|
2408
|
+
description: What this template is for.
|
|
2409
|
+
category: Template category (Development, DevOps, Research, etc).
|
|
2410
|
+
tags: Searchable tags.
|
|
2411
|
+
"""
|
|
2412
|
+
if not slug or not slug.strip():
|
|
2413
|
+
return {"error": "slug cannot be empty"}
|
|
2414
|
+
if not name or not name.strip():
|
|
2415
|
+
return {"error": "name cannot be empty"}
|
|
2416
|
+
api_key = _get_api_key()
|
|
2417
|
+
return be.sb_rpc("mc_template_publish", {
|
|
2418
|
+
"p_api_key": api_key,
|
|
2419
|
+
"p_project_id": _PROJECT_ID,
|
|
2420
|
+
"p_slug": slug.strip(),
|
|
2421
|
+
"p_name": name.strip(),
|
|
2422
|
+
"p_description": description,
|
|
2423
|
+
"p_category": category,
|
|
2424
|
+
"p_tags": tags or [],
|
|
2425
|
+
})
|
|
2426
|
+
|
|
2427
|
+
|
|
2269
2428
|
# ----------------- PROACTIVE HEALTH SCAN -----------------
|
|
2270
2429
|
|
|
2271
2430
|
@mcp.tool()
|
|
@@ -2485,21 +2644,36 @@ def meshcode_add_agent(name: str, role: str = "") -> Dict[str, Any]:
|
|
|
2485
2644
|
@mcp.tool()
|
|
2486
2645
|
@with_working_status
|
|
2487
2646
|
def meshcode_edit_agent(name: str, role: Optional[str] = None, launch_prompt: Optional[str] = None) -> Dict[str, Any]:
|
|
2488
|
-
"""Update an agent's role or launch prompt.
|
|
2647
|
+
"""Update an agent's role or launch prompt. Notifies the running agent to reload.
|
|
2489
2648
|
|
|
2490
2649
|
Args:
|
|
2491
2650
|
name: Agent name to edit.
|
|
2492
2651
|
role: New role description.
|
|
2493
2652
|
launch_prompt: New launch prompt (injected into agent's system context).
|
|
2494
2653
|
"""
|
|
2495
|
-
|
|
2654
|
+
result = be.sb_rpc("mc_agent_update_profile", {
|
|
2496
2655
|
"p_project_id": _PROJECT_ID,
|
|
2497
2656
|
"p_agent_name": name,
|
|
2498
2657
|
"p_color": None,
|
|
2499
2658
|
"p_display_name": None,
|
|
2500
2659
|
"p_role_description": role,
|
|
2501
2660
|
"p_launch_prompt": launch_prompt,
|
|
2502
|
-
})
|
|
2661
|
+
})
|
|
2662
|
+
if not result:
|
|
2663
|
+
return {"error": "failed to update agent"}
|
|
2664
|
+
|
|
2665
|
+
# Hot-swap: notify the running agent to reload its prompt
|
|
2666
|
+
if name != AGENT_NAME:
|
|
2667
|
+
try:
|
|
2668
|
+
be.send_message(_PROJECT_ID, AGENT_NAME, name, {
|
|
2669
|
+
"type": "prompt_update",
|
|
2670
|
+
"text": f"Your {'role' if role else 'launch prompt'} was updated by {AGENT_NAME}. Changes take effect on next boot or meshcode_check.",
|
|
2671
|
+
"updated_fields": {"role": role, "launch_prompt": launch_prompt is not None},
|
|
2672
|
+
}, msg_type="system", api_key=_get_api_key())
|
|
2673
|
+
except Exception:
|
|
2674
|
+
pass # best-effort notification
|
|
2675
|
+
|
|
2676
|
+
return result
|
|
2503
2677
|
|
|
2504
2678
|
|
|
2505
2679
|
@mcp.tool()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|