nexo-brain 7.1.1 → 7.1.2

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 (176) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +3 -2
  3. package/bin/nexo-brain.js +198 -92
  4. package/package.json +1 -1
  5. package/src/agent_runner.py +10 -8
  6. package/src/auto_close_sessions.py +19 -2
  7. package/src/auto_update.py +232 -28
  8. package/src/autonomy_mandate.py +260 -0
  9. package/src/bootstrap_docs.py +22 -1
  10. package/src/cli.py +181 -1
  11. package/src/cli_email.py +104 -73
  12. package/src/client_sync.py +22 -1
  13. package/src/cognitive/_core.py +5 -3
  14. package/src/core_prompts.py +50 -0
  15. package/src/cron_recovery.py +81 -7
  16. package/src/crons/manifest.json +57 -0
  17. package/src/crons/sync.py +95 -26
  18. package/src/dashboard/app.py +59 -0
  19. package/src/dashboard/templates/base.html +2 -0
  20. package/src/dashboard/templates/feature-disabled.html +27 -0
  21. package/src/db/_email_accounts.py +67 -18
  22. package/src/db/_fts.py +5 -5
  23. package/src/db/_personal_scripts.py +1 -1
  24. package/src/db/_skills.py +3 -3
  25. package/src/doctor/providers/runtime.py +35 -20
  26. package/src/email_config.py +18 -9
  27. package/src/enforcement_classifier.py +3 -12
  28. package/src/evolution_cycle.py +37 -149
  29. package/src/guardian_telemetry.py +3 -2
  30. package/src/hook_guardrails.py +61 -0
  31. package/src/hooks/capture-tool-logs.sh +11 -3
  32. package/src/hooks/daily-briefing-check.sh +7 -2
  33. package/src/hooks/heartbeat-enforcement.py +14 -1
  34. package/src/hooks/heartbeat-posttool.sh +2 -0
  35. package/src/hooks/heartbeat-user-msg.sh +2 -0
  36. package/src/hooks/inbox-hook.sh +6 -2
  37. package/src/hooks/post-compact.sh +12 -4
  38. package/src/hooks/pre-compact.sh +12 -4
  39. package/src/migrate_embeddings.py +5 -3
  40. package/src/nexo_migrate.py +3 -1
  41. package/src/plugin_loader.py +14 -5
  42. package/src/plugins/adaptive_mode.py +4 -1
  43. package/src/plugins/backup.py +32 -20
  44. package/src/plugins/evolution.py +2 -0
  45. package/src/plugins/memory_export.py +6 -1
  46. package/src/plugins/personal_plugins.py +17 -7
  47. package/src/plugins/personal_scripts.py +64 -3
  48. package/src/presets/entities_universal.json +67 -4
  49. package/src/product_mode.py +201 -0
  50. package/src/r14_correction_learning.py +5 -20
  51. package/src/r15_project_context.py +4 -10
  52. package/src/r16_declared_done.py +3 -16
  53. package/src/r17_promise_debt.py +3 -16
  54. package/src/r18_followup_autocomplete.py +5 -7
  55. package/src/r19_project_grep.py +5 -8
  56. package/src/r20_constant_change.py +5 -15
  57. package/src/r21_legacy_path.py +5 -7
  58. package/src/r22_personal_script.py +4 -8
  59. package/src/r23_ssh_without_atlas.py +4 -11
  60. package/src/r23b_deploy_vhost.py +7 -6
  61. package/src/r23c_cwd_mismatch.py +7 -6
  62. package/src/r23d_chown_chmod_recursive.py +6 -6
  63. package/src/r23e_force_push_main.py +5 -6
  64. package/src/r23f_db_no_where.py +5 -6
  65. package/src/r23g_secrets_in_output.py +5 -5
  66. package/src/r23h_shebang_mismatch.py +6 -5
  67. package/src/r23i_auto_deploy_ignored.py +5 -6
  68. package/src/r23j_global_install.py +5 -6
  69. package/src/r23k_script_duplicates_skill.py +7 -6
  70. package/src/r23l_resource_collision.py +7 -6
  71. package/src/r23m_message_duplicate.py +6 -5
  72. package/src/r24_stale_memory.py +4 -9
  73. package/src/r25_nora_maria_read_only.py +5 -10
  74. package/src/r34_identity_coherence.py +6 -13
  75. package/src/r_catalog.py +3 -7
  76. package/src/resonance_map.py +13 -13
  77. package/src/runtime_power.py +29 -80
  78. package/src/script_registry.py +236 -6
  79. package/src/scripts/check-context.py +8 -25
  80. package/src/scripts/deep-sleep/extract.py +6 -10
  81. package/src/scripts/nexo-auto-update.py +27 -4
  82. package/src/scripts/nexo-catchup.py +9 -19
  83. package/src/scripts/nexo-cognitive-decay.py +26 -3
  84. package/src/scripts/nexo-daily-self-audit.py +50 -51
  85. package/src/scripts/nexo-email-migrate-config.py +30 -11
  86. package/src/scripts/nexo-email-monitor.py +97 -238
  87. package/src/scripts/nexo-followup-runner.py +70 -133
  88. package/src/scripts/nexo-hook-record.py +1 -1
  89. package/src/scripts/nexo-immune.py +6 -31
  90. package/src/scripts/nexo-impact-scorer.py +27 -4
  91. package/src/scripts/nexo-learning-housekeep.py +26 -3
  92. package/src/scripts/nexo-learning-validator.py +34 -32
  93. package/src/scripts/nexo-migrate.py +28 -12
  94. package/src/scripts/nexo-morning-agent.py +9 -23
  95. package/src/scripts/nexo-outcome-checker.py +27 -4
  96. package/src/scripts/nexo-postmortem-consolidator.py +30 -62
  97. package/src/scripts/nexo-pre-commit.py +28 -0
  98. package/src/scripts/nexo-proactive-dashboard.py +27 -0
  99. package/src/scripts/nexo-reflection.py +33 -3
  100. package/src/scripts/nexo-runtime-preflight.py +27 -2
  101. package/src/scripts/nexo-send-reply.py +10 -8
  102. package/src/scripts/nexo-sleep.py +11 -25
  103. package/src/scripts/nexo-synthesis.py +7 -40
  104. package/src/scripts/nexo-watchdog-smoke.py +30 -1
  105. package/src/scripts/nexo-watchdog.sh +23 -17
  106. package/src/scripts/phase_guardian_analysis.py +27 -4
  107. package/src/server.py +14 -3
  108. package/src/storage_router.py +8 -6
  109. package/src/tools_drive.py +5 -13
  110. package/src/tools_guardian.py +3 -4
  111. package/src/tools_menu.py +2 -2
  112. package/src/tools_reminders_crud.py +17 -0
  113. package/src/tools_sessions.py +1 -4
  114. package/src/user_context.py +3 -6
  115. package/src/user_data_portability.py +31 -23
  116. package/templates/CLAUDE.md.template +11 -3
  117. package/templates/CODEX.AGENTS.md.template +11 -3
  118. package/templates/core-prompts/catchup-assessment.md +19 -0
  119. package/templates/core-prompts/check-context.md +24 -0
  120. package/templates/core-prompts/daily-self-audit.md +42 -0
  121. package/templates/core-prompts/daily-synthesis.md +40 -0
  122. package/templates/core-prompts/deep-sleep-extract-json-output.md +8 -0
  123. package/templates/core-prompts/drive-signal-classifier-system.md +4 -0
  124. package/templates/core-prompts/drive-signal-classifier-user.md +6 -0
  125. package/templates/core-prompts/email-monitor.md +202 -0
  126. package/templates/core-prompts/enforcement-classifier-retry.md +1 -0
  127. package/templates/core-prompts/enforcement-classifier-strict.md +1 -0
  128. package/templates/core-prompts/evolution-public-contribution.md +32 -0
  129. package/templates/core-prompts/evolution-public-pr-review.md +38 -0
  130. package/templates/core-prompts/evolution-weekly.md +71 -0
  131. package/templates/core-prompts/followup-runner-operator-attention-context.md +4 -0
  132. package/templates/core-prompts/followup-runner-operator-attention-question.md +1 -0
  133. package/templates/core-prompts/followup-runner.md +74 -0
  134. package/templates/core-prompts/immune-triage.md +31 -0
  135. package/templates/core-prompts/interactive-startup.md +1 -0
  136. package/templates/core-prompts/json-object-only.md +1 -0
  137. package/templates/core-prompts/learning-validator.md +25 -0
  138. package/templates/core-prompts/morning-agent-json-output.md +1 -0
  139. package/templates/core-prompts/morning-agent.md +23 -0
  140. package/templates/core-prompts/postmortem-consolidator.md +60 -0
  141. package/templates/core-prompts/r-catalog.md +1 -0
  142. package/templates/core-prompts/r14-correction-learning-injection.md +1 -0
  143. package/templates/core-prompts/r14-correction-learning-question.md +1 -0
  144. package/templates/core-prompts/r15-project-context-injection.md +1 -0
  145. package/templates/core-prompts/r16-declared-done-injection.md +1 -0
  146. package/templates/core-prompts/r16-declared-done-question.md +1 -0
  147. package/templates/core-prompts/r17-promise-debt-injection.md +1 -0
  148. package/templates/core-prompts/r17-promise-debt-question.md +1 -0
  149. package/templates/core-prompts/r18-followup-autocomplete-injection.md +3 -0
  150. package/templates/core-prompts/r19-project-grep-injection.md +1 -0
  151. package/templates/core-prompts/r20-constant-change-injection.md +1 -0
  152. package/templates/core-prompts/r20-constant-change-question.md +1 -0
  153. package/templates/core-prompts/r21-legacy-path-injection.md +1 -0
  154. package/templates/core-prompts/r22-personal-script-injection.md +1 -0
  155. package/templates/core-prompts/r23-ssh-without-atlas-injection.md +1 -0
  156. package/templates/core-prompts/r23b-deploy-vhost-injection.md +1 -0
  157. package/templates/core-prompts/r23c-cwd-mismatch-injection.md +1 -0
  158. package/templates/core-prompts/r23d-chown-chmod-recursive-injection.md +1 -0
  159. package/templates/core-prompts/r23e-force-push-main-injection.md +1 -0
  160. package/templates/core-prompts/r23f-db-no-where-injection.md +1 -0
  161. package/templates/core-prompts/r23g-secrets-in-output-injection.md +1 -0
  162. package/templates/core-prompts/r23h-shebang-mismatch-injection.md +1 -0
  163. package/templates/core-prompts/r23i-auto-deploy-ignored-injection.md +1 -0
  164. package/templates/core-prompts/r23j-global-install-injection.md +1 -0
  165. package/templates/core-prompts/r23k-script-duplicates-skill-injection.md +1 -0
  166. package/templates/core-prompts/r23l-resource-collision-injection.md +1 -0
  167. package/templates/core-prompts/r23m-message-duplicate-injection.md +1 -0
  168. package/templates/core-prompts/r24-stale-memory-injection.md +1 -0
  169. package/templates/core-prompts/r25-read-only-host-injection.md +1 -0
  170. package/templates/core-prompts/r34-identity-coherence-probe.md +1 -0
  171. package/templates/core-prompts/r34-identity-coherence-question.md +1 -0
  172. package/templates/core-prompts/sleep.md +25 -0
  173. package/templates/email-template.md +55 -0
  174. package/templates/nexo_helper.py +31 -13
  175. package/templates/plugin-template.py +3 -3
  176. package/templates/skill-template.md +2 -1
@@ -0,0 +1,201 @@
1
+ """Product-mode contracts shared by Brain and Desktop."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import sys
8
+ import time
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ import paths
13
+
14
+
15
+ DESKTOP_PRODUCT_ENV = "NEXO_DESKTOP_MANAGED"
16
+ ALLOW_CORE_WRITES_ENV = "NEXO_ALLOW_CORE_WRITES"
17
+ PRODUCT_MODE_FILENAME = "product-mode.json"
18
+ DESKTOP_PRODUCT_MODE = "desktop_closed_product"
19
+ DESKTOP_DISABLED_FEATURES = ("evolution",)
20
+ DESKTOP_EVOLUTION_DISABLED_REASON = "Disabled by NEXO Desktop product contract"
21
+
22
+
23
+ def _now_iso() -> str:
24
+ return time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime())
25
+
26
+
27
+ def product_mode_path() -> Path:
28
+ return paths.config_dir() / PRODUCT_MODE_FILENAME
29
+
30
+
31
+ def _desktop_install_markers(home: Path | None = None) -> list[Path]:
32
+ base = Path(home) if home is not None else Path.home()
33
+ markers: list[Path] = [
34
+ base / "Applications" / "NEXO Desktop.app",
35
+ base / "Library" / "Application Support" / "NEXO Desktop",
36
+ base / ".local" / "share" / "NEXO Desktop",
37
+ base / ".config" / "NEXO Desktop",
38
+ ]
39
+ if home is None and sys.platform == "darwin":
40
+ markers.insert(0, Path("/Applications/NEXO Desktop.app"))
41
+ if os.name == "nt":
42
+ local = Path(os.environ.get("LOCALAPPDATA", str(base / "AppData" / "Local")))
43
+ roaming = Path(os.environ.get("APPDATA", str(base / "AppData" / "Roaming")))
44
+ markers.extend(
45
+ [
46
+ local / "Programs" / "NEXO Desktop",
47
+ roaming / "NEXO Desktop",
48
+ ]
49
+ )
50
+ return markers
51
+
52
+
53
+ def desktop_product_install_detected(home: Path | None = None) -> bool:
54
+ for candidate in _desktop_install_markers(home):
55
+ try:
56
+ if candidate.exists():
57
+ return True
58
+ except Exception:
59
+ continue
60
+ return False
61
+
62
+
63
+ def load_product_mode() -> dict[str, Any]:
64
+ path = product_mode_path()
65
+ try:
66
+ raw = json.loads(path.read_text())
67
+ except Exception:
68
+ return {}
69
+ return raw if isinstance(raw, dict) else {}
70
+
71
+
72
+ def save_product_mode(payload: dict[str, Any]) -> Path:
73
+ path = product_mode_path()
74
+ path.parent.mkdir(parents=True, exist_ok=True)
75
+ tmp_path = path.with_suffix(path.suffix + ".tmp")
76
+ tmp_path.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n")
77
+ tmp_path.replace(path)
78
+ return path
79
+
80
+
81
+ def desktop_product_requested() -> bool:
82
+ if str(os.environ.get(DESKTOP_PRODUCT_ENV, "")).strip() == "1":
83
+ return True
84
+ payload = load_product_mode()
85
+ if payload.get("desktop_managed") is True:
86
+ return True
87
+ if str(payload.get("product_mode") or "").strip().lower() == DESKTOP_PRODUCT_MODE:
88
+ return True
89
+ return desktop_product_install_detected()
90
+
91
+
92
+ def mark_desktop_product_managed(*, source: str = "desktop") -> dict[str, Any]:
93
+ existing = load_product_mode()
94
+ payload = dict(existing) if isinstance(existing, dict) else {}
95
+ payload.update(
96
+ {
97
+ "desktop_managed": True,
98
+ "product_mode": DESKTOP_PRODUCT_MODE,
99
+ "disabled_features": list(DESKTOP_DISABLED_FEATURES),
100
+ "updated_at": _now_iso(),
101
+ "source": str(source or "desktop").strip() or "desktop",
102
+ }
103
+ )
104
+ if not payload.get("created_at"):
105
+ payload["created_at"] = payload["updated_at"]
106
+ save_product_mode(payload)
107
+ return payload
108
+
109
+
110
+ def _default_objective_payload() -> dict[str, Any]:
111
+ return {
112
+ "objective": "Improve operational excellence and reduce repeated errors",
113
+ "focus_areas": ["error_prevention", "proactivity", "memory_quality"],
114
+ "evolution_enabled": False,
115
+ "evolution_mode": "review",
116
+ "dimensions": {
117
+ "episodic_memory": {"current": 0, "target": 90},
118
+ "autonomy": {"current": 0, "target": 80},
119
+ "proactivity": {"current": 0, "target": 70},
120
+ "self_improvement": {"current": 0, "target": 60},
121
+ "agi": {"current": 0, "target": 20},
122
+ },
123
+ "total_evolutions": 0,
124
+ "consecutive_failures": 0,
125
+ "created_at": _now_iso(),
126
+ }
127
+
128
+
129
+ def _normalize_objective(payload: dict[str, Any]) -> dict[str, Any]:
130
+ try:
131
+ from evolution_cycle import normalize_objective
132
+
133
+ return normalize_objective(payload)
134
+ except Exception:
135
+ return payload
136
+
137
+
138
+ def load_evolution_objective() -> tuple[Path, dict[str, Any]]:
139
+ objective_path = paths.brain_dir() / "evolution-objective.json"
140
+ try:
141
+ raw = json.loads(objective_path.read_text())
142
+ payload = raw if isinstance(raw, dict) else {}
143
+ except Exception:
144
+ payload = {}
145
+ if not payload:
146
+ payload = _default_objective_payload()
147
+ return objective_path, _normalize_objective(payload)
148
+
149
+
150
+ def enforce_desktop_product_contract(*, source: str = "desktop") -> dict[str, Any]:
151
+ if not desktop_product_requested():
152
+ return {"applied": False, "reason": "desktop_not_requested"}
153
+
154
+ mode_payload = mark_desktop_product_managed(source=source)
155
+ objective_path, objective = load_evolution_objective()
156
+ changed_objective = (
157
+ bool(objective.get("evolution_enabled", True))
158
+ or str(objective.get("disabled_reason") or "") != DESKTOP_EVOLUTION_DISABLED_REASON
159
+ )
160
+
161
+ objective["evolution_enabled"] = False
162
+ objective["disabled_reason"] = DESKTOP_EVOLUTION_DISABLED_REASON
163
+ objective["disabled_by"] = "desktop_product"
164
+ objective["desktop_managed"] = True
165
+ if not objective.get("created_at"):
166
+ objective["created_at"] = _now_iso()
167
+
168
+ objective_path.parent.mkdir(parents=True, exist_ok=True)
169
+ objective_path.write_text(json.dumps(objective, indent=2, ensure_ascii=False) + "\n")
170
+ return {
171
+ "applied": True,
172
+ "mode_path": str(product_mode_path()),
173
+ "objective_path": str(objective_path),
174
+ "changed_objective": changed_objective,
175
+ "mode": mode_payload,
176
+ }
177
+
178
+
179
+ def is_cron_blocked(cron_id: str | None) -> bool:
180
+ clean = str(cron_id or "").strip().lower()
181
+ return clean == "evolution" and desktop_product_requested()
182
+
183
+
184
+ def filter_blocked_crons(crons: list[dict[str, Any]]) -> list[dict[str, Any]]:
185
+ return [dict(cron) for cron in crons if not is_cron_blocked(cron.get("id"))]
186
+
187
+
188
+ def core_writes_allowed() -> bool:
189
+ return str(os.environ.get(ALLOW_CORE_WRITES_ENV, "")).strip() == "1"
190
+
191
+
192
+ def is_protected_runtime_core_path(file_path: str | os.PathLike[str] | None) -> bool:
193
+ raw = str(file_path or "").strip()
194
+ if not raw:
195
+ return False
196
+ try:
197
+ candidate = Path(raw).expanduser().resolve(strict=False)
198
+ candidate.relative_to(paths.core_dir().resolve(strict=False))
199
+ return True
200
+ except Exception:
201
+ return False
@@ -28,27 +28,12 @@ docs/client-parity-checklist.md).
28
28
  """
29
29
  from __future__ import annotations
30
30
 
31
+ from core_prompts import render_core_prompt
31
32
 
32
- CLASSIFIER_QUESTION = (
33
- "Is the user message below a correction of the assistant's previous "
34
- "answer or behaviour? Answer yes if the user is pushing back, "
35
- "disagreeing, contradicting, saying something was wrong, or teaching "
36
- "the assistant a rule it should have known. Answer no for simple "
37
- "questions, thanks, acknowledgments, neutral continuations, or "
38
- "delegations without feedback."
39
- )
40
-
41
-
42
- INJECTION_PROMPT_TEMPLATE = (
43
- "R14 post-user-correction: the last user message was classified as a "
44
- "correction (or carried strongly negative valence) and three tool calls "
45
- "have elapsed without a nexo_learning_add. Capture the rule you just "
46
- "learned NOW via nexo_learning_add(category=..., title=..., content=..., "
47
- "reasoning=..., prevention=...). The auto_capture hook fires in parallel, "
48
- "but this reminder stays active until you either call learning_add or "
49
- "acknowledge the correction via nexo_cognitive_trust(event='correction'). "
50
- "Do not produce visible text for this reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
51
- )
33
+ CLASSIFIER_QUESTION = render_core_prompt("r14-correction-learning-question")
34
+
35
+
36
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt("r14-correction-learning-injection")
52
37
 
53
38
 
54
39
  DEFAULT_WINDOW_TOOL_CALLS = 3
@@ -25,17 +25,11 @@ from __future__ import annotations
25
25
 
26
26
  import re
27
27
 
28
+ from core_prompts import render_core_prompt
28
29
 
29
- INJECTION_PROMPT_TEMPLATE = (
30
- "R15 pre-project-action: the last user message mentions project "
31
- "'{project}' but the session has no recent nexo_recall('{project}') "
32
- "nor a read of project-atlas.json. Before touching any file or cron "
33
- "for this project, load its context: run nexo_recall('{project}'), "
34
- "inspect entity_list(type='project'), and/or cat "
35
- "~/.nexo/brain/project-atlas.json. Learning #213 / #151 — assuming "
36
- "server, DNS, or repo details from memory without verifying leads "
37
- "to concrete incidents. Do not produce visible text for this "
38
- "reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
30
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
31
+ "r15-project-context-injection",
32
+ project="{project}",
39
33
  )
40
34
 
41
35
 
@@ -19,25 +19,12 @@ next tranche alongside the JS classifier infrastructure).
19
19
  """
20
20
  from __future__ import annotations
21
21
 
22
+ from core_prompts import render_core_prompt
22
23
 
23
- CLASSIFIER_QUESTION = (
24
- "Does the assistant message below declare that a task is finished, "
25
- "completed, shipped, or already done? Answer yes only if the assistant "
26
- "is claiming completion of the current work. Answer no for status "
27
- "updates, mid-task progress reports, questions, or partial summaries."
28
- )
24
+ CLASSIFIER_QUESTION = render_core_prompt("r16-declared-done-question")
29
25
 
30
26
 
31
- INJECTION_PROMPT_TEMPLATE = (
32
- "R16 declared-done without task_close: your last message claims the "
33
- "task is complete, but there is an open protocol_task. Close it now "
34
- "with nexo_task_close(sid=..., task_id=..., outcome='done', "
35
- "evidence='<substantive proof>', files_changed='...'). Evidence must "
36
- "be >= 50 chars AND not a single filler word (R03 validator will "
37
- "reject empty / 'ok' / 'done' / 'fixed'). If the work is partial, "
38
- "close with outcome='partial' and outcome_notes instead. Do not "
39
- "produce visible text for this reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
40
- )
27
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt("r16-declared-done-injection")
41
28
 
42
29
 
43
30
  def detect_declared_done(assistant_text: str, *, classifier=None) -> bool:
@@ -18,24 +18,11 @@ twins at the end of the tranche).
18
18
  """
19
19
  from __future__ import annotations
20
20
 
21
+ from core_prompts import render_core_prompt
21
22
 
22
- CLASSIFIER_QUESTION = (
23
- "Does the assistant message below explicitly promise a FUTURE action "
24
- "it has not yet executed? Answer yes if the assistant says it will do, "
25
- "create, send, implement, run, check, or perform something later or "
26
- "next. Answer no for status reports, summaries, questions, clarifying "
27
- "statements, or present-tense execution reports (where the action has "
28
- "already happened)."
29
- )
23
+ CLASSIFIER_QUESTION = render_core_prompt("r17-promise-debt-question")
30
24
 
31
- INJECTION_PROMPT_TEMPLATE = (
32
- "R17 promise-debt: your last message promised a future action but no "
33
- "tool call matching that promise happened in the next two turns. If "
34
- "the action is still pending, execute it NOW — promise without "
35
- "execution opens operational debt (learning #38 / #29 Fase A R29). "
36
- "If the promise was a misstatement, correct it explicitly in the next "
37
- "message. Do not produce visible text for this reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
38
- )
25
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt("r17-promise-debt-injection")
39
26
 
40
27
  DEFAULT_WINDOW_TOOL_CALLS = 2
41
28
 
@@ -18,14 +18,12 @@ and run the R04 matcher over it.
18
18
  """
19
19
  from __future__ import annotations
20
20
 
21
+ from core_prompts import render_core_prompt
21
22
 
22
- INJECTION_PROMPT_TEMPLATE = (
23
- "R18 retroactive-complete suggestion: the action you just performed "
24
- "matches {count} active followup(s) at Jaccard >= 0.70. If any of "
25
- "these is now resolved by that action, call nexo_followup_complete "
26
- "on it. Do NOT close a followup mechanically — verify the outcome "
27
- "first. Matching followups:\n{items}\nDo not produce visible text "
28
- "for this reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
23
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
24
+ "r18-followup-autocomplete-injection",
25
+ count="{count}",
26
+ items="{items}",
29
27
  )
30
28
 
31
29
 
@@ -11,15 +11,12 @@ owned by the caller (HeadlessEnforcer).
11
11
  """
12
12
  from __future__ import annotations
13
13
 
14
+ from core_prompts import render_core_prompt
14
15
 
15
- INJECTION_PROMPT_TEMPLATE = (
16
- "R19 pre-Write on project '{project}' without Grep: the project is "
17
- "flagged require_grep=true (shared-state hygiene — learning #144) "
18
- "and the recent tool history has no Grep call covering the function "
19
- "or constant about to change. BEFORE writing '{path}', run Grep "
20
- "for the symbol(s) you are introducing / modifying to confirm no "
21
- "other caller breaks. Do not produce visible text for this "
22
- "reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
16
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
17
+ "r19-project-grep-injection",
18
+ project="{project}",
19
+ path="{path}",
23
20
  )
24
21
 
25
22
 
@@ -27,23 +27,13 @@ from __future__ import annotations
27
27
 
28
28
  import re
29
29
 
30
+ from core_prompts import render_core_prompt
30
31
 
31
- CLASSIFIER_QUESTION = (
32
- "Does the code edit below modify a module-level constant, configuration "
33
- "key, enumeration entry, feature-flag, or shared global value? Answer "
34
- "yes only if the change affects a symbol that other files in the same "
35
- "project are likely to import or reference. Answer no for local "
36
- "variables, function bodies, doc-strings, or purely stylistic edits."
37
- )
32
+ CLASSIFIER_QUESTION = render_core_prompt("r20-constant-change-question")
38
33
 
39
- INJECTION_PROMPT_TEMPLATE = (
40
- "R20 constant-change without grep: the edit on {path} appears to touch "
41
- "a module-level constant / config key / shared global, but there is no "
42
- "Grep of the symbol in the recent tool history. Before shipping this "
43
- "change, run Grep (or `rg`) against the whole repo for the old symbol "
44
- "value to confirm every caller is updated (or tolerates the new value). "
45
- "Learning #144 and the R20 contract require grep-all-usages on shared "
46
- "state. Do not produce visible text for this reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
34
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
35
+ "r20-constant-change-injection",
36
+ path="{path}",
47
37
  )
48
38
 
49
39
 
@@ -12,14 +12,12 @@ Operators may add more via nexo_entity_create.
12
12
  """
13
13
  from __future__ import annotations
14
14
 
15
+ from core_prompts import render_core_prompt
15
16
 
16
- INJECTION_PROMPT_TEMPLATE = (
17
- "R21 legacy path: the operation targets '{legacy}' but that path is "
18
- "superseded by '{canonical}' in the entity registry (type=legacy_path). "
19
- "Rerun the operation against the canonical path. Operators on v6+ "
20
- "runtime do not keep the legacy tree alive; writes there end up "
21
- "orphaned at the next maintenance sweep. Do not produce visible text "
22
- "for this reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
17
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
18
+ "r21-legacy-path-injection",
19
+ legacy="{legacy}",
20
+ canonical="{canonical}",
23
21
  )
24
22
 
25
23
 
@@ -16,15 +16,11 @@ Structural check against recent_tool_records, no LLM.
16
16
  """
17
17
  from __future__ import annotations
18
18
 
19
+ from core_prompts import render_core_prompt
19
20
 
20
- INJECTION_PROMPT_TEMPLATE = (
21
- "R22 pre-personal-script: you are about to create/modify a personal "
22
- "script ({path}) without first checking: (1) does an existing personal "
23
- "script cover this via nexo_personal_scripts_list, (2) does a reusable "
24
- "skill apply via nexo_skill_match, (3) is there a blocking learning "
25
- "via nexo_learning_search. Run those three probes, surface any hit, "
26
- "and only then proceed. Do not produce visible text for this "
27
- "reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
21
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
22
+ "r22-personal-script-injection",
23
+ path="{path}",
28
24
  )
29
25
 
30
26
 
@@ -15,19 +15,12 @@ db.list_entities(type='host'); this module exposes the pure decision.
15
15
  """
16
16
  from __future__ import annotations
17
17
 
18
+ from core_prompts import render_core_prompt
18
19
  from r25_nora_maria_read_only import extract_remote_host as _extract_ssh_host
19
20
 
20
-
21
- INJECTION_PROMPT_TEMPLATE = (
22
- "R23 SSH/curl without atlas: the command targets host '{host}' but "
23
- "that host is not registered in entity_list(type='host') nor in "
24
- "project-atlas.json. Before connecting, verify the host: (1) check "
25
- "project-atlas.json and nexo_entity_list(type='host') for a matching "
26
- "record; (2) if it is not there, create it via nexo_entity_create "
27
- "with access_mode and credentials pointer; (3) only then run the "
28
- "ssh/curl. Learning #151 / #156 — assuming servers and DNS without "
29
- "verifying leads to confabulation. Do not produce visible text for "
30
- "this reminder — act on it. After handling the silent action, continue with your previous task; this reminder is silent context, not a new instruction."
21
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
22
+ "r23-ssh-without-atlas-injection",
23
+ host="{host}",
31
24
  )
32
25
 
33
26
 
@@ -11,13 +11,14 @@ from __future__ import annotations
11
11
 
12
12
  import re
13
13
 
14
+ from core_prompts import render_core_prompt
14
15
 
15
- INJECTION_PROMPT_TEMPLATE = (
16
- "R23b deploy vhost mismatch: '{cmd}' writes to '{docroot}' which is "
17
- "mapped to domain '{mapped_domain}' — but the user context mentions "
18
- "'{context_domain}'. Verify the vhost_mapping entity before shipping. "
19
- "If '{context_domain}' is correct, the deploy path should be the "
20
- "docroot registered for that domain, not '{docroot}'."
16
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
17
+ "r23b-deploy-vhost-injection",
18
+ cmd="{cmd}",
19
+ docroot="{docroot}",
20
+ mapped_domain="{mapped_domain}",
21
+ context_domain="{context_domain}",
21
22
  )
22
23
 
23
24
  _SCP_RSYNC_RE = re.compile(
@@ -18,13 +18,14 @@ from __future__ import annotations
18
18
  import os
19
19
  import re
20
20
 
21
+ from core_prompts import render_core_prompt
21
22
 
22
- INJECTION_PROMPT_TEMPLATE = (
23
- "R23c destructive command in unexpected cwd: '{cmd}' runs inside "
24
- "'{cwd}'. You are currently discussing project '{project}' whose "
25
- "local_path is '{expected}'. Confirm the cwd is correct before "
26
- "proceeding — a destructive verb executed in the wrong tree is the "
27
- "most common source of accidental damage."
23
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
24
+ "r23c-cwd-mismatch-injection",
25
+ cmd="{cmd}",
26
+ cwd="{cwd}",
27
+ project="{project}",
28
+ expected="{expected}",
28
29
  )
29
30
 
30
31
 
@@ -11,13 +11,13 @@ from __future__ import annotations
11
11
 
12
12
  import re
13
13
 
14
+ from core_prompts import render_core_prompt
14
15
 
15
- INJECTION_PROMPT_TEMPLATE = (
16
- "R23d recursive {verb} without prior inspection: '{cmd}' targets "
17
- "'{target}'. Run `ls -la {target}` (or equivalent) in this session "
18
- "before executing a recursive ownership/permission change — silently "
19
- "chown-R'ing a root-ish tree is one of the fastest ways to render a "
20
- "box unbootable."
16
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
17
+ "r23d-chown-chmod-recursive-injection",
18
+ verb="{verb}",
19
+ cmd="{cmd}",
20
+ target="{target}",
21
21
  )
22
22
 
23
23
 
@@ -8,13 +8,12 @@ from __future__ import annotations
8
8
 
9
9
  import re
10
10
 
11
+ from core_prompts import render_core_prompt
11
12
 
12
- INJECTION_PROMPT_TEMPLATE = (
13
- "R23e force push to {branch}: the command '{cmd}' would rewrite a "
14
- "protected branch. Force-push to main/master/production/release-* is "
15
- "never automatic — stop and confirm with the operator before proceeding. "
16
- "If the intent is to recover a broken state, prefer `git revert` + a "
17
- "fresh commit over a history rewrite."
13
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
14
+ "r23e-force-push-main-injection",
15
+ branch="{branch}",
16
+ cmd="{cmd}",
18
17
  )
19
18
 
20
19
  # Matches `git push [options] [remote] BRANCH` where --force or -f appears
@@ -9,13 +9,12 @@ from __future__ import annotations
9
9
 
10
10
  import re
11
11
 
12
+ from core_prompts import render_core_prompt
12
13
 
13
- INJECTION_PROMPT_TEMPLATE = (
14
- "R23f destructive SQL without WHERE: the command '{cmd}' contains a "
15
- "{verb} statement with no WHERE clause against a production DB. "
16
- "Abort. If the intent is a full wipe, run it interactively against "
17
- "the DB console with explicit operator confirmation, not via the "
18
- "enforcement wrapper."
14
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
15
+ "r23f-db-no-where-injection",
16
+ cmd="{cmd}",
17
+ verb="{verb}",
19
18
  )
20
19
 
21
20
  # Simple SQL heuristic. Looks for DELETE FROM <tbl> or UPDATE <tbl> SET
@@ -12,12 +12,12 @@ from __future__ import annotations
12
12
 
13
13
  import re
14
14
 
15
+ from core_prompts import render_core_prompt
15
16
 
16
- INJECTION_PROMPT_TEMPLATE = (
17
- "R23g secrets reaching output stream: '{cmd}' is likely to expose "
18
- "credentials ({reason}). Redact before logging/emailing, or route "
19
- "the value through `nexo_credential_get` so the secret stays behind "
20
- "the secret manager boundary."
17
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
18
+ "r23g-secrets-in-output-injection",
19
+ cmd="{cmd}",
20
+ reason="{reason}",
21
21
  )
22
22
 
23
23
 
@@ -15,12 +15,13 @@ import os
15
15
  import re
16
16
  import shutil
17
17
 
18
+ from core_prompts import render_core_prompt
18
19
 
19
- INJECTION_PROMPT_TEMPLATE = (
20
- "R23h shebang mismatch: '{script}' declares interpreter '{shebang}' "
21
- "but the shell resolves it to '{actual}'. Align versions before "
22
- "running — interpreter drift produces the hardest-to-diagnose "
23
- "runtime bugs."
20
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
21
+ "r23h-shebang-mismatch-injection",
22
+ script="{script}",
23
+ shebang="{shebang}",
24
+ actual="{actual}",
24
25
  )
25
26
 
26
27
 
@@ -12,13 +12,12 @@ from __future__ import annotations
12
12
 
13
13
  import re
14
14
 
15
+ from core_prompts import render_core_prompt
15
16
 
16
- INJECTION_PROMPT_TEMPLATE = (
17
- "R23i auto-deploy after recent push: project '{project}' has "
18
- "auto_deploy=true and you just pushed. The edit you are about to "
19
- "make to '{path}' will be overwritten on the next deploy unless "
20
- "you also commit+push it. Either commit the change or disable "
21
- "auto-deploy temporarily."
17
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
18
+ "r23i-auto-deploy-ignored-injection",
19
+ project="{project}",
20
+ path="{path}",
22
21
  )
23
22
 
24
23
 
@@ -11,13 +11,12 @@ from __future__ import annotations
11
11
 
12
12
  import re
13
13
 
14
+ from core_prompts import render_core_prompt
14
15
 
15
- INJECTION_PROMPT_TEMPLATE = (
16
- "R23j global install without explicit request: '{cmd}' installs "
17
- "'{pkg}' globally. Prefer a project-scoped install (venv, nvm, "
18
- "`--save`, local brew cask) unless the operator asked for a global "
19
- "tool. If the operator explicitly asked, retry after saying "
20
- "`yes install globally` or equivalent."
16
+ INJECTION_PROMPT_TEMPLATE = render_core_prompt(
17
+ "r23j-global-install-injection",
18
+ cmd="{cmd}",
19
+ pkg="{pkg}",
21
20
  )
22
21
 
23
22