supermemory-agent 0.2.3__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.
Files changed (54) hide show
  1. storage/adapters/__init__.py +0 -0
  2. storage/adapters/file.py +397 -0
  3. storage/adapters/postgres_qdrant_redis.py +32 -0
  4. storage/adapters/sqlite_chroma.py +95 -0
  5. supermemory_agent-0.2.3.dist-info/METADATA +170 -0
  6. supermemory_agent-0.2.3.dist-info/RECORD +54 -0
  7. supermemory_agent-0.2.3.dist-info/WHEEL +4 -0
  8. supermemory_agent-0.2.3.dist-info/entry_points.txt +2 -0
  9. supermemory_agent-0.2.3.dist-info/licenses/LICENSE +21 -0
  10. supermemory_mcp/__init__.py +5 -0
  11. supermemory_mcp/bridge.py +35 -0
  12. supermemory_mcp/handlers.py +772 -0
  13. supermemory_mcp/server.py +522 -0
  14. supermemory_mcp/text.py +16 -0
  15. uall/__init__.py +0 -0
  16. uall/analytics/service.py +35 -0
  17. uall/collector/__init__.py +0 -0
  18. uall/collector/service.py +100 -0
  19. uall/distillation/distiller.py +80 -0
  20. uall/evaluation/engine.py +38 -0
  21. uall/experiments/manager.py +83 -0
  22. uall/memory/__init__.py +0 -0
  23. uall/memory/confidence.py +36 -0
  24. uall/memory/freshness.py +28 -0
  25. uall/memory/graph.py +24 -0
  26. uall/memory/namespaces.py +40 -0
  27. uall/memory/policies.py +44 -0
  28. uall/memory/provenance.py +23 -0
  29. uall/memory/pruning.py +55 -0
  30. uall/memory/retrieval.py +98 -0
  31. uall/memory/ttl.py +22 -0
  32. uall/memory/validator.py +144 -0
  33. uall/optimization/optimizers.py +59 -0
  34. uall/promotion/queue.py +107 -0
  35. uall/recommendations/engine.py +66 -0
  36. uall/reflection/engine.py +72 -0
  37. uall/rollback/manager.py +49 -0
  38. uall/service.py +572 -0
  39. uall/skills/library.py +19 -0
  40. uall/telemetry/retrieval.py +40 -0
  41. uall_core/__init__.py +0 -0
  42. uall_core/ports/storage.py +71 -0
  43. uall_core/providers/heuristic.py +58 -0
  44. uall_core/providers/llm.py +6 -0
  45. uall_core/schemas/__init__.py +0 -0
  46. uall_core/schemas/common.py +73 -0
  47. uall_core/schemas/events.py +75 -0
  48. uall_core/schemas/graph.py +32 -0
  49. uall_core/schemas/lesson.py +109 -0
  50. uall_core/schemas/namespace.py +76 -0
  51. uall_python/__init__.py +3 -0
  52. uall_python/client.py +337 -0
  53. uall_server/__init__.py +0 -0
  54. uall_server/main.py +284 -0
@@ -0,0 +1,772 @@
1
+ """Unified MCP tool handlers — GitHub-compatible + extended learn.* tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import uuid
7
+
8
+ from supermemory_mcp.bridge import SuperMemoryBridge
9
+ from uall_core.schemas.common import PolicyVersion, Skill
10
+ from uall_core.schemas.events import Event, EventType, Feedback, RunEnd, RunStart, StageMetadata
11
+ from uall_core.schemas.lesson import CandidateLesson, Lesson, MemorySearchRequest
12
+
13
+ _bridge: SuperMemoryBridge | None = None
14
+ _data_dir: str | None = None
15
+
16
+
17
+ def _get_bridge(data_dir: str | None = None) -> SuperMemoryBridge:
18
+ global _bridge, _data_dir
19
+ if data_dir and data_dir != _data_dir:
20
+ _bridge = SuperMemoryBridge(data_dir)
21
+ _data_dir = data_dir
22
+ if _bridge is None:
23
+ _bridge = SuperMemoryBridge(data_dir)
24
+ _data_dir = data_dir
25
+ return _bridge
26
+
27
+
28
+ def reset_service() -> None:
29
+ global _bridge, _data_dir
30
+ _bridge = None
31
+ _data_dir = None
32
+
33
+
34
+ async def get_service(data_dir: str | None = None):
35
+ bridge = _get_bridge(data_dir)
36
+ return await bridge.service()
37
+
38
+
39
+ def _parse_namespace(namespace: str | None) -> tuple[str | None, str | None]:
40
+ if namespace and ":" in namespace:
41
+ level, ns_id = namespace.split(":", 1)
42
+ return level, ns_id
43
+ if namespace == "global":
44
+ return "global", None
45
+ return None, namespace
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Tool registry
50
+ # ---------------------------------------------------------------------------
51
+
52
+ GITHUB_TOOLS = [
53
+ {
54
+ "name": "retrieve",
55
+ "description": "Retrieve policy-first, stage-aware lessons.",
56
+ "inputSchema": {
57
+ "type": "object",
58
+ "properties": {
59
+ "query": {"type": "string"},
60
+ "workflow": {"type": "string"},
61
+ "step": {"type": "string"},
62
+ "tool": {"type": "string"},
63
+ "agent": {"type": "string"},
64
+ "domain": {"type": "string"},
65
+ "language": {"type": "string"},
66
+ "environment": {"type": "string"},
67
+ "namespace": {"type": "string", "default": "global"},
68
+ "top_k": {"type": "integer", "default": 5},
69
+ "max_tokens": {"type": "integer", "default": 800},
70
+ },
71
+ "required": ["query"],
72
+ },
73
+ },
74
+ {
75
+ "name": "record_event",
76
+ "description": "Record a bounded significant event. Full transcripts should not be sent here.",
77
+ "inputSchema": {
78
+ "type": "object",
79
+ "properties": {
80
+ "event_type": {"type": "string"},
81
+ "summary": {"type": "string"},
82
+ "workflow": {"type": "string"},
83
+ "step": {"type": "string"},
84
+ "tool": {"type": "string"},
85
+ "agent": {"type": "string"},
86
+ "domain": {"type": "string"},
87
+ "language": {"type": "string"},
88
+ "environment": {"type": "string"},
89
+ "namespace": {"type": "string", "default": "global"},
90
+ "payload": {"type": "object"},
91
+ },
92
+ "required": ["event_type", "summary"],
93
+ },
94
+ },
95
+ {
96
+ "name": "record_failure",
97
+ "description": "Record a bounded failure signal.",
98
+ "inputSchema": {
99
+ "type": "object",
100
+ "properties": {
101
+ "summary": {"type": "string"},
102
+ "workflow": {"type": "string"},
103
+ "step": {"type": "string"},
104
+ "tool": {"type": "string"},
105
+ "agent": {"type": "string"},
106
+ "domain": {"type": "string"},
107
+ "language": {"type": "string"},
108
+ "environment": {"type": "string"},
109
+ "namespace": {"type": "string", "default": "global"},
110
+ "payload": {"type": "object"},
111
+ },
112
+ "required": ["summary"],
113
+ },
114
+ },
115
+ {
116
+ "name": "record_correction",
117
+ "description": "Record a bounded correction signal.",
118
+ "inputSchema": {
119
+ "type": "object",
120
+ "properties": {
121
+ "summary": {"type": "string"},
122
+ "workflow": {"type": "string"},
123
+ "step": {"type": "string"},
124
+ "tool": {"type": "string"},
125
+ "agent": {"type": "string"},
126
+ "domain": {"type": "string"},
127
+ "language": {"type": "string"},
128
+ "environment": {"type": "string"},
129
+ "namespace": {"type": "string", "default": "global"},
130
+ "payload": {"type": "object"},
131
+ },
132
+ "required": ["summary"],
133
+ },
134
+ },
135
+ {
136
+ "name": "reflect",
137
+ "description": "Create a candidate lesson from evidence events.",
138
+ "inputSchema": {
139
+ "type": "object",
140
+ "properties": {
141
+ "event_ids": {"type": "array", "items": {"type": "string"}},
142
+ "suggestion": {"type": "string"},
143
+ "lesson_text": {"type": "string"},
144
+ },
145
+ "required": ["event_ids"],
146
+ },
147
+ },
148
+ {
149
+ "name": "validate",
150
+ "description": "Validate a lesson candidate and enqueue approved lessons for promotion.",
151
+ "inputSchema": {
152
+ "type": "object",
153
+ "properties": {
154
+ "reflection_id": {"type": "string"},
155
+ "candidate_lesson": {"type": "string"},
156
+ "event_ids": {"type": "array", "items": {"type": "string"}},
157
+ "metadata": {"type": "object"},
158
+ },
159
+ },
160
+ },
161
+ {
162
+ "name": "process_promotions",
163
+ "description": "Process pending validated lessons and promote passing items.",
164
+ "inputSchema": {
165
+ "type": "object",
166
+ "properties": {"limit": {"type": "integer", "default": 50}},
167
+ },
168
+ },
169
+ {
170
+ "name": "report_outcome",
171
+ "description": "Report telemetry for a retrieved lesson.",
172
+ "inputSchema": {
173
+ "type": "object",
174
+ "properties": {
175
+ "lesson_id": {"type": "string"},
176
+ "used": {"type": "boolean"},
177
+ "accepted": {"type": "boolean"},
178
+ "improved": {"type": "boolean"},
179
+ "retrieval_id": {"type": "string"},
180
+ "run_id": {"type": "string"},
181
+ },
182
+ "required": ["lesson_id", "used", "accepted", "improved"],
183
+ },
184
+ },
185
+ {
186
+ "name": "get_policies",
187
+ "description": "Return active policies visible to a namespace.",
188
+ "inputSchema": {
189
+ "type": "object",
190
+ "properties": {"namespace": {"type": "string", "default": "global"}},
191
+ },
192
+ },
193
+ {
194
+ "name": "add_policy",
195
+ "description": "Add a local policy rule.",
196
+ "inputSchema": {
197
+ "type": "object",
198
+ "properties": {
199
+ "rule": {"type": "string"},
200
+ "namespace": {"type": "string", "default": "global"},
201
+ "priority": {"type": "integer", "default": 100},
202
+ },
203
+ "required": ["rule"],
204
+ },
205
+ },
206
+ {
207
+ "name": "add_skill",
208
+ "description": "Add a reusable skill/workflow block.",
209
+ "inputSchema": {
210
+ "type": "object",
211
+ "properties": {
212
+ "name": {"type": "string"},
213
+ "description": {"type": "string"},
214
+ "steps": {"type": "array", "items": {"type": "string"}},
215
+ "workflow": {"type": "string"},
216
+ "tools": {"type": "array", "items": {"type": "string"}},
217
+ "namespace": {"type": "string", "default": "global"},
218
+ "version": {"type": "string", "default": "0.1.0"},
219
+ "metadata": {"type": "object"},
220
+ },
221
+ "required": ["name", "description", "steps"],
222
+ },
223
+ },
224
+ {
225
+ "name": "search_skills",
226
+ "description": "Search reusable skills visible to a namespace.",
227
+ "inputSchema": {
228
+ "type": "object",
229
+ "properties": {
230
+ "query": {"type": "string"},
231
+ "workflow": {"type": "string"},
232
+ "namespace": {"type": "string", "default": "global"},
233
+ "top_k": {"type": "integer", "default": 5},
234
+ },
235
+ "required": ["query"],
236
+ },
237
+ },
238
+ {
239
+ "name": "get_skill",
240
+ "description": "Read a reusable skill by ID.",
241
+ "inputSchema": {
242
+ "type": "object",
243
+ "properties": {"skill_id": {"type": "string"}},
244
+ "required": ["skill_id"],
245
+ },
246
+ },
247
+ ]
248
+
249
+ LEARN_TOOLS = [
250
+ {
251
+ "name": "learn.run.start",
252
+ "description": "Start a UALL run with workflow and step metadata",
253
+ "inputSchema": {
254
+ "type": "object",
255
+ "properties": {
256
+ "workflow_id": {"type": "string"},
257
+ "step": {"type": "string"},
258
+ "agents": {"type": "array", "items": {"type": "string"}},
259
+ "namespace": {"type": "string"},
260
+ "agent": {"type": "string"},
261
+ },
262
+ "required": ["workflow_id"],
263
+ },
264
+ },
265
+ {
266
+ "name": "learn.run.event",
267
+ "description": "Record a selective event (failure, correction, suggestion, workflow_step)",
268
+ "inputSchema": {
269
+ "type": "object",
270
+ "properties": {
271
+ "run_id": {"type": "string"},
272
+ "event_type": {"type": "string"},
273
+ "snippet": {"type": "string"},
274
+ "before": {"type": "string"},
275
+ "after": {"type": "string"},
276
+ "intent": {"type": "string"},
277
+ "workflow": {"type": "string"},
278
+ "step": {"type": "string"},
279
+ "agent": {"type": "string"},
280
+ "tags": {"type": "array", "items": {"type": "string"}},
281
+ },
282
+ "required": ["run_id", "event_type"],
283
+ },
284
+ },
285
+ {
286
+ "name": "learn.run.end",
287
+ "description": "End a UALL run with outcome and lessons used",
288
+ "inputSchema": {
289
+ "type": "object",
290
+ "properties": {
291
+ "run_id": {"type": "string"},
292
+ "success": {"type": "boolean"},
293
+ "lessons_used": {"type": "array", "items": {"type": "string"}},
294
+ },
295
+ "required": ["run_id", "success"],
296
+ },
297
+ },
298
+ {
299
+ "name": "learn.store",
300
+ "description": "Store a validated lesson (post-validation only)",
301
+ "inputSchema": {
302
+ "type": "object",
303
+ "properties": {"lesson_json": {"type": "string"}},
304
+ "required": ["lesson_json"],
305
+ },
306
+ },
307
+ {
308
+ "name": "learn.retrieve",
309
+ "description": "Retrieve lessons for a workflow step (hybrid pipeline)",
310
+ "inputSchema": {
311
+ "type": "object",
312
+ "properties": {
313
+ "query": {"type": "string"},
314
+ "workflow": {"type": "string"},
315
+ "step": {"type": "string"},
316
+ "namespace": {"type": "string"},
317
+ "max_tokens": {"type": "integer"},
318
+ },
319
+ "required": ["query"],
320
+ },
321
+ },
322
+ {
323
+ "name": "learn.reflect",
324
+ "description": "Reflect on evidence events and queue for promotion (requires event_ids)",
325
+ "inputSchema": {
326
+ "type": "object",
327
+ "properties": {
328
+ "event_ids": {"type": "array", "items": {"type": "string"}},
329
+ "suggestion": {"type": "string"},
330
+ "failure": {"type": "string"},
331
+ "root_cause": {"type": "string"},
332
+ "fix": {"type": "string"},
333
+ "workflow": {"type": "string"},
334
+ "step": {"type": "string"},
335
+ "auto_promote": {"type": "boolean", "default": False},
336
+ },
337
+ },
338
+ },
339
+ {
340
+ "name": "learn.validate",
341
+ "description": "Validate a candidate lesson without storing",
342
+ "inputSchema": {
343
+ "type": "object",
344
+ "properties": {
345
+ "failure": {"type": "string"},
346
+ "root_cause": {"type": "string"},
347
+ "fix": {"type": "string"},
348
+ "event_ids": {"type": "array", "items": {"type": "string"}},
349
+ },
350
+ "required": ["failure", "fix"],
351
+ },
352
+ },
353
+ {
354
+ "name": "learn.evaluate",
355
+ "description": "Evaluate a completed run",
356
+ "inputSchema": {
357
+ "type": "object",
358
+ "properties": {"run_id": {"type": "string"}},
359
+ "required": ["run_id"],
360
+ },
361
+ },
362
+ {
363
+ "name": "learn.feedback",
364
+ "description": "Record user feedback or correction",
365
+ "inputSchema": {
366
+ "type": "object",
367
+ "properties": {
368
+ "rating": {"type": "string"},
369
+ "comment": {"type": "string"},
370
+ "run_id": {"type": "string"},
371
+ },
372
+ "required": ["rating"],
373
+ },
374
+ },
375
+ {
376
+ "name": "learn.improvements",
377
+ "description": "Get actionable recommendations from distilled lessons",
378
+ "inputSchema": {
379
+ "type": "object",
380
+ "properties": {
381
+ "workflow_id": {"type": "string"},
382
+ "agent_id": {"type": "string"},
383
+ },
384
+ },
385
+ },
386
+ {
387
+ "name": "learn.analytics",
388
+ "description": "Get UALL analytics summary",
389
+ "inputSchema": {"type": "object", "properties": {}},
390
+ },
391
+ {
392
+ "name": "learn.policies",
393
+ "description": "Get organization policies (injected before lessons)",
394
+ "inputSchema": {"type": "object", "properties": {}},
395
+ },
396
+ {
397
+ "name": "learn.experiment",
398
+ "description": "Start an A/B experiment for prompt or workflow change",
399
+ "inputSchema": {
400
+ "type": "object",
401
+ "properties": {
402
+ "resource_id": {"type": "string"},
403
+ "variant_b": {"type": "string"},
404
+ "traffic_split": {"type": "number"},
405
+ },
406
+ "required": ["resource_id", "variant_b"],
407
+ },
408
+ },
409
+ {
410
+ "name": "learn.rollback",
411
+ "description": "Rollback a prompt, workflow, or lesson version",
412
+ "inputSchema": {
413
+ "type": "object",
414
+ "properties": {
415
+ "resource_type": {"type": "string"},
416
+ "resource_id": {"type": "string"},
417
+ "target_version": {"type": "string"},
418
+ },
419
+ "required": ["resource_type", "resource_id", "target_version"],
420
+ },
421
+ },
422
+ {
423
+ "name": "learn.skills",
424
+ "description": "Search the versioned skill library",
425
+ "inputSchema": {
426
+ "type": "object",
427
+ "properties": {"query": {"type": "string"}},
428
+ "required": ["query"],
429
+ },
430
+ },
431
+ {
432
+ "name": "learn.telemetry",
433
+ "description": "Report lesson outcome: retrieved → used → accepted → improved",
434
+ "inputSchema": {
435
+ "type": "object",
436
+ "properties": {
437
+ "lesson_id": {"type": "string"},
438
+ "run_id": {"type": "string"},
439
+ "used": {"type": "boolean"},
440
+ "accepted": {"type": "boolean"},
441
+ "improved": {"type": "boolean"},
442
+ "telemetry_id": {"type": "string"},
443
+ },
444
+ "required": ["lesson_id"],
445
+ },
446
+ },
447
+ ]
448
+
449
+ TOOLS = GITHUB_TOOLS + LEARN_TOOLS
450
+
451
+
452
+ async def handle_tool(name: str, arguments: dict, data_dir: str | None = None) -> str:
453
+ svc = await get_service(data_dir)
454
+ args = arguments or {}
455
+
456
+ # --- GitHub-compatible tools ---
457
+ if name == "record_event":
458
+ return json.dumps(
459
+ await svc.record_mcp_event(
460
+ args["event_type"],
461
+ args["summary"],
462
+ workflow=args.get("workflow"),
463
+ step=args.get("step"),
464
+ tool=args.get("tool"),
465
+ agent=args.get("agent"),
466
+ domain=args.get("domain"),
467
+ language=args.get("language"),
468
+ environment=args.get("environment"),
469
+ namespace=args.get("namespace", "global"),
470
+ payload=args.get("payload"),
471
+ )
472
+ )
473
+
474
+ if name == "record_failure":
475
+ return json.dumps(
476
+ await svc.record_mcp_event(
477
+ "failure",
478
+ args["summary"],
479
+ workflow=args.get("workflow"),
480
+ step=args.get("step"),
481
+ tool=args.get("tool"),
482
+ agent=args.get("agent"),
483
+ domain=args.get("domain"),
484
+ language=args.get("language"),
485
+ environment=args.get("environment"),
486
+ namespace=args.get("namespace", "global"),
487
+ payload=args.get("payload"),
488
+ )
489
+ )
490
+
491
+ if name == "record_correction":
492
+ return json.dumps(
493
+ await svc.record_mcp_event(
494
+ "correction",
495
+ args["summary"],
496
+ workflow=args.get("workflow"),
497
+ step=args.get("step"),
498
+ tool=args.get("tool"),
499
+ agent=args.get("agent"),
500
+ domain=args.get("domain"),
501
+ language=args.get("language"),
502
+ environment=args.get("environment"),
503
+ namespace=args.get("namespace", "global"),
504
+ payload=args.get("payload"),
505
+ )
506
+ )
507
+
508
+ if name == "reflect":
509
+ try:
510
+ return json.dumps(
511
+ await svc.reflect_from_events(
512
+ args["event_ids"],
513
+ suggestion=args.get("suggestion"),
514
+ lesson_text=args.get("lesson_text"),
515
+ )
516
+ )
517
+ except ValueError as exc:
518
+ return json.dumps({"error": str(exc)})
519
+
520
+ if name == "validate":
521
+ return json.dumps(
522
+ await svc.validate_and_enqueue(
523
+ reflection_id=args.get("reflection_id"),
524
+ candidate_lesson=args.get("candidate_lesson"),
525
+ event_ids=args.get("event_ids"),
526
+ metadata=args.get("metadata"),
527
+ )
528
+ )
529
+
530
+ if name == "process_promotions":
531
+ return json.dumps(await svc.process_promotion_queue(limit=args.get("limit", 50)))
532
+
533
+ if name == "retrieve":
534
+ return json.dumps(
535
+ await svc.retrieve_for_mcp(
536
+ args["query"],
537
+ workflow=args.get("workflow"),
538
+ step=args.get("step"),
539
+ tool=args.get("tool"),
540
+ agent=args.get("agent"),
541
+ domain=args.get("domain"),
542
+ language=args.get("language"),
543
+ environment=args.get("environment"),
544
+ namespace=args.get("namespace", "global"),
545
+ top_k=args.get("top_k", 5),
546
+ max_tokens=args.get("max_tokens", 800),
547
+ )
548
+ )
549
+
550
+ if name == "report_outcome":
551
+ return json.dumps(
552
+ await svc.report_outcome(
553
+ args["lesson_id"],
554
+ args["used"],
555
+ args["accepted"],
556
+ args["improved"],
557
+ retrieval_id=args.get("retrieval_id"),
558
+ run_id=args.get("run_id"),
559
+ )
560
+ )
561
+
562
+ if name == "get_policies":
563
+ policies = await svc.get_policies()
564
+ return json.dumps({"policies": [p.model_dump(mode="json") for p in policies]})
565
+
566
+ if name == "add_policy":
567
+ return json.dumps(
568
+ await svc.add_policy_rule(
569
+ args["rule"],
570
+ namespace=args.get("namespace", "global"),
571
+ priority=args.get("priority", 100),
572
+ )
573
+ )
574
+
575
+ if name == "add_skill":
576
+ return json.dumps(
577
+ await svc.add_mcp_skill(
578
+ args["name"],
579
+ args["description"],
580
+ args["steps"],
581
+ workflow=args.get("workflow"),
582
+ tools=args.get("tools"),
583
+ namespace=args.get("namespace", "global"),
584
+ version=args.get("version", "0.1.0"),
585
+ metadata=args.get("metadata"),
586
+ )
587
+ )
588
+
589
+ if name == "search_skills":
590
+ skills = await svc.search_mcp_skills(
591
+ args["query"],
592
+ workflow=args.get("workflow"),
593
+ namespace=args.get("namespace", "global"),
594
+ top_k=args.get("top_k", 5),
595
+ )
596
+ return json.dumps({"skills": skills})
597
+
598
+ if name == "get_skill":
599
+ skill = await svc.get_mcp_skill(args["skill_id"])
600
+ return json.dumps(skill or {"error": "not_found"})
601
+
602
+ # --- Extended learn.* tools ---
603
+ if name == "learn.run.start":
604
+ ns_level, ns_id = _parse_namespace(args.get("namespace"))
605
+ stage = StageMetadata(
606
+ workflow=args["workflow_id"],
607
+ step=args.get("step"),
608
+ agent=args.get("agent"),
609
+ namespace=ns_level,
610
+ namespace_id=ns_id,
611
+ )
612
+ run_id = args.get("run_id") or f"run_{uuid.uuid4().hex[:8]}"
613
+ start = RunStart(
614
+ run_id=run_id,
615
+ workflow_id=args["workflow_id"],
616
+ agents=args.get("agents", []),
617
+ stage=stage,
618
+ )
619
+ result = await svc.start_run(start)
620
+ result["run_id"] = run_id
621
+ return json.dumps(result)
622
+
623
+ if name == "learn.run.event":
624
+ stage = StageMetadata(
625
+ workflow=args.get("workflow"),
626
+ step=args.get("step"),
627
+ agent=args.get("agent"),
628
+ )
629
+ etype = args["event_type"]
630
+ payload: dict = {}
631
+ if etype == "failure":
632
+ payload = {"snippet": args.get("snippet", "")[:500], "summary": args.get("snippet", "")[:500]}
633
+ elif etype == "correction":
634
+ payload = {
635
+ "before": args.get("before", "")[:300],
636
+ "after": args.get("after", "")[:300],
637
+ "intent": args.get("intent", ""),
638
+ "summary": args.get("after", "")[:500],
639
+ }
640
+ elif etype == "workflow_step":
641
+ payload = {"outcome": args.get("outcome", "ok")}
642
+ event = Event(
643
+ event_id=f"{etype}_{uuid.uuid4().hex[:8]}",
644
+ event_type=EventType(etype) if etype in EventType._value2member_map_ else EventType.FAILURE,
645
+ run_id=args["run_id"],
646
+ stage=stage,
647
+ tags=args.get("tags", []),
648
+ payload=payload,
649
+ )
650
+ return json.dumps(await svc.record_event(event))
651
+
652
+ if name == "learn.run.end":
653
+ end = RunEnd(
654
+ run_id=args["run_id"],
655
+ success=args["success"],
656
+ lessons_used=args.get("lessons_used", []),
657
+ )
658
+ return json.dumps(await svc.end_run(end))
659
+
660
+ if name == "learn.store":
661
+ lesson_data = json.loads(args["lesson_json"])
662
+ lesson = Lesson(**lesson_data)
663
+ lid = await svc.store_lesson(lesson)
664
+ return json.dumps({"lesson_id": lid, "stored": True})
665
+
666
+ if name == "learn.retrieve":
667
+ result = await svc.retrieve_for_mcp(
668
+ args.get("query", ""),
669
+ workflow=args.get("workflow"),
670
+ step=args.get("step"),
671
+ namespace=args.get("namespace", "global"),
672
+ max_tokens=args.get("max_tokens", 800),
673
+ )
674
+ return json.dumps(result)
675
+
676
+ if name == "learn.reflect":
677
+ event_ids = args.get("event_ids", [])
678
+ if not event_ids:
679
+ return json.dumps({"status": "rejected", "reason": "event_ids required for evidence-first reflection"})
680
+ reflection = await svc.reflect_from_events(
681
+ event_ids,
682
+ suggestion=args.get("suggestion") or args.get("fix"),
683
+ lesson_text=args.get("fix"),
684
+ )
685
+ validation = await svc.validate_and_enqueue(
686
+ reflection_id=reflection["id"],
687
+ event_ids=event_ids,
688
+ )
689
+ if validation.get("action") == "reject":
690
+ return json.dumps({"status": "rejected", "reason": validation.get("reason"), "reflection_id": reflection["id"]})
691
+ result = {
692
+ "status": "queued",
693
+ "reflection_id": reflection["id"],
694
+ "pending_id": validation.get("pending_id"),
695
+ "validation": validation,
696
+ }
697
+ if args.get("auto_promote"):
698
+ result["promotion"] = await svc.process_promotion_queue()
699
+ return json.dumps(result)
700
+
701
+ if name == "learn.validate":
702
+ candidate = CandidateLesson(
703
+ reflection_id="",
704
+ failure=args["failure"],
705
+ root_cause=args.get("root_cause", ""),
706
+ fix=args["fix"],
707
+ evidence_payload=args,
708
+ event_ids=args.get("event_ids", []),
709
+ )
710
+ result = await svc.validate_lesson(candidate)
711
+ return json.dumps(result.model_dump(mode="json"))
712
+
713
+ if name == "learn.evaluate":
714
+ return json.dumps(await svc.evaluate(args["run_id"]))
715
+
716
+ if name == "learn.feedback":
717
+ fb = Feedback(
718
+ rating=args["rating"],
719
+ comment=args.get("comment"),
720
+ run_id=args.get("run_id"),
721
+ )
722
+ return json.dumps(await svc.record_feedback(fb))
723
+
724
+ if name == "learn.improvements":
725
+ return json.dumps(
726
+ await svc.get_recommendations(
727
+ workflow_id=args.get("workflow_id"),
728
+ agent_id=args.get("agent_id"),
729
+ )
730
+ )
731
+
732
+ if name == "learn.analytics":
733
+ return json.dumps(await svc.get_analytics())
734
+
735
+ if name == "learn.policies":
736
+ policies = await svc.get_policies()
737
+ return json.dumps([p.model_dump(mode="json") for p in policies])
738
+
739
+ if name == "learn.experiment":
740
+ exp = await svc.start_experiment(
741
+ resource_type="prompt",
742
+ resource_id=args["resource_id"],
743
+ variant_a="current",
744
+ variant_b=args["variant_b"],
745
+ traffic_split=args.get("traffic_split", 0.1),
746
+ )
747
+ return json.dumps(exp.model_dump(mode="json"))
748
+
749
+ if name == "learn.rollback":
750
+ return json.dumps(
751
+ await svc.rollback_resource(
752
+ args["resource_type"], args["resource_id"], args["target_version"]
753
+ )
754
+ )
755
+
756
+ if name == "learn.skills":
757
+ skills = await svc.search_skills(args["query"])
758
+ return json.dumps([s.model_dump(mode="json") for s in skills])
759
+
760
+ if name == "learn.telemetry":
761
+ return json.dumps(
762
+ await svc.record_lesson_outcome(
763
+ args["lesson_id"],
764
+ telemetry_id=args.get("telemetry_id"),
765
+ run_id=args.get("run_id"),
766
+ used=args.get("used", False),
767
+ accepted=args.get("accepted", False),
768
+ improved=args.get("improved"),
769
+ )
770
+ )
771
+
772
+ return json.dumps({"error": f"Unknown tool: {name}"})