mesh-context-layer 0.4.0__py3-none-any.whl

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.
mesh/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ from .memory import Mesh
2
+ from .io import export_namespace, import_namespace
3
+ from .namespaces import list_namespaces, delete_namespace, rename_namespace, namespace_stats
4
+ from .audit import log_recall, log_learn, get_audit_log, get_audit_stats
5
+ from .context_builder import build_context
6
+ from .digest import generate_digest
7
+ from .passive import extract_memories_from_log
8
+
9
+ __all__ = ["Mesh", "export_namespace", "import_namespace", "list_namespaces", "delete_namespace", "rename_namespace", "namespace_stats", "log_recall", "log_learn", "get_audit_log", "get_audit_stats", "build_context", "generate_digest", "extract_memories_from_log"]
10
+ __version__ = "0.4.0"
mesh/audit.py ADDED
@@ -0,0 +1,189 @@
1
+ """
2
+ mesh/audit.py — Audit trail for all memory reads and writes.
3
+ Logs every recall() and learn() call with timestamp, agent, query/content, and result.
4
+ """
5
+
6
+ import sqlite3
7
+ import json
8
+ from datetime import datetime, timezone
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ MESH_DIR = Path.home() / ".mesh"
13
+
14
+
15
+ def _get_audit_db_path(namespace: str) -> Path:
16
+ return MESH_DIR / f"audit_{namespace}.db"
17
+
18
+
19
+ def _init_audit_db(namespace: str):
20
+ db_path = _get_audit_db_path(namespace)
21
+ MESH_DIR.mkdir(exist_ok=True)
22
+ conn = sqlite3.connect(db_path)
23
+ cursor = conn.cursor()
24
+ cursor.execute("""
25
+ CREATE TABLE IF NOT EXISTS audit_log (
26
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
27
+ timestamp TEXT NOT NULL,
28
+ action TEXT NOT NULL, -- 'recall' or 'learn'
29
+ agent_id TEXT,
30
+ namespace TEXT,
31
+ query TEXT, -- for recalls: the query string
32
+ content TEXT, -- for learns: the content stored
33
+ top_result TEXT, -- for recalls: the top result content
34
+ top_similarity REAL, -- for recalls: top result similarity
35
+ result_count INTEGER, -- for recalls: number of results
36
+ memory_id TEXT, -- for learns: the stored memory ID
37
+ had_conflict INTEGER DEFAULT 0 -- for learns: 1 if contradiction detected
38
+ )
39
+ """)
40
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_timestamp ON audit_log(timestamp)")
41
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_action ON audit_log(action)")
42
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_agent ON audit_log(agent_id)")
43
+ conn.commit()
44
+ conn.close()
45
+
46
+
47
+ def log_recall(
48
+ query: str,
49
+ namespace: str,
50
+ agent_id: str,
51
+ results: list
52
+ ):
53
+ """Log a recall event to the audit trail."""
54
+ try:
55
+ _init_audit_db(namespace)
56
+ db_path = _get_audit_db_path(namespace)
57
+ conn = sqlite3.connect(db_path)
58
+ cursor = conn.cursor()
59
+
60
+ top_result = results[0]["content"] if results else None
61
+ top_similarity = results[0].get("similarity", 0.0) if results else None
62
+
63
+ cursor.execute("""
64
+ INSERT INTO audit_log
65
+ (timestamp, action, agent_id, namespace, query, top_result, top_similarity, result_count)
66
+ VALUES (?, 'recall', ?, ?, ?, ?, ?, ?)
67
+ """, (
68
+ datetime.now(timezone.utc).isoformat(),
69
+ agent_id,
70
+ namespace,
71
+ query,
72
+ top_result,
73
+ top_similarity,
74
+ len(results)
75
+ ))
76
+ conn.commit()
77
+ conn.close()
78
+ except Exception:
79
+ pass # Audit logging must never crash the main flow
80
+
81
+
82
+ def log_learn(
83
+ content: str,
84
+ namespace: str,
85
+ agent_id: str,
86
+ result: dict
87
+ ):
88
+ """Log a learn event to the audit trail."""
89
+ try:
90
+ _init_audit_db(namespace)
91
+ db_path = _get_audit_db_path(namespace)
92
+ conn = sqlite3.connect(db_path)
93
+ cursor = conn.cursor()
94
+
95
+ cursor.execute("""
96
+ INSERT INTO audit_log
97
+ (timestamp, action, agent_id, namespace, content, memory_id, had_conflict)
98
+ VALUES (?, 'learn', ?, ?, ?, ?, ?)
99
+ """, (
100
+ datetime.now(timezone.utc).isoformat(),
101
+ agent_id,
102
+ namespace,
103
+ content,
104
+ result.get("memory_id"),
105
+ 1 if result.get("conflict") else 0
106
+ ))
107
+ conn.commit()
108
+ conn.close()
109
+ except Exception:
110
+ pass
111
+
112
+
113
+ def get_audit_log(
114
+ namespace: str,
115
+ limit: int = 50,
116
+ action_filter: str = "all"
117
+ ) -> list[dict]:
118
+ """
119
+ Return recent audit entries for a namespace, most recent first.
120
+
121
+ Args:
122
+ namespace: The namespace to query.
123
+ limit: Max entries to return.
124
+ action_filter: "all", "read" (maps to recall), or "write" (maps to learn).
125
+ """
126
+ db_path = _get_audit_db_path(namespace)
127
+ if not db_path.exists():
128
+ return []
129
+
130
+ conn = sqlite3.connect(db_path)
131
+ conn.row_factory = sqlite3.Row
132
+ cursor = conn.cursor()
133
+
134
+ action_map = {"read": "recall", "write": "learn"}
135
+ action_sql = action_map.get(action_filter)
136
+
137
+ if action_sql:
138
+ cursor.execute("""
139
+ SELECT * FROM audit_log
140
+ WHERE action = ?
141
+ ORDER BY timestamp DESC
142
+ LIMIT ?
143
+ """, (action_sql, limit))
144
+ else:
145
+ cursor.execute("""
146
+ SELECT * FROM audit_log
147
+ ORDER BY timestamp DESC
148
+ LIMIT ?
149
+ """, (limit,))
150
+
151
+ rows = cursor.fetchall()
152
+ conn.close()
153
+ return [dict(r) for r in rows]
154
+
155
+
156
+ def get_audit_stats(namespace: str) -> dict:
157
+ """Return aggregate stats from the audit log."""
158
+ db_path = _get_audit_db_path(namespace)
159
+ if not db_path.exists():
160
+ return {"total_recalls": 0, "total_learns": 0, "agents": [], "conflicts_detected": 0}
161
+
162
+ conn = sqlite3.connect(db_path)
163
+ conn.row_factory = sqlite3.Row
164
+ cursor = conn.cursor()
165
+
166
+ cursor.execute("SELECT COUNT(*) as c FROM audit_log WHERE action = 'recall'")
167
+ recalls = cursor.fetchone()["c"]
168
+
169
+ cursor.execute("SELECT COUNT(*) as c FROM audit_log WHERE action = 'learn'")
170
+ learns = cursor.fetchone()["c"]
171
+
172
+ cursor.execute("SELECT DISTINCT agent_id FROM audit_log WHERE agent_id IS NOT NULL")
173
+ agents = [r["agent_id"] for r in cursor.fetchall()]
174
+
175
+ cursor.execute("SELECT COUNT(*) as c FROM audit_log WHERE had_conflict = 1")
176
+ conflicts = cursor.fetchone()["c"]
177
+
178
+ cursor.execute("SELECT MAX(timestamp) as last FROM audit_log")
179
+ last = cursor.fetchone()["last"]
180
+
181
+ conn.close()
182
+
183
+ return {
184
+ "total_recalls": recalls,
185
+ "total_learns": learns,
186
+ "agents": agents,
187
+ "conflicts_detected": conflicts,
188
+ "last_activity": last
189
+ }
mesh/cli.py ADDED
@@ -0,0 +1,321 @@
1
+ """
2
+ mesh/cli.py — Command-line interface for Mesh.
3
+ """
4
+
5
+ import sys
6
+ import json
7
+ import argparse
8
+
9
+
10
+ def export_cmd():
11
+ """Entry point for: mesh-export"""
12
+ parser = argparse.ArgumentParser(
13
+ description="Export Mesh memories to a JSON file.",
14
+ epilog="Example: mesh-export backup.json --namespace work"
15
+ )
16
+ parser.add_argument("output", help="Output file path (e.g. backup.json)")
17
+ parser.add_argument("--namespace", default="shared", help="Namespace to export (default: shared)")
18
+ parser.add_argument("--no-embeddings", action="store_true",
19
+ help="Exclude embedding vectors (smaller file, human-readable)")
20
+ args = parser.parse_args()
21
+
22
+ from .io import export_namespace
23
+ export_namespace(
24
+ namespace=args.namespace,
25
+ output_path=args.output,
26
+ include_embeddings=not args.no_embeddings
27
+ )
28
+
29
+
30
+ def import_cmd():
31
+ """Entry point for: mesh-import"""
32
+ parser = argparse.ArgumentParser(
33
+ description="Import Mesh memories from a JSON export file.",
34
+ epilog="Example: mesh-import backup.json --namespace work"
35
+ )
36
+ parser.add_argument("input", help="Input export file path")
37
+ parser.add_argument("--namespace", default=None,
38
+ help="Override target namespace (default: use namespace from export file)")
39
+ parser.add_argument("--overwrite", action="store_true",
40
+ help="Overwrite existing memories with same ID (default: skip)")
41
+ parser.add_argument("--dry-run", action="store_true",
42
+ help="Preview what would be imported without writing anything")
43
+ args = parser.parse_args()
44
+
45
+ from .io import import_namespace
46
+ import_namespace(
47
+ input_path=args.input,
48
+ target_namespace=args.namespace,
49
+ merge_strategy="overwrite" if args.overwrite else "skip_existing",
50
+ dry_run=args.dry_run
51
+ )
52
+
53
+
54
+ def namespaces_cmd():
55
+ """Entry point for: mesh-namespaces"""
56
+ parser = argparse.ArgumentParser(
57
+ description="Manage Mesh namespaces.",
58
+ epilog="""Commands:
59
+ mesh-namespaces list List all namespaces
60
+ mesh-namespaces delete work Delete namespace 'work' and all its memories
61
+ mesh-namespaces rename work work-old Rename namespace 'work' to 'work-old'
62
+ mesh-namespaces stats work Show stats for namespace 'work'
63
+ """
64
+ )
65
+ parser.add_argument("command", choices=["list", "delete", "rename", "stats"])
66
+ parser.add_argument("args", nargs="*", help="Arguments for the command")
67
+ parsed = parser.parse_args()
68
+
69
+ from .namespaces import list_namespaces, delete_namespace, rename_namespace, namespace_stats
70
+
71
+ if parsed.command == "list":
72
+ namespaces = list_namespaces()
73
+ if not namespaces:
74
+ print("No namespaces found.")
75
+ return
76
+ print(f"{'Namespace':<30} {'Memories':>10} {'Local-only':>12} {'Last updated':<25}")
77
+ print("-" * 80)
78
+ for ns in namespaces:
79
+ print(f"{ns['namespace']:<30} {ns['memory_count']:>10} {ns['local_only_count']:>12} {ns['last_updated']:<25}")
80
+
81
+ elif parsed.command == "delete":
82
+ if not parsed.args:
83
+ print("Error: provide a namespace name. Example: mesh-namespaces delete work")
84
+ sys.exit(1)
85
+ name = parsed.args[0]
86
+ confirm = input(f"Delete ALL memories in namespace '{name}'? This cannot be undone. [y/N] ")
87
+ if confirm.lower() != "y":
88
+ print("Cancelled.")
89
+ return
90
+ count = delete_namespace(name)
91
+ print(f"✓ Deleted namespace '{name}' ({count} memories removed)")
92
+
93
+ elif parsed.command == "rename":
94
+ if len(parsed.args) < 2:
95
+ print("Error: provide old and new names. Example: mesh-namespaces rename work work-archived")
96
+ sys.exit(1)
97
+ old, new = parsed.args[0], parsed.args[1]
98
+ count = rename_namespace(old, new)
99
+ print(f"✓ Renamed '{old}' → '{new}' ({count} memories updated)")
100
+
101
+ elif parsed.command == "stats":
102
+ if not parsed.args:
103
+ print("Error: provide a namespace name. Example: mesh-namespaces stats work")
104
+ sys.exit(1)
105
+ name = parsed.args[0]
106
+ stats = namespace_stats(name)
107
+ print(f"\nNamespace: {name}")
108
+ print(f" Total memories: {stats['memory_count']}")
109
+ print(f" Local-only: {stats['local_only_count']}")
110
+ print(f" Exportable: {stats['exportable_count']}")
111
+ print(f" Memory types: {json.dumps(stats['type_breakdown'], indent=4)}")
112
+ print(f" Avg confidence: {stats['avg_confidence']:.2f}")
113
+ print(f" Stale memories: {stats['stale_count']}")
114
+ print(f" Total recalls: {stats['total_recalls']}")
115
+ print(f" Created: {stats['oldest_memory']}")
116
+ print(f" Last updated: {stats['newest_memory']}")
117
+
118
+
119
+ def context_cmd():
120
+ """Entry point for: mesh-context"""
121
+ import argparse
122
+ parser = argparse.ArgumentParser(
123
+ description="Output Mesh memories as a formatted system prompt block.",
124
+ epilog="""Examples:
125
+ mesh-context # Top 10 memories, markdown format
126
+ mesh-context --query "deploy process" # Focused on deploy memories
127
+ mesh-context --format plain --count 5 # Plain text, 5 memories
128
+ CONTEXT=$(mesh-context) && echo $CONTEXT # Capture for use in scripts
129
+
130
+ Pipe directly into agent invocations:
131
+ aider --system-prompt "$(mesh-context)" ...
132
+ sgpt --system "$(mesh-context)" "help me with the deploy"
133
+ """
134
+ )
135
+ parser.add_argument("--query", default=None, help="Focus context on a specific topic")
136
+ parser.add_argument("--namespace", default=None, help="Namespace to query (default: shared)")
137
+ parser.add_argument("--count", type=int, default=10, help="Number of memories to include")
138
+ parser.add_argument("--format", choices=["markdown", "plain", "xml", "json"], default="markdown")
139
+ parser.add_argument("--min-confidence", type=float, default=0.3)
140
+ args = parser.parse_args()
141
+
142
+ from .context_builder import build_context
143
+ result = build_context(
144
+ query=args.query,
145
+ namespace=args.namespace,
146
+ count=args.count,
147
+ format=args.format,
148
+ min_confidence=args.min_confidence
149
+ )
150
+ if result:
151
+ print(result)
152
+ else:
153
+ import sys
154
+ print("No memories found.", file=sys.stderr)
155
+ sys.exit(1)
156
+
157
+
158
+ def add_cmd():
159
+ """Entry point for: mesh-add"""
160
+ import argparse
161
+ parser = argparse.ArgumentParser(
162
+ description="Store a memory directly into Mesh.",
163
+ epilog="Example: mesh-add 'prod DB password rotated on May 1' --type fact --tags db,security"
164
+ )
165
+ parser.add_argument("content", help="The memory to store")
166
+ parser.add_argument("--type", dest="memory_type", default="general",
167
+ choices=["fact", "process", "decision", "error", "general"],
168
+ help="Memory type (default: general)")
169
+ parser.add_argument("--tags", default="", help="Comma-separated tags")
170
+ parser.add_argument("--private", action="store_true",
171
+ help="Mark as local-only (never synced or exported)")
172
+ parser.add_argument("--namespace", default=None)
173
+ parser.add_argument("--confidence", type=float, default=1.0)
174
+ parser.add_argument("--ttl", type=int, default=None,
175
+ help="Days before this memory is considered stale")
176
+ args = parser.parse_args()
177
+
178
+ from .memory import Mesh
179
+ import os
180
+ ns = args.namespace or os.environ.get("MESH_NAMESPACE", "shared")
181
+ mesh = Mesh(namespace=ns, agent_id="human-cli")
182
+
183
+ tags = [t.strip() for t in args.tags.split(",") if t.strip()] if args.tags else []
184
+
185
+ result = mesh.learn(
186
+ content=args.content,
187
+ memory_type=args.memory_type,
188
+ confidence=args.confidence,
189
+ tags=tags,
190
+ local_only=args.private,
191
+ ttl_days=args.ttl
192
+ )
193
+
194
+ private_note = " (private — will not be exported or synced)" if args.private else ""
195
+ conflict_note = ""
196
+ if result.get("conflict"):
197
+ conflict_note = f"\n⚠️ Possible conflict with existing memory:\n \"{result['conflict']['existing_memory']}\""
198
+
199
+ print(f"✓ Stored [{args.memory_type}] in namespace '{ns}'{private_note}{conflict_note}")
200
+ if result.get("memory_id"):
201
+ print(f" ID: {result['memory_id']}")
202
+
203
+
204
+ def ask_cmd():
205
+ """Entry point for: mesh-ask"""
206
+ import argparse
207
+ parser = argparse.ArgumentParser(
208
+ description="Search Mesh for memories relevant to a question.",
209
+ epilog="Example: mesh-ask 'what is the deploy process?'"
210
+ )
211
+ parser.add_argument("query", help="What to search for")
212
+ parser.add_argument("--count", type=int, default=5)
213
+ parser.add_argument("--namespace", default=None)
214
+ parser.add_argument("--type", dest="memory_type", default=None)
215
+ parser.add_argument("--min-confidence", type=float, default=0.0)
216
+ args = parser.parse_args()
217
+
218
+ from .memory import Mesh
219
+ import os
220
+ ns = args.namespace or os.environ.get("MESH_NAMESPACE", "shared")
221
+ mesh = Mesh(namespace=ns, agent_id="human-cli")
222
+
223
+ results = mesh.recall(
224
+ query=args.query,
225
+ count=args.count,
226
+ memory_type=args.memory_type,
227
+ min_confidence=args.min_confidence
228
+ )
229
+
230
+ if not results:
231
+ print(f"No memories found for: {args.query}")
232
+ return
233
+
234
+ print(f"\nFound {len(results)} memories in namespace '{ns}':\n")
235
+ for i, mem in enumerate(results, 1):
236
+ sim = mem.get("similarity", 0)
237
+ conf = mem.get("effective_confidence", mem.get("confidence", 1.0))
238
+ stale = " [STALE]" if mem.get("is_stale") else ""
239
+ private = " [PRIVATE]" if mem.get("local_only") else ""
240
+ tags = f" #{' #'.join(mem['tags'])}" if mem.get("tags") else ""
241
+
242
+ print(f" {i}. {mem['content']}{stale}{private}")
243
+ print(f" type={mem.get('memory_type','general')} | "
244
+ f"similarity={sim:.2f} | confidence={conf:.2f} | "
245
+ f"from={mem.get('source_agent','unknown')}{tags}")
246
+ print()
247
+
248
+
249
+ def why_cmd():
250
+ """Entry point for: mesh-why"""
251
+ import argparse
252
+ parser = argparse.ArgumentParser(
253
+ description="Show recent memory reads and writes across all agents (audit trail).",
254
+ epilog="Example: mesh-why --limit 30 --namespace work"
255
+ )
256
+ parser.add_argument("--limit", type=int, default=20)
257
+ parser.add_argument("--namespace", default=None)
258
+ parser.add_argument("--action", choices=["read", "write", "all"], default="all")
259
+ args = parser.parse_args()
260
+
261
+ from .audit import get_audit_log
262
+ import os
263
+ ns = args.namespace or os.environ.get("MESH_NAMESPACE", "shared")
264
+ entries = get_audit_log(namespace=ns, limit=args.limit, action_filter=args.action)
265
+
266
+ if not entries:
267
+ print(f"No audit entries found for namespace '{ns}'.")
268
+ return
269
+
270
+ print(f"\nAudit trail for namespace '{ns}' (most recent first):\n")
271
+ for entry in entries:
272
+ action_icon = "📖" if entry["action"] == "recall" else "✏️"
273
+ print(f" {action_icon} [{entry['timestamp'][:19]}] {entry['agent_id']}")
274
+ if entry["action"] == "recall":
275
+ print(f" asked: \"{entry['query']}\"")
276
+ if entry.get("top_result"):
277
+ print(f" got: \"{entry['top_result'][:80]}...\""
278
+ if len(entry.get("top_result","")) > 80
279
+ else f" got: \"{entry['top_result']}\"")
280
+ else:
281
+ print(f" stored: \"{entry['content'][:80]}\"")
282
+ print()
283
+
284
+
285
+ def status_cmd():
286
+ """Entry point for: mesh-status"""
287
+ import argparse
288
+ parser = argparse.ArgumentParser(description="Show Mesh status overview.")
289
+ parser.add_argument("--namespace", default=None)
290
+ args = parser.parse_args()
291
+
292
+ from .namespaces import list_namespaces, namespace_stats
293
+ from .audit import get_audit_log
294
+ import os
295
+
296
+ ns = args.namespace or os.environ.get("MESH_NAMESPACE", "shared")
297
+
298
+ try:
299
+ stats = namespace_stats(ns)
300
+ except ValueError:
301
+ print(f"Namespace '{ns}' is empty or does not exist yet.")
302
+ print(f"Run 'mesh-add \"something\"' to create it.")
303
+ return
304
+
305
+ recent = get_audit_log(namespace=ns, limit=5)
306
+ last_activity = recent[0]["timestamp"][:19] if recent else "never"
307
+
308
+ print(f"\nMesh Status — namespace: {ns}\n")
309
+ print(f" Memories: {stats['memory_count']} total, "
310
+ f"{stats['local_only_count']} private, "
311
+ f"{stats['stale_count']} stale")
312
+ print(f" Exportable: {stats['exportable_count']}")
313
+ print(f" Total recalls: {stats['total_recalls']}")
314
+ print(f" Last activity: {last_activity}")
315
+ print(f" Types: {stats['type_breakdown']}")
316
+
317
+ all_ns = list_namespaces()
318
+ if len(all_ns) > 1:
319
+ others = [n["namespace"] for n in all_ns if n["namespace"] != ns]
320
+ print(f" Other namespaces: {', '.join(others)}")
321
+ print()
@@ -0,0 +1,96 @@
1
+ """
2
+ mesh/context_builder.py — Builds formatted system prompt context blocks from Mesh memories.
3
+ """
4
+
5
+ import os
6
+ from typing import Optional
7
+ from .memory import Mesh
8
+
9
+
10
+ def build_context(
11
+ query: Optional[str] = None,
12
+ namespace: Optional[str] = None,
13
+ count: int = 10,
14
+ format: str = "markdown",
15
+ min_confidence: float = 0.3,
16
+ agent_id: str = "context-builder"
17
+ ) -> str:
18
+ """
19
+ Build a formatted context block from the most relevant memories.
20
+
21
+ Args:
22
+ query: Optional query to focus the context. If None, pulls top memories by access frequency.
23
+ namespace: Namespace to query. Defaults to MESH_NAMESPACE env var or "shared".
24
+ count: Number of memories to include. Default 10.
25
+ format: Output format — "markdown", "plain", "xml", or "json".
26
+ min_confidence: Minimum confidence score to include. Default 0.3.
27
+ agent_id: Agent ID to log this context pull under.
28
+
29
+ Returns:
30
+ Formatted string ready to paste into a system prompt.
31
+ """
32
+ ns = namespace or os.environ.get("MESH_NAMESPACE", "shared")
33
+ mesh = Mesh(namespace=ns, agent_id=agent_id)
34
+
35
+ if query:
36
+ memories = mesh.recall(query=query, n=count, min_confidence=min_confidence)
37
+ else:
38
+ # No query: pull most-accessed memories as general context
39
+ memories = mesh.inspect(limit=count * 3) # get more, then sort
40
+ memories = sorted(memories, key=lambda m: m.get("access_count", 0), reverse=True)
41
+ memories = [m for m in memories if m.get("confidence", 1.0) >= min_confidence][:count]
42
+
43
+ if not memories:
44
+ return ""
45
+
46
+ return _format(memories, format, ns)
47
+
48
+
49
+ def _format(memories: list, format: str, namespace: str) -> str:
50
+ if format == "json":
51
+ import json
52
+ return json.dumps({
53
+ "mesh_context": True,
54
+ "namespace": namespace,
55
+ "memories": memories
56
+ }, indent=2)
57
+
58
+ if format == "xml":
59
+ lines = ["<mesh_context>"]
60
+ for mem in memories:
61
+ stale = ' stale="true"' if mem.get("is_stale") else ""
62
+ lines.append(f' <memory type="{mem.get("memory_type", "general")}"{stale}>')
63
+ lines.append(f' {mem["content"]}')
64
+ lines.append(f' </memory>')
65
+ lines.append("</mesh_context>")
66
+ return "\n".join(lines)
67
+
68
+ if format == "plain":
69
+ lines = ["[MESH CONTEXT]"]
70
+ for mem in memories:
71
+ stale = " (possibly stale)" if mem.get("is_stale") else ""
72
+ lines.append(f"- {mem['content']}{stale}")
73
+ return "\n".join(lines)
74
+
75
+ # Default: markdown (best for LLM system prompts)
76
+ lines = [
77
+ "<mesh_context>",
78
+ "The following facts were stored by AI agents working on this project.",
79
+ "Trust these as ground truth unless you have newer information.",
80
+ "",
81
+ ]
82
+
83
+ by_type: dict = {}
84
+ for mem in memories:
85
+ t = mem.get("memory_type", "general")
86
+ by_type.setdefault(t, []).append(mem)
87
+
88
+ for mem_type, mems in sorted(by_type.items()):
89
+ lines.append(f"**{mem_type.title()}**")
90
+ for mem in mems:
91
+ stale = " *(possibly stale)*" if mem.get("is_stale") else ""
92
+ lines.append(f"- {mem['content']}{stale}")
93
+ lines.append("")
94
+
95
+ lines.append("</mesh_context>")
96
+ return "\n".join(lines)