meshcode 2.0.8__tar.gz → 2.0.9__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.8 → meshcode-2.0.9}/PKG-INFO +1 -1
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/__init__.py +1 -1
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/meshcode_mcp/backend.py +67 -3
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/meshcode_mcp/server.py +5 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.0.8 → meshcode-2.0.9}/pyproject.toml +1 -1
- {meshcode-2.0.8 → meshcode-2.0.9}/README.md +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/cli.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/comms_v4.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/invites.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/launcher.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/launcher_install.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/preferences.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/run_agent.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/secrets.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/self_update.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode/setup_clients.py +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.0.8 → meshcode-2.0.9}/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.9"
|
|
@@ -105,6 +105,29 @@ def sb_update(table: str, filters: str, updates: Dict) -> Any:
|
|
|
105
105
|
return _request("PATCH", f"{table}?{filters}", data=updates, prefer="return=representation")
|
|
106
106
|
|
|
107
107
|
|
|
108
|
+
# Session recording config — set by MCP server at boot
|
|
109
|
+
_recording_enabled = False
|
|
110
|
+
_recording_api_key = ""
|
|
111
|
+
_recording_project_id = ""
|
|
112
|
+
_recording_agent_name = ""
|
|
113
|
+
_recording_session_id = ""
|
|
114
|
+
|
|
115
|
+
# RPCs that should NOT be recorded (internal/heartbeat/recording itself)
|
|
116
|
+
_SKIP_RECORDING = {"mc_heartbeat", "mc_record_event", "mc_agent_set_status_by_api_key",
|
|
117
|
+
"mc_acquire_agent_lease", "mc_release_agent_lease"}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def enable_recording(api_key: str, project_id: str, agent_name: str, session_id: str):
|
|
121
|
+
"""Called by MCP server at boot to enable session recording."""
|
|
122
|
+
global _recording_enabled, _recording_api_key, _recording_project_id
|
|
123
|
+
global _recording_agent_name, _recording_session_id
|
|
124
|
+
_recording_enabled = True
|
|
125
|
+
_recording_api_key = api_key
|
|
126
|
+
_recording_project_id = project_id
|
|
127
|
+
_recording_agent_name = agent_name
|
|
128
|
+
_recording_session_id = session_id
|
|
129
|
+
|
|
130
|
+
|
|
108
131
|
def sb_rpc(fn_name: str, params: Dict) -> Any:
|
|
109
132
|
url = f"{SUPABASE_URL}/rest/v1/rpc/{fn_name}"
|
|
110
133
|
body = json.dumps(params).encode("utf-8")
|
|
@@ -112,16 +135,57 @@ def sb_rpc(fn_name: str, params: Dict) -> Any:
|
|
|
112
135
|
try:
|
|
113
136
|
with urlopen(req, timeout=10) as resp:
|
|
114
137
|
raw = resp.read().decode("utf-8")
|
|
115
|
-
|
|
138
|
+
result = json.loads(raw) if raw.strip() else None
|
|
116
139
|
except HTTPError as e:
|
|
117
140
|
err = e.read().decode("utf-8", errors="replace")
|
|
118
141
|
try:
|
|
119
|
-
|
|
142
|
+
result = {"_error": json.loads(err).get("message", err[:200])}
|
|
120
143
|
except Exception:
|
|
121
|
-
|
|
144
|
+
result = {"_error": err[:200]}
|
|
145
|
+
# Record errors too
|
|
146
|
+
if _recording_enabled and fn_name not in _SKIP_RECORDING:
|
|
147
|
+
_bg_record("error", {"rpc": fn_name, "error": str(err)[:200]})
|
|
148
|
+
return result
|
|
122
149
|
except URLError as e:
|
|
123
150
|
return {"_error": str(e.reason)}
|
|
124
151
|
|
|
152
|
+
# Auto-record tool calls to session events (hot-reloadable)
|
|
153
|
+
if _recording_enabled and fn_name not in _SKIP_RECORDING:
|
|
154
|
+
_bg_record("tool_call", {"rpc": fn_name})
|
|
155
|
+
|
|
156
|
+
return result
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _bg_record(event_type: str, payload: dict):
|
|
160
|
+
"""Background-thread recording — never blocks the caller."""
|
|
161
|
+
import threading
|
|
162
|
+
def _do():
|
|
163
|
+
try:
|
|
164
|
+
sb_rpc_raw("mc_record_event", {
|
|
165
|
+
"p_api_key": _recording_api_key,
|
|
166
|
+
"p_project_id": _recording_project_id,
|
|
167
|
+
"p_agent_name": _recording_agent_name,
|
|
168
|
+
"p_session_id": _recording_session_id,
|
|
169
|
+
"p_event_type": event_type,
|
|
170
|
+
"p_payload": payload,
|
|
171
|
+
})
|
|
172
|
+
except Exception:
|
|
173
|
+
pass
|
|
174
|
+
threading.Thread(target=_do, daemon=True).start()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def sb_rpc_raw(fn_name: str, params: Dict) -> Any:
|
|
178
|
+
"""Raw RPC call without recording (to avoid infinite recursion)."""
|
|
179
|
+
url = f"{SUPABASE_URL}/rest/v1/rpc/{fn_name}"
|
|
180
|
+
body = json.dumps(params).encode("utf-8")
|
|
181
|
+
req = Request(url, data=body, method="POST", headers=_headers(content_profile=False))
|
|
182
|
+
try:
|
|
183
|
+
with urlopen(req, timeout=10) as resp:
|
|
184
|
+
raw = resp.read().decode("utf-8")
|
|
185
|
+
return json.loads(raw) if raw.strip() else None
|
|
186
|
+
except Exception:
|
|
187
|
+
return None
|
|
188
|
+
|
|
125
189
|
|
|
126
190
|
# ============================================================
|
|
127
191
|
# Project + agent helpers
|
|
@@ -762,6 +762,11 @@ async def lifespan(_app):
|
|
|
762
762
|
hb_thread = _threading.Thread(target=_heartbeat_thread_fn, daemon=True, name="meshcode-heartbeat")
|
|
763
763
|
hb_thread.start()
|
|
764
764
|
log.info(f"lifespan started — Realtime + heartbeat thread active for {AGENT_NAME}")
|
|
765
|
+
# Enable session recording in backend.py (hot-reloadable)
|
|
766
|
+
try:
|
|
767
|
+
be.enable_recording(_get_api_key(), _PROJECT_ID, AGENT_NAME, _SESSION_ID)
|
|
768
|
+
except Exception:
|
|
769
|
+
pass
|
|
765
770
|
_record_event_bg("boot", {"agent": AGENT_NAME, "project": PROJECT_NAME, "session_id": _SESSION_ID})
|
|
766
771
|
try:
|
|
767
772
|
yield {"realtime": _REALTIME}
|
|
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
|