nexo-brain 5.3.26 → 5.3.28

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 (212) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/src/hook_guardrails.py +44 -0
  4. package/src/server.py +3 -0
  5. package/src/tools_sessions.py +6 -1
  6. package/src/dashboard/static/favicon 2.svg +0 -32
  7. package/src/dashboard/static/nexo-logo 2.png +0 -0
  8. package/src/dashboard/static/nexo-logo 2.svg +0 -40
  9. package/src/dashboard/static/style 2.css +0 -2458
  10. package/src/dashboard/templates/adaptive 2.html +0 -118
  11. package/src/dashboard/templates/artifacts 2.html +0 -133
  12. package/src/dashboard/templates/backups 2.html +0 -136
  13. package/src/dashboard/templates/base 2.html +0 -417
  14. package/src/dashboard/templates/calendar 2.html +0 -591
  15. package/src/dashboard/templates/chat 2.html +0 -356
  16. package/src/dashboard/templates/claims 2.html +0 -259
  17. package/src/dashboard/templates/cortex 2.html +0 -321
  18. package/src/dashboard/templates/credentials 2.html +0 -128
  19. package/src/dashboard/templates/crons 2.html +0 -370
  20. package/src/dashboard/templates/dashboard 2.html +0 -494
  21. package/src/dashboard/templates/dreams 2.html +0 -252
  22. package/src/dashboard/templates/email 2.html +0 -160
  23. package/src/dashboard/templates/evolution 2.html +0 -189
  24. package/src/dashboard/templates/feed 2.html +0 -249
  25. package/src/dashboard/templates/followup_health 2.html +0 -170
  26. package/src/dashboard/templates/graph 2.html +0 -201
  27. package/src/dashboard/templates/guard 2.html +0 -259
  28. package/src/dashboard/templates/inbox 2.html +0 -251
  29. package/src/dashboard/templates/memory 2.html +0 -420
  30. package/src/dashboard/templates/operations 2.html +0 -608
  31. package/src/dashboard/templates/plugins 2.html +0 -185
  32. package/src/dashboard/templates/protocol 2.html +0 -199
  33. package/src/dashboard/templates/rules 2.html +0 -246
  34. package/src/dashboard/templates/sentiment 2.html +0 -247
  35. package/src/dashboard/templates/sessions 2.html +0 -218
  36. package/src/dashboard/templates/skills 2.html +0 -329
  37. package/src/dashboard/templates/somatic 2.html +0 -73
  38. package/src/dashboard/templates/triggers 2.html +0 -133
  39. package/src/dashboard/templates/trust 2.html +0 -360
  40. package/src/db/__init__ 2.py +0 -259
  41. package/src/db/_core 2.py +0 -437
  42. package/src/db/_credentials 2.py +0 -124
  43. package/src/db/_episodic 2.py +0 -762
  44. package/src/db/_evolution 2.py +0 -54
  45. package/src/db/_fts 2.py +0 -406
  46. package/src/db/_goal_profiles 2.py +0 -376
  47. package/src/db/_hot_context 2.py +0 -660
  48. package/src/db/_outcomes 2.py +0 -800
  49. package/src/db/_personal_scripts 2.py +0 -582
  50. package/src/db/_sessions 2.py +0 -330
  51. package/src/db/_tasks 2.py +0 -91
  52. package/src/db/_watchers 2.py +0 -173
  53. package/src/doctor/formatters 2.py +0 -52
  54. package/src/doctor/models 2.py +0 -69
  55. package/src/doctor/planes 2.py +0 -87
  56. package/src/doctor/providers/__init__ 2.py +0 -1
  57. package/src/doctor/providers/deep 2.py +0 -367
  58. package/src/evolution_cycle 2.py +0 -519
  59. package/src/hooks/auto_capture 2.py +0 -208
  60. package/src/hooks/caffeinate-guard 2.sh +0 -8
  61. package/src/hooks/capture-session 2.sh +0 -21
  62. package/src/hooks/capture-tool-logs 2.sh +0 -158
  63. package/src/hooks/daily-briefing-check 2.sh +0 -33
  64. package/src/hooks/heartbeat-enforcement 2.py +0 -90
  65. package/src/hooks/heartbeat-posttool 2.sh +0 -18
  66. package/src/hooks/inbox-hook 2.sh +0 -76
  67. package/src/hooks/post-compact 2.sh +0 -152
  68. package/src/hooks/pre-compact 2.sh +0 -169
  69. package/src/hooks/protocol-guardrail 2.sh +0 -10
  70. package/src/hooks/protocol-pretool-guardrail 2.sh +0 -9
  71. package/src/hooks/session-stop 2.sh +0 -52
  72. package/src/kg_populate 2.py +0 -292
  73. package/src/maintenance 2.py +0 -53
  74. package/src/memory_backends 2.py +0 -71
  75. package/src/migrate_embeddings 2.py +0 -124
  76. package/src/nexo_sdk 2.py +0 -103
  77. package/src/observability 2.py +0 -199
  78. package/src/plugin_loader 2.py +0 -217
  79. package/src/plugins/__init__ 2.py +0 -0
  80. package/src/plugins/artifact_registry 2.py +0 -450
  81. package/src/plugins/backup 2.py +0 -127
  82. package/src/plugins/claims_tools 2.py +0 -119
  83. package/src/plugins/cognitive_memory 2.py +0 -609
  84. package/src/plugins/core_rules 2.py +0 -252
  85. package/src/plugins/cortex 2.py +0 -1155
  86. package/src/plugins/entities 2.py +0 -67
  87. package/src/plugins/episodic_memory 2.py +0 -560
  88. package/src/plugins/evolution 2.py +0 -167
  89. package/src/plugins/goal_engine 2.py +0 -142
  90. package/src/plugins/guard 2.py +0 -862
  91. package/src/plugins/impact 2.py +0 -29
  92. package/src/plugins/knowledge_graph_tools 2.py +0 -137
  93. package/src/plugins/media_memory_tools 2.py +0 -98
  94. package/src/plugins/memory_export 2.py +0 -196
  95. package/src/plugins/outcomes 2.py +0 -130
  96. package/src/plugins/personal_scripts 2.py +0 -117
  97. package/src/plugins/preferences 2.py +0 -47
  98. package/src/plugins/protocol 2.py +0 -1449
  99. package/src/plugins/simple_api 2.py +0 -106
  100. package/src/plugins/skills 2.py +0 -341
  101. package/src/plugins/state_watchers 2.py +0 -79
  102. package/src/plugins/update 2.py +0 -986
  103. package/src/plugins/user_state_tools 2.py +0 -43
  104. package/src/plugins/workflow 2.py +0 -588
  105. package/src/protocol_settings 2.py +0 -59
  106. package/src/public_contribution 2.py +0 -466
  107. package/src/public_evolution_queue 2.py +0 -241
  108. package/src/requirements 2.txt +0 -14
  109. package/src/retroactive_learnings 2.py +0 -373
  110. package/src/rules/__init__ 2.py +0 -0
  111. package/src/rules/core-rules 2.json +0 -331
  112. package/src/rules/migrate 2.py +0 -207
  113. package/src/runtime_power 2.py +0 -874
  114. package/src/script_registry 2.py +0 -1559
  115. package/src/scripts/check-context 2.py +0 -272
  116. package/src/scripts/deep-sleep/apply_findings 2.py +0 -2327
  117. package/src/scripts/deep-sleep/collect 2.py +0 -928
  118. package/src/scripts/deep-sleep/extract 2.py +0 -330
  119. package/src/scripts/deep-sleep/extract-prompt 2.md +0 -285
  120. package/src/scripts/deep-sleep/synthesize 2.py +0 -312
  121. package/src/scripts/deep-sleep/synthesize-prompt 2.md +0 -336
  122. package/src/scripts/nexo-agent-run 2.py +0 -75
  123. package/src/scripts/nexo-auto-update 2.py +0 -6
  124. package/src/scripts/nexo-backup 2.sh +0 -25
  125. package/src/scripts/nexo-brain-activation 2.sh +0 -140
  126. package/src/scripts/nexo-catchup 2.py +0 -300
  127. package/src/scripts/nexo-cognitive-decay 2.py +0 -257
  128. package/src/scripts/nexo-cortex-cycle 2.py +0 -293
  129. package/src/scripts/nexo-cron-wrapper 2.sh +0 -53
  130. package/src/scripts/nexo-daily-self-audit 2.py +0 -2161
  131. package/src/scripts/nexo-dashboard 2.sh +0 -29
  132. package/src/scripts/nexo-deep-sleep 2.sh +0 -86
  133. package/src/scripts/nexo-evolution-run 2.py +0 -1664
  134. package/src/scripts/nexo-followup-hygiene 2.py +0 -139
  135. package/src/scripts/nexo-hook-record 2.py +0 -42
  136. package/src/scripts/nexo-immune 2.py +0 -936
  137. package/src/scripts/nexo-impact-scorer 2.py +0 -117
  138. package/src/scripts/nexo-inbox-hook 2.sh +0 -74
  139. package/src/scripts/nexo-install 2.py +0 -6
  140. package/src/scripts/nexo-learning-housekeep 2.py +0 -401
  141. package/src/scripts/nexo-learning-validator 2.py +0 -266
  142. package/src/scripts/nexo-migrate 2.py +0 -260
  143. package/src/scripts/nexo-outcome-checker 2.py +0 -127
  144. package/src/scripts/nexo-postmortem-consolidator 2.py +0 -456
  145. package/src/scripts/nexo-pre-commit 2.py +0 -120
  146. package/src/scripts/nexo-prevent-sleep 2.sh +0 -35
  147. package/src/scripts/nexo-proactive-dashboard 2.py +0 -354
  148. package/src/scripts/nexo-reflection 2.py +0 -256
  149. package/src/scripts/nexo-runtime-preflight 2.py +0 -274
  150. package/src/scripts/nexo-sleep 2.py +0 -631
  151. package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
  152. package/src/scripts/nexo-sync-clients 2.py +0 -16
  153. package/src/scripts/nexo-synthesis 2.py +0 -475
  154. package/src/scripts/nexo-tcc-approve 2.sh +0 -79
  155. package/src/scripts/nexo-update 2.sh +0 -306
  156. package/src/scripts/nexo-watchdog 2.sh +0 -1207
  157. package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
  158. package/src/scripts/rehydrate_learnings_from_archive 2.py +0 -245
  159. package/src/server 2.py +0 -1296
  160. package/src/skills/run-nexo-audit-phase/guide 2.md +0 -43
  161. package/src/skills/run-nexo-audit-phase/skill 2.json +0 -59
  162. package/src/skills/run-nexo-core-fix-cycle/guide 2.md +0 -17
  163. package/src/skills/run-nexo-core-fix-cycle/script 2.py +0 -276
  164. package/src/skills/run-nexo-core-fix-cycle/skill 2.json +0 -58
  165. package/src/skills/run-release-final-audit/guide 2.md +0 -16
  166. package/src/skills/run-release-final-audit/script 2.py +0 -259
  167. package/src/skills/run-release-final-audit/skill 2.json +0 -77
  168. package/src/skills/run-runtime-doctor/guide 2.md +0 -12
  169. package/src/skills/run-runtime-doctor/script 2.py +0 -21
  170. package/src/skills/run-runtime-doctor/skill 2.json +0 -25
  171. package/src/skills_runtime 2.py +0 -932
  172. package/src/state_watchers_runtime 2.py +0 -475
  173. package/src/storage_router 2.py +0 -32
  174. package/src/system_catalog 2.py +0 -786
  175. package/src/tools_coordination 2.py +0 -103
  176. package/src/tools_credentials 2.py +0 -68
  177. package/src/tools_drive 2.py +0 -487
  178. package/src/tools_hot_context 2.py +0 -163
  179. package/src/tools_learnings 2.py +0 -612
  180. package/src/tools_menu 2.py +0 -229
  181. package/src/tools_reminders 2.py +0 -88
  182. package/src/tools_reminders_crud 2.py +0 -363
  183. package/src/tools_sessions 2.py +0 -1054
  184. package/src/tools_system_catalog 2.py +0 -19
  185. package/src/tools_task_history 2.py +0 -57
  186. package/src/tools_transcripts 2.py +0 -98
  187. package/src/transcript_utils 2.py +0 -412
  188. package/src/user_context 2.py +0 -46
  189. package/src/user_data_portability 2.py +0 -328
  190. package/src/user_state_model 2.py +0 -170
  191. package/templates/CLAUDE.md 2.template +0 -108
  192. package/templates/CODEX.AGENTS.md 2.template +0 -66
  193. package/templates/launchagents/README 2.md +0 -132
  194. package/templates/launchagents/com.nexo.auto-close-sessions 2.plist +0 -39
  195. package/templates/launchagents/com.nexo.catchup 2.plist +0 -39
  196. package/templates/launchagents/com.nexo.cognitive-decay 2.plist +0 -40
  197. package/templates/launchagents/com.nexo.dashboard 2.plist +0 -43
  198. package/templates/launchagents/com.nexo.deep-sleep 2.plist +0 -43
  199. package/templates/launchagents/com.nexo.evolution 2.plist +0 -44
  200. package/templates/launchagents/com.nexo.followup-hygiene 2.plist +0 -45
  201. package/templates/launchagents/com.nexo.immune 2.plist +0 -41
  202. package/templates/launchagents/com.nexo.postmortem 2.plist +0 -45
  203. package/templates/launchagents/com.nexo.self-audit 2.plist +0 -47
  204. package/templates/launchagents/com.nexo.synthesis 2.plist +0 -45
  205. package/templates/launchagents/com.nexo.watchdog 2.plist +0 -37
  206. package/templates/nexo_helper 2.py +0 -301
  207. package/templates/openclaw 2.json +0 -13
  208. package/templates/plugin-template 2.py +0 -40
  209. package/templates/script-template 2.py +0 -59
  210. package/templates/script-template 2.sh +0 -13
  211. package/templates/skill-script-template 2.py +0 -48
  212. package/templates/skill-template 2.md +0 -33
@@ -1,106 +0,0 @@
1
- """Minimal public API wrappers for the core NEXO mental model."""
2
-
3
- from __future__ import annotations
4
-
5
- import hashlib
6
- import json
7
-
8
- import cognitive
9
-
10
- from plugins.episodic_memory import handle_recall
11
- from plugins.workflow import handle_workflow_open
12
-
13
-
14
- def handle_remember(
15
- content: str,
16
- title: str = "",
17
- domain: str = "",
18
- source_type: str = "note",
19
- tags: str = "",
20
- bypass_gate: bool = True,
21
- ) -> str:
22
- """Store one durable memory item with a single high-level call."""
23
- clean_content = (content or "").strip()
24
- if not clean_content:
25
- return json.dumps({"ok": False, "error": "content is required"}, ensure_ascii=False, indent=2)
26
-
27
- clean_title = (title or "").strip()[:120]
28
- # Content fingerprint for deterministic dedup id — not security-sensitive.
29
- source_id = hashlib.sha1(
30
- f"{clean_title}|{clean_content}".encode("utf-8"), usedforsecurity=False
31
- ).hexdigest()[:12]
32
- memory_id = cognitive.ingest_to_ltm(
33
- clean_content,
34
- source_type=(source_type or "note").strip()[:40],
35
- source_id=source_id,
36
- source_title=clean_title or clean_content[:80],
37
- domain=(domain or "").strip()[:120],
38
- tags=(tags or "").strip()[:200],
39
- bypass_gate=bool(bypass_gate),
40
- )
41
- return json.dumps(
42
- {
43
- "ok": bool(memory_id),
44
- "memory_id": int(memory_id or 0),
45
- "source_type": (source_type or "note").strip()[:40],
46
- "title": clean_title or clean_content[:80],
47
- "domain": (domain or "").strip()[:120],
48
- },
49
- ensure_ascii=False,
50
- indent=2,
51
- )
52
-
53
-
54
- def handle_memory_recall(query: str, days: int = 30) -> str:
55
- """High-level memory lookup wrapper around nexo_recall."""
56
- return handle_recall((query or "").strip(), days=max(1, int(days or 30)))
57
-
58
-
59
- def handle_consolidate(
60
- max_insights: int = 12,
61
- threshold: float = 0.9,
62
- dry_run: bool = False,
63
- ) -> str:
64
- """Run the core memory consolidation cycle explicitly."""
65
- promoted = cognitive.promote_stm_to_ltm()
66
- quarantine = cognitive.process_quarantine()
67
- dreamed = cognitive.dream_cycle(max_insights=max(1, int(max_insights or 12)))
68
- semantic = cognitive.consolidate_semantic(threshold=float(threshold or 0.9), dry_run=bool(dry_run))
69
- payload = {
70
- "ok": True,
71
- "promoted_to_ltm": int(promoted or 0),
72
- "quarantine": quarantine,
73
- "dream_cycle": dreamed,
74
- "semantic_consolidation": semantic,
75
- "dry_run": bool(dry_run),
76
- }
77
- return json.dumps(payload, ensure_ascii=False, indent=2)
78
-
79
-
80
- def handle_run_workflow(
81
- sid: str,
82
- goal: str,
83
- steps: str = "[]",
84
- goal_id: str = "",
85
- shared_state: str = "{}",
86
- owner: str = "",
87
- idempotency_key: str = "",
88
- ) -> str:
89
- """Open a durable workflow with the public mental-model naming."""
90
- return handle_workflow_open(
91
- sid=sid,
92
- goal=goal,
93
- steps=steps,
94
- goal_id=goal_id,
95
- shared_state=shared_state,
96
- owner=owner,
97
- idempotency_key=idempotency_key,
98
- )
99
-
100
-
101
- TOOLS = [
102
- (handle_remember, "nexo_remember", "High-level memory write: store one durable memory item."),
103
- (handle_memory_recall, "nexo_memory_recall", "High-level memory lookup wrapper around nexo_recall."),
104
- (handle_consolidate, "nexo_consolidate", "Run NEXO memory consolidation explicitly: promote, process quarantine, dream, consolidate."),
105
- (handle_run_workflow, "nexo_run_workflow", "High-level durable workflow entry point for the public API surface."),
106
- ]
@@ -1,341 +0,0 @@
1
- """Skills plugin — reusable procedures, executable skills, and feedback loops."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
-
7
- from db import (
8
- create_skill,
9
- delete_skill,
10
- get_skill,
11
- get_skill_stats,
12
- list_skills,
13
- match_skills,
14
- merge_skills,
15
- record_skill_usage,
16
- search_skills,
17
- update_skill,
18
- )
19
- from skills_runtime import (
20
- apply_skill,
21
- approve_skill_execution,
22
- compose_skills,
23
- detect_skill_coactivation_candidates,
24
- get_featured_skill_summaries,
25
- list_evolution_candidates,
26
- materialize_outcome_pattern_skill,
27
- promote_skill,
28
- review_skill_outcomes,
29
- retire_skill,
30
- sync_skills,
31
- test_skill,
32
- )
33
-
34
-
35
- def handle_skill_create(
36
- id: str,
37
- name: str,
38
- description: str = "",
39
- level: str = "draft",
40
- tags: str = "[]",
41
- trigger_patterns: str = "[]",
42
- source_sessions: str = "[]",
43
- linked_learnings: str = "[]",
44
- file_path: str = "",
45
- mode: str = "",
46
- source_kind: str = "personal",
47
- execution_level: str = "none",
48
- approval_required: bool = False,
49
- params_schema: str = "{}",
50
- command_template: str = "{}",
51
- executable_entry: str = "",
52
- ) -> str:
53
- if not id.startswith("SK-"):
54
- return "ERROR: Skill ID must start with 'SK-' (e.g., SK-DEPLOY-CHROME-EXT)"
55
- if get_skill(id):
56
- return f"ERROR: Skill {id} already exists. Use nexo_skill_update to modify."
57
-
58
- result = create_skill(
59
- skill_id=id,
60
- name=name,
61
- description=description,
62
- level=level,
63
- tags=tags,
64
- trigger_patterns=trigger_patterns,
65
- source_sessions=source_sessions,
66
- linked_learnings=linked_learnings,
67
- file_path=file_path,
68
- mode=mode,
69
- source_kind=source_kind,
70
- execution_level=execution_level,
71
- approval_required=approval_required,
72
- params_schema=params_schema,
73
- command_template=command_template,
74
- executable_entry=executable_entry,
75
- )
76
- if "error" in result:
77
- return f"ERROR: {result['error']}"
78
-
79
- return (
80
- f"Skill {id} created ({result['level']}, {result.get('mode', 'guide')}, trust={result.get('trust_score', 50)}).\n"
81
- f" Name: {name}\n"
82
- f" Source: {result.get('source_kind', source_kind)}\n"
83
- f" Execution: {result.get('execution_level', execution_level)}"
84
- )
85
-
86
-
87
- def handle_skill_match(task: str, level: str = "") -> str:
88
- matches = match_skills(task, level=level)
89
- if not matches:
90
- return f"No skills found for: '{task}'"
91
-
92
- lines = [f"SKILLS MATCHED ({len(matches)}) for '{task}':"]
93
- for match in matches:
94
- match_method = match.pop("_match", "unknown")
95
- lines.append(
96
- f" [{match['id']}] {match['name']} ({match['level']}, {match.get('mode', 'guide')}, "
97
- f"{match.get('source_kind', 'personal')}, trust={match['trust_score']}, used={match['use_count']}x) "
98
- f"via {match_method}"
99
- )
100
- lines.append(f" {match['description'][:140]}")
101
- return "\n".join(lines)
102
-
103
-
104
- def handle_skill_get(id: str) -> str:
105
- skill = get_skill(id)
106
- if not skill:
107
- return f"ERROR: Skill {id} not found."
108
-
109
- lines = [
110
- f"SKILL: {skill['id']}",
111
- f" Name: {skill['name']}",
112
- f" Description: {skill['description']}",
113
- f" Level: {skill['level']}",
114
- f" Mode: {skill.get('mode', 'guide')}",
115
- f" Source: {skill.get('source_kind', 'personal')}",
116
- f" Trust: {skill['trust_score']}",
117
- f" Execution level: {skill.get('execution_level', 'none')}",
118
- f" Approval required: {bool(skill.get('approval_required', 0))}",
119
- f" Approved at: {skill.get('approved_at') or 'no'}",
120
- f" Definition: {skill.get('definition_path') or '(none)'}",
121
- f" File: {skill.get('file_path') or '(none)'}",
122
- f" Params schema: {skill.get('params_schema', '{}')}",
123
- f" Triggers: {skill['trigger_patterns']}",
124
- f" Stats: {skill['use_count']} uses, {skill['success_count']} success, {skill['fail_count']} fail",
125
- ]
126
- return "\n".join(lines)
127
-
128
-
129
- def handle_skill_result(id: str, success: bool = True, context: str = "", notes: str = "") -> str:
130
- result = record_skill_usage(skill_id=id, success=success, context=context, notes=notes)
131
- if "error" in result:
132
- return f"ERROR: {result['error']}"
133
-
134
- promotion = result.get("_promotion")
135
- msg = f"Skill {id} usage recorded: {'SUCCESS' if success else 'FAILURE'} (trust={result['trust_score']})"
136
- if promotion:
137
- msg += f"\n ⚡ PROMOTION: {promotion}"
138
- return msg
139
-
140
-
141
- def handle_skill_list(level: str = "", tag: str = "", source_kind: str = "") -> str:
142
- skills = list_skills(level=level, tag=tag, source_kind=source_kind)
143
- if not skills:
144
- return "No skills found."
145
-
146
- lines = [f"SKILLS ({len(skills)}):"]
147
- for skill in skills:
148
- lines.append(
149
- f" [{skill['id']}] {skill['name']} ({skill['level']}, {skill.get('mode', 'guide')}, "
150
- f"{skill.get('source_kind', 'personal')}, trust={skill['trust_score']}, used={skill['use_count']}x)"
151
- )
152
- return "\n".join(lines)
153
-
154
-
155
- def handle_skill_merge(id1: str, id2: str, keep_id: str = "") -> str:
156
- result = merge_skills(id1, id2, keep_id=keep_id)
157
- if "error" in result:
158
- return f"ERROR: {result['error']}"
159
- return (
160
- f"Skills merged. Kept {result['id']}, deleted {result['_merged_from']}.\n"
161
- f" Trust: {result['trust_score']}, Uses: {result['use_count']}"
162
- )
163
-
164
-
165
- def handle_skill_stats() -> str:
166
- stats = get_skill_stats()
167
- return (
168
- "SKILL STATS:\n"
169
- f" Total: {stats['total']}\n"
170
- f" By level: {', '.join(f'{k}={v}' for k, v in sorted(stats['by_level'].items()))}\n"
171
- f" Avg trust: {stats['avg_trust']}\n"
172
- f" Total uses: {stats['total_uses']} (success rate: {stats['success_rate']}%)\n"
173
- f" Uses last 7d: {stats['uses_last_7d']}\n"
174
- f" Reuse rate: {stats['skill_reuse_rate']}\n"
175
- f" Outcome-backed skills: {stats['outcome_backed_skills']} (avg success: {stats['outcome_backed_success_rate']}%)\n"
176
- f" Promoted from evidence: {stats['promoted_from_evidence_count']}\n"
177
- f" Retired for poor outcomes: {stats['retired_for_poor_outcomes_count']}"
178
- )
179
-
180
-
181
- def handle_skill_apply(id: str, params: str = "{}", mode: str = "auto", dry_run: bool = False, context: str = "") -> str:
182
- return json.dumps(apply_skill(id, params=params, mode=mode, dry_run=dry_run, context=context), ensure_ascii=False)
183
-
184
-
185
- def handle_skill_test(id: str, params: str = "{}", mode: str = "auto", context: str = "") -> str:
186
- return json.dumps(test_skill(id, params=params, mode=mode, context=context), ensure_ascii=False)
187
-
188
-
189
- def handle_skill_approve(id: str, execution_level: str = "", approved_by: str = "") -> str:
190
- result = approve_skill_execution(id, execution_level=execution_level, approved_by=approved_by)
191
- if "error" in result:
192
- return f"ERROR: {result['error']}"
193
- return (
194
- f"Skill {id} approved.\n"
195
- f" Execution level: {result.get('execution_level', 'none')}\n"
196
- f" Approved at: {result.get('approved_at', '')}\n"
197
- f" Approved by: {result.get('approved_by', '')}"
198
- )
199
-
200
-
201
- def handle_skill_sync() -> str:
202
- result = sync_skills()
203
- return json.dumps(result, ensure_ascii=False)
204
-
205
-
206
- def handle_skill_featured(limit: int = 5) -> str:
207
- return json.dumps(get_featured_skill_summaries(limit=limit), ensure_ascii=False)
208
-
209
-
210
- def handle_skill_evolution_candidates() -> str:
211
- return json.dumps(list_evolution_candidates(), ensure_ascii=False)
212
-
213
-
214
- def handle_skill_compose_candidates(
215
- min_co_occurrence: int = 3,
216
- min_success_rate: float = 0.6,
217
- limit: int = 20,
218
- ) -> str:
219
- """List Voyager-style composition candidates from skill_usage co-activation.
220
-
221
- Closes Fase 5 item 5. Detects pairs of skills that fire together
222
- in the same session repeatedly and could become a single composite.
223
- Returns the candidates sorted by co-occurrence count, with a
224
- suggested deterministic skill_id (SK-COMPOSE-<a>+<b>) that
225
- nexo_skill_compose can take as input.
226
-
227
- Args:
228
- min_co_occurrence: minimum sessions where both skills fired (default 3).
229
- min_success_rate: minimum joint success rate (default 0.6).
230
- limit: max candidates to return (default 20).
231
- """
232
- return json.dumps(
233
- detect_skill_coactivation_candidates(
234
- min_co_occurrence=int(min_co_occurrence),
235
- min_success_rate=float(min_success_rate),
236
- limit=int(limit),
237
- ),
238
- ensure_ascii=False,
239
- indent=2,
240
- )
241
-
242
-
243
- def handle_skill_seed_from_outcome_pattern(pattern_key: str) -> str:
244
- return json.dumps(materialize_outcome_pattern_skill(pattern_key), ensure_ascii=False)
245
-
246
-
247
- def handle_skill_outcome_review(id: str, auto_apply: bool = False) -> str:
248
- return json.dumps(review_skill_outcomes(id, auto_apply=auto_apply), ensure_ascii=False)
249
-
250
-
251
- def handle_skill_promote(id: str, target_level: str = "published", reason: str = "") -> str:
252
- return json.dumps(promote_skill(id, target_level=target_level, reason=reason), ensure_ascii=False)
253
-
254
-
255
- def handle_skill_retire(id: str, replacement_id: str = "", reason: str = "") -> str:
256
- return json.dumps(retire_skill(id, replacement_id=replacement_id, reason=reason), ensure_ascii=False)
257
-
258
-
259
- def handle_skill_compose(
260
- new_id: str,
261
- name: str,
262
- component_ids: str = "[]",
263
- description: str = "",
264
- level: str = "draft",
265
- mode: str = "guide",
266
- tags: str = "[]",
267
- trigger_patterns: str = "[]",
268
- ) -> str:
269
- try:
270
- component_list = json.loads(component_ids) if str(component_ids or "").strip().startswith("[") else [
271
- item.strip() for item in str(component_ids or "").split(",") if item.strip()
272
- ]
273
- except json.JSONDecodeError:
274
- component_list = []
275
- try:
276
- tags_list = json.loads(tags) if str(tags or "").strip().startswith("[") else [
277
- item.strip() for item in str(tags or "").split(",") if item.strip()
278
- ]
279
- except json.JSONDecodeError:
280
- tags_list = []
281
- try:
282
- trigger_list = json.loads(trigger_patterns) if str(trigger_patterns or "").strip().startswith("[") else [
283
- item.strip() for item in str(trigger_patterns or "").split(",") if item.strip()
284
- ]
285
- except json.JSONDecodeError:
286
- trigger_list = []
287
- return json.dumps(
288
- compose_skills(
289
- new_skill_id=new_id,
290
- name=name,
291
- component_ids=component_list,
292
- description=description,
293
- level=level,
294
- mode=mode,
295
- tags=tags_list,
296
- trigger_patterns=trigger_list,
297
- ),
298
- ensure_ascii=False,
299
- )
300
-
301
-
302
- TOOLS = [
303
- (handle_skill_create, "nexo_skill_create",
304
- "Create a new skill with guide/execute/hybrid metadata, triggers, params schema, and execution level."),
305
- (handle_skill_match, "nexo_skill_match",
306
- "Find skills matching a task description. Call before multi-step tasks."),
307
- (handle_skill_get, "nexo_skill_get",
308
- "Get a skill's full details, including execution metadata and approval state."),
309
- (handle_skill_result, "nexo_skill_result",
310
- "Record the result of using a skill. Updates trust and promotions."),
311
- (handle_skill_list, "nexo_skill_list",
312
- "List skills, optionally filtered by level, tag, or source kind."),
313
- (handle_skill_merge, "nexo_skill_merge",
314
- "Merge two similar skills into one."),
315
- (handle_skill_stats, "nexo_skill_stats",
316
- "Show aggregate skill statistics."),
317
- (handle_skill_apply, "nexo_skill_apply",
318
- "Apply a skill in guide, execute, or hybrid mode. Execution goes through the stable nexo scripts runtime."),
319
- (handle_skill_test, "nexo_skill_test",
320
- "Test a skill through the canonical runtime in dry-run mode before wider use."),
321
- (handle_skill_approve, "nexo_skill_approve",
322
- "Approve a local/remote executable skill so it can run."),
323
- (handle_skill_sync, "nexo_skill_sync",
324
- "Sync filesystem skill definitions from personal/core/community directories into SQLite."),
325
- (handle_skill_featured, "nexo_skill_featured",
326
- "Return featured published/stable skills for startup discovery."),
327
- (handle_skill_evolution_candidates, "nexo_skill_evolution_candidates",
328
- "Return candidates for skill improvement or text-to-script evolution."),
329
- (handle_skill_seed_from_outcome_pattern, "nexo_skill_seed_from_outcome_pattern",
330
- "Materialize a draft skill candidate from a repeated successful outcome pattern with linked evidence."),
331
- (handle_skill_outcome_review, "nexo_skill_outcome_review",
332
- "Review whether a skill should be promoted, deprioritized, or retired based on sustained outcome evidence."),
333
- (handle_skill_promote, "nexo_skill_promote",
334
- "Promote a skill to a stronger published/stable lifecycle stage."),
335
- (handle_skill_retire, "nexo_skill_retire",
336
- "Retire a skill cleanly so it leaves the active lifecycle."),
337
- (handle_skill_compose, "nexo_skill_compose",
338
- "Compose multiple existing skills into one higher-level reusable skill."),
339
- (handle_skill_compose_candidates, "nexo_skill_compose_candidates",
340
- "Voyager-style detection: list skill pairs that fired together in 3+ sessions with 60%+ joint success and could become a single composite (closes Fase 5 item 5)."),
341
- ]
@@ -1,79 +0,0 @@
1
- """State watchers plugin — persistent drift/health/expiry watchers."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
-
7
- from db import create_state_watcher, list_state_watchers, update_state_watcher
8
- from state_watchers_runtime import run_state_watchers
9
-
10
-
11
- def handle_state_watcher_create(
12
- watcher_type: str,
13
- title: str,
14
- target: str = "",
15
- severity: str = "warn",
16
- status: str = "active",
17
- config: str = "{}",
18
- ) -> str:
19
- """Create a persistent state watcher for drift, health, or expiry."""
20
- try:
21
- watcher = create_state_watcher(
22
- watcher_type,
23
- title,
24
- target=target,
25
- severity=severity,
26
- status=status,
27
- config=json.loads(config) if str(config).strip() else {},
28
- )
29
- except (ValueError, json.JSONDecodeError) as exc:
30
- return json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False, indent=2)
31
- return json.dumps({"ok": True, "watcher": watcher}, ensure_ascii=False, indent=2)
32
-
33
-
34
- def handle_state_watcher_update(
35
- watcher_id: str,
36
- title: str = "",
37
- target: str = "",
38
- severity: str = "",
39
- status: str = "",
40
- config: str = "",
41
- ) -> str:
42
- """Update an existing state watcher."""
43
- payload = None
44
- if str(config).strip():
45
- try:
46
- payload = json.loads(config)
47
- except json.JSONDecodeError as exc:
48
- return json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False, indent=2)
49
- watcher = update_state_watcher(
50
- watcher_id,
51
- title=(title or None),
52
- target=(target or None),
53
- severity=(severity or None),
54
- status=(status or None),
55
- config=payload,
56
- )
57
- if not watcher:
58
- return json.dumps({"ok": False, "error": f"Unknown watcher_id: {watcher_id}"}, ensure_ascii=False, indent=2)
59
- return json.dumps({"ok": True, "watcher": watcher}, ensure_ascii=False, indent=2)
60
-
61
-
62
- def handle_state_watcher_list(status: str = "", watcher_type: str = "", limit: int = 50) -> str:
63
- """List configured state watchers."""
64
- watchers = list_state_watchers(status=status, watcher_type=watcher_type, limit=max(1, int(limit or 50)))
65
- return json.dumps({"ok": True, "count": len(watchers), "watchers": watchers}, ensure_ascii=False, indent=2)
66
-
67
-
68
- def handle_state_watcher_run(status: str = "active", persist: bool = True) -> str:
69
- """Run active state watchers and return their current health."""
70
- summary = run_state_watchers(status=status or "active", persist=bool(persist))
71
- return json.dumps({"ok": True, **summary}, ensure_ascii=False, indent=2)
72
-
73
-
74
- TOOLS = [
75
- (handle_state_watcher_create, "nexo_state_watcher_create", "Create a persistent state watcher for repo drift, cron drift, API health, environment drift, or expiry."),
76
- (handle_state_watcher_update, "nexo_state_watcher_update", "Update an existing persistent state watcher."),
77
- (handle_state_watcher_list, "nexo_state_watcher_list", "List persistent state watchers."),
78
- (handle_state_watcher_run, "nexo_state_watcher_run", "Run persistent state watchers and return their current health summary."),
79
- ]