meshcode 2.0.6__tar.gz → 2.0.8__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.0.6 → meshcode-2.0.8}/PKG-INFO +1 -1
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/__init__.py +1 -1
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/backend.py +14 -2
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/server.py +71 -20
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.0.6 → meshcode-2.0.8}/pyproject.toml +1 -1
- {meshcode-2.0.6 → meshcode-2.0.8}/README.md +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/cli.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/comms_v4.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/invites.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/launcher.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/launcher_install.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/preferences.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/run_agent.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/secrets.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/self_update.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/setup_clients.py +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.0.6 → meshcode-2.0.8}/setup.cfg +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
-
__version__ = "2.0.
|
|
2
|
+
__version__ = "2.0.8"
|
|
@@ -319,10 +319,22 @@ def record_event(api_key, project_id, agent_name, session_id, event_type, payloa
|
|
|
319
319
|
})
|
|
320
320
|
|
|
321
321
|
|
|
322
|
-
def get_history(project_id: str, limit: int = 20) -> List[Dict]:
|
|
322
|
+
def get_history(project_id: str, limit: int = 20, agent_filter: str = "") -> List[Dict]:
|
|
323
|
+
filters = f"project_id=eq.{project_id}&type=neq.ack"
|
|
324
|
+
if agent_filter:
|
|
325
|
+
filters += f"&or=(from_agent.eq.{quote(agent_filter)},to_agent.eq.{quote(agent_filter)})"
|
|
323
326
|
return sb_select(
|
|
324
327
|
"mc_messages",
|
|
325
|
-
|
|
328
|
+
filters,
|
|
326
329
|
order="created_at.desc",
|
|
327
330
|
limit=limit,
|
|
328
331
|
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def get_message_by_id(project_id: str, msg_id: str) -> Optional[Dict]:
|
|
335
|
+
results = sb_select(
|
|
336
|
+
"mc_messages",
|
|
337
|
+
f"project_id=eq.{project_id}&id=eq.{msg_id}",
|
|
338
|
+
limit=1,
|
|
339
|
+
)
|
|
340
|
+
return results[0] if results else None
|
|
@@ -342,8 +342,8 @@ _current_state = "online"
|
|
|
342
342
|
_last_tool_at = _time.time()
|
|
343
343
|
_current_tool = ""
|
|
344
344
|
_IDLE_THRESHOLD_S = 120 # seconds without tool call → IDLE
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
_WORKING_COOLDOWN_S = 60 # seconds after last tool returns before flipping to ONLINE
|
|
346
|
+
_working_timer: Optional[_threading.Timer] = None
|
|
347
347
|
|
|
348
348
|
|
|
349
349
|
async def _async_flip_status(status: str, task: str = "") -> None:
|
|
@@ -372,11 +372,11 @@ def _schedule_flip(status: str, task: str = "") -> None:
|
|
|
372
372
|
|
|
373
373
|
def _set_state(state: str, tool: str = "") -> None:
|
|
374
374
|
"""Update the state machine and broadcast to dashboard."""
|
|
375
|
-
global _current_state, _current_tool, _last_tool_at,
|
|
376
|
-
# Cancel any pending
|
|
377
|
-
if
|
|
378
|
-
|
|
379
|
-
|
|
375
|
+
global _current_state, _current_tool, _last_tool_at, _working_timer
|
|
376
|
+
# Cancel any pending working→online timer
|
|
377
|
+
if _working_timer is not None:
|
|
378
|
+
_working_timer.cancel()
|
|
379
|
+
_working_timer = None
|
|
380
380
|
_current_state = state
|
|
381
381
|
_current_tool = tool
|
|
382
382
|
if state == "working":
|
|
@@ -384,11 +384,11 @@ def _set_state(state: str, tool: str = "") -> None:
|
|
|
384
384
|
_schedule_flip(state, tool)
|
|
385
385
|
|
|
386
386
|
|
|
387
|
-
def
|
|
388
|
-
"""Called after
|
|
389
|
-
global
|
|
390
|
-
|
|
391
|
-
if _current_state == "
|
|
387
|
+
def _working_to_online() -> None:
|
|
388
|
+
"""Called after WORKING cooldown expires — flip to ONLINE."""
|
|
389
|
+
global _working_timer
|
|
390
|
+
_working_timer = None
|
|
391
|
+
if _current_state == "working":
|
|
392
392
|
_set_state("online", "")
|
|
393
393
|
|
|
394
394
|
|
|
@@ -406,13 +406,12 @@ def with_working_status(func):
|
|
|
406
406
|
return await func(*args, **kwargs)
|
|
407
407
|
finally:
|
|
408
408
|
if not skip:
|
|
409
|
-
global _last_tool_at,
|
|
409
|
+
global _last_tool_at, _working_timer
|
|
410
410
|
_last_tool_at = _time.time()
|
|
411
|
-
#
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
_processing_timer.start()
|
|
411
|
+
# Stay WORKING with 60s cooldown — covers LLM thinking between tools
|
|
412
|
+
_working_timer = _threading.Timer(_WORKING_COOLDOWN_S, _working_to_online)
|
|
413
|
+
_working_timer.daemon = True
|
|
414
|
+
_working_timer.start()
|
|
416
415
|
return awrapper
|
|
417
416
|
else:
|
|
418
417
|
@_functools.wraps(func)
|
|
@@ -700,9 +699,9 @@ def _heartbeat_thread_fn():
|
|
|
700
699
|
while not _heartbeat_stop.is_set():
|
|
701
700
|
try:
|
|
702
701
|
be.sb_rpc("mc_heartbeat", {"p_project_id": _PROJECT_ID, "p_agent_name": AGENT_NAME, "p_version": "2.0.0"})
|
|
703
|
-
# Idle detection: only from online
|
|
702
|
+
# Idle detection: only from online state (NOT waiting or working)
|
|
704
703
|
# Agents in meshcode_wait should stay WAITING, not flip to IDLE
|
|
705
|
-
if _current_state
|
|
704
|
+
if _current_state == "online" and (_time.time() - _last_tool_at) > _IDLE_THRESHOLD_S:
|
|
706
705
|
_set_state("idle", f"idle ({int((_time.time() - _last_tool_at) / 60)}m)")
|
|
707
706
|
# Sync current state to DB (in case realtime missed it)
|
|
708
707
|
try:
|
|
@@ -883,6 +882,58 @@ def meshcode_read(include_acks: bool = False) -> Dict[str, Any]:
|
|
|
883
882
|
return split
|
|
884
883
|
|
|
885
884
|
|
|
885
|
+
@mcp.tool()
|
|
886
|
+
@with_working_status
|
|
887
|
+
def meshcode_history(limit: int = 20, agent_filter: Optional[str] = None) -> Dict[str, Any]:
|
|
888
|
+
"""View recent message history (both read and unread). Use when you need
|
|
889
|
+
context from past conversations or lost messages after context compression.
|
|
890
|
+
|
|
891
|
+
Args:
|
|
892
|
+
limit: Max messages to return (default 20).
|
|
893
|
+
agent_filter: Optional agent name to filter (shows messages to/from that agent).
|
|
894
|
+
"""
|
|
895
|
+
raw = be.get_history(_PROJECT_ID, limit=limit, agent_filter=agent_filter or "")
|
|
896
|
+
messages = [
|
|
897
|
+
{
|
|
898
|
+
"from": m.get("from_agent", ""),
|
|
899
|
+
"to": m.get("to_agent", ""),
|
|
900
|
+
"type": m.get("type", "msg"),
|
|
901
|
+
"ts": m.get("created_at", ""),
|
|
902
|
+
"payload": m.get("payload", {}),
|
|
903
|
+
"id": m.get("id", ""),
|
|
904
|
+
"read": m.get("read", False),
|
|
905
|
+
}
|
|
906
|
+
for m in raw
|
|
907
|
+
]
|
|
908
|
+
return {"ok": True, "messages": messages, "count": len(messages)}
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
@mcp.tool()
|
|
912
|
+
@with_working_status
|
|
913
|
+
def meshcode_read_message(msg_id: str) -> Dict[str, Any]:
|
|
914
|
+
"""Fetch a specific message by ID. Use when you need to re-read a message
|
|
915
|
+
after context was compressed or session restarted.
|
|
916
|
+
|
|
917
|
+
Args:
|
|
918
|
+
msg_id: The UUID of the message to fetch.
|
|
919
|
+
"""
|
|
920
|
+
msg = be.get_message_by_id(_PROJECT_ID, msg_id)
|
|
921
|
+
if not msg:
|
|
922
|
+
return {"error": "message not found", "msg_id": msg_id}
|
|
923
|
+
return {
|
|
924
|
+
"ok": True,
|
|
925
|
+
"message": {
|
|
926
|
+
"from": msg.get("from_agent", ""),
|
|
927
|
+
"to": msg.get("to_agent", ""),
|
|
928
|
+
"type": msg.get("type", "msg"),
|
|
929
|
+
"ts": msg.get("created_at", ""),
|
|
930
|
+
"payload": msg.get("payload", {}),
|
|
931
|
+
"id": msg.get("id", ""),
|
|
932
|
+
"read": msg.get("read", False),
|
|
933
|
+
},
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
|
|
886
937
|
def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
|
887
938
|
"""Return {reason, from} if the list contains a global_done signal, else None."""
|
|
888
939
|
for m in messages:
|
|
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
|