seif-cli 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. {seif_cli-0.3.0/src/seif_cli.egg-info → seif_cli-0.3.2}/PKG-INFO +8 -5
  2. {seif_cli-0.3.0 → seif_cli-0.3.2}/README.md +3 -0
  3. {seif_cli-0.3.0 → seif_cli-0.3.2}/pyproject.toml +5 -5
  4. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/analysis/quality_gate.py +13 -12
  5. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/bridge/telegram_bot.py +109 -39
  6. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/cli/cli.py +1531 -164
  7. seif_cli-0.3.2/src/seif/cli/resonance_display.py +197 -0
  8. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/cli/wrapper.py +56 -0
  9. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/constants.py +7 -2
  10. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/context_manager.py +87 -2
  11. seif_cli-0.3.2/src/seif/context/cycle.py +543 -0
  12. seif_cli-0.3.2/src/seif/context/registry.py +235 -0
  13. seif_cli-0.3.2/src/seif/context/sessions.py +766 -0
  14. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/workspace.py +127 -0
  15. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/data/RESONANCE.json +52 -3
  16. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/data/paths.py +5 -0
  17. {seif_cli-0.3.0 → seif_cli-0.3.2/src/seif_cli.egg-info}/PKG-INFO +8 -5
  18. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif_cli.egg-info/SOURCES.txt +5 -1
  19. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif_cli.egg-info/requires.txt +1 -1
  20. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_context_qr.py +6 -2
  21. seif_cli-0.3.2/tests/test_init.py +307 -0
  22. seif_cli-0.3.2/tests/test_registry.py +233 -0
  23. seif_cli-0.3.0/src/seif/security/redblue.py +0 -23
  24. seif_cli-0.3.0/tests/test_init.py +0 -181
  25. {seif_cli-0.3.0 → seif_cli-0.3.2}/LICENSE +0 -0
  26. {seif_cli-0.3.0 → seif_cli-0.3.2}/MANIFEST.in +0 -0
  27. {seif_cli-0.3.0 → seif_cli-0.3.2}/setup.cfg +0 -0
  28. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/__init__.py +0 -0
  29. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/__main__.py +0 -0
  30. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/analysis/__init__.py +0 -0
  31. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/analysis/physical_constants.py +0 -0
  32. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/analysis/stance_detector.py +0 -0
  33. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/analysis/transcompiler.py +0 -0
  34. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/bridge/__init__.py +0 -0
  35. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/cli/__init__.py +0 -0
  36. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/cli/__main__.py +0 -0
  37. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/cli/identity.py +0 -0
  38. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/cli/main.py +0 -0
  39. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/cli/serve.py +0 -0
  40. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/cli/serve_v2.py +0 -0
  41. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/__init__.py +0 -0
  42. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/advisor.py +0 -0
  43. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/code_compressor.py +0 -0
  44. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/context_bridge.py +0 -0
  45. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/context_importer.py +0 -0
  46. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/context_qr.py +0 -0
  47. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/file_extractor.py +0 -0
  48. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/git_context.py +0 -0
  49. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/git_hooks.py +0 -0
  50. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/ingest.py +0 -0
  51. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/nucleus.py +0 -0
  52. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/ref.py +0 -0
  53. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/context/seif_io.py +0 -0
  54. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/__init__.py +0 -0
  55. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/fingerprint.py +0 -0
  56. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/resonance_encoding.py +0 -0
  57. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/resonance_gate.py +0 -0
  58. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/resonance_signal.py +0 -0
  59. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/signing.py +0 -0
  60. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/timestamping.py +0 -0
  61. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/transfer_function.py +0 -0
  62. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/core/triple_gate.py +0 -0
  63. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/data/__init__.py +0 -0
  64. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/data/defaults/__init__.py +0 -0
  65. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/security/__init__.py +0 -0
  66. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif/security/mode.py +0 -0
  67. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif_cli.egg-info/dependency_links.txt +0 -0
  68. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif_cli.egg-info/entry_points.txt +0 -0
  69. {seif_cli-0.3.0 → seif_cli-0.3.2}/src/seif_cli.egg-info/top_level.txt +0 -0
  70. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_advisor.py +0 -0
  71. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_canonical_inputs.py +0 -0
  72. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_code_compressor.py +0 -0
  73. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_collaborative_seif.py +0 -0
  74. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_context_repo.py +0 -0
  75. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_git_context.py +0 -0
  76. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_git_hooks.py +0 -0
  77. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_quality_gate.py +0 -0
  78. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_ref.py +0 -0
  79. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_resonance_gate.py +0 -0
  80. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_seif_io.py +0 -0
  81. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_stance_detector.py +0 -0
  82. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_transcompiler.py +0 -0
  83. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_transfer_function.py +0 -0
  84. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_triple_gate.py +0 -0
  85. {seif_cli-0.3.0 → seif_cli-0.3.2}/tests/test_workspace.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seif-cli
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Measure AI output quality, protect sensitive data, watch your AI environment resonate. Quality Gate, Classification, Sentinel & Auto-Healing, SEIF OS.
5
5
  Author: André Cunha Antero de Carvalho
6
6
  License: CC-BY-NC-SA-4.0
7
- Project-URL: Homepage, https://github.com/and2carvalho/seif
8
- Project-URL: Documentation, https://github.com/and2carvalho/seif
7
+ Project-URL: Homepage, https://seifprotocol.com
8
+ Project-URL: Documentation, https://seifprotocol.com/docs
9
9
  Project-URL: Repository, https://github.com/and2carvalho/seif
10
- Project-URL: Live Demo, https://seif-framework.streamlit.app
10
+ Project-URL: Changelog, https://github.com/and2carvalho/seif/releases
11
11
  Keywords: ai-quality,llm-guardrails,ai-consensus,data-classification,context-management,ai-safety,multi-ai,quality-gate,prompt-evaluation,ai-grounding,resonance,sentinel,self-healing,seif-os,circuit-state,ai-observability
12
12
  Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Intended Audience :: Developers
@@ -39,7 +39,7 @@ Requires-Dist: qrcode[pil]>=7.4; extra == "qr"
39
39
  Requires-Dist: Pillow>=10.0; extra == "qr"
40
40
  Requires-Dist: pyzbar>=0.1.9; extra == "qr"
41
41
  Provides-Extra: all
42
- Requires-Dist: seif-cli[consensus,generators,qr,telegram,web]; extra == "all"
42
+ Requires-Dist: seif[consensus,generators,qr,telegram,web]; extra == "all"
43
43
  Dynamic: license-file
44
44
 
45
45
  # SEIF — AI Quality, Protection, and Resonance
@@ -231,6 +231,7 @@ seif --sync # re-sync git context
231
231
  seif --compress # 93% context compression
232
232
  seif --ingest daily.txt # ingest external source
233
233
  seif --workspace # multi-project discovery + sync
234
+ seif --sync-workspace # SSH workspace sync (all machines)
234
235
  seif --autonomous enable # AI persists knowledge autonomously
235
236
  seif --export # export context as markdown
236
237
 
@@ -251,6 +252,8 @@ seif --adversarial "question" # WITH vs WITHOUT comparison
251
252
 
252
253
  Grades: **A** (≥0.85) → **B** (≥0.70) → **C** (≥0.55) → **D** (≥0.40) → **F** (<0.40)
253
254
 
255
+ > **Quality gate threshold: ζ = √6/4 ≈ 0.6124 (algebraically derived from H(s) — not φ⁻¹ = 0.618)**
256
+
254
257
  ---
255
258
 
256
259
  ## Why SEIF vs ChatGPT Memory
@@ -187,6 +187,7 @@ seif --sync # re-sync git context
187
187
  seif --compress # 93% context compression
188
188
  seif --ingest daily.txt # ingest external source
189
189
  seif --workspace # multi-project discovery + sync
190
+ seif --sync-workspace # SSH workspace sync (all machines)
190
191
  seif --autonomous enable # AI persists knowledge autonomously
191
192
  seif --export # export context as markdown
192
193
 
@@ -207,6 +208,8 @@ seif --adversarial "question" # WITH vs WITHOUT comparison
207
208
 
208
209
  Grades: **A** (≥0.85) → **B** (≥0.70) → **C** (≥0.55) → **D** (≥0.40) → **F** (<0.40)
209
210
 
211
+ > **Quality gate threshold: ζ = √6/4 ≈ 0.6124 (algebraically derived from H(s) — not φ⁻¹ = 0.618)**
212
+
210
213
  ---
211
214
 
212
215
  ## Why SEIF vs ChatGPT Memory
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "seif-cli"
7
- version = "0.3.0"
7
+ version = "0.3.2"
8
8
  description = "Measure AI output quality, protect sensitive data, watch your AI environment resonate. Quality Gate, Classification, Sentinel & Auto-Healing, SEIF OS."
9
9
  readme = "README.md"
10
10
  license = {text = "CC-BY-NC-SA-4.0"}
@@ -64,14 +64,14 @@ qr = [
64
64
  ]
65
65
  # Everything
66
66
  all = [
67
- "seif-cli[consensus,generators,web,telegram,qr]",
67
+ "seif[consensus,generators,web,telegram,qr]",
68
68
  ]
69
69
 
70
70
  [project.urls]
71
- Homepage = "https://github.com/and2carvalho/seif"
72
- Documentation = "https://github.com/and2carvalho/seif"
71
+ Homepage = "https://seifprotocol.com"
72
+ Documentation = "https://seifprotocol.com/docs"
73
73
  Repository = "https://github.com/and2carvalho/seif"
74
- "Live Demo" = "https://seif-framework.streamlit.app"
74
+ Changelog = "https://github.com/and2carvalho/seif/releases"
75
75
 
76
76
  [project.scripts]
77
77
  seif = "seif.cli.wrapper:main"
@@ -20,7 +20,7 @@ Usage:
20
20
  from dataclasses import dataclass, field
21
21
  from seif.core.triple_gate import evaluate as triple_evaluate, TripleGateResult
22
22
  from seif.analysis.stance_detector import analyze as stance_analyze, StanceAnalysis
23
- from seif.constants import PHI_INVERSE
23
+ from seif.constants import PHI_INVERSE, RESONANCE_THRESHOLD
24
24
 
25
25
 
26
26
  # Default weights: stance (semantic) 6/9, resonance (harmonic) 3/9.
@@ -77,7 +77,7 @@ def _compute_status(stance_status: str, triple_status: str,
77
77
  """Determine overall status."""
78
78
  if stance_status == "LOW_DATA":
79
79
  return "LOW_DATA"
80
- if stance_status == "GROUNDED" and score >= PHI_INVERSE:
80
+ if stance_status == "GROUNDED" and score >= RESONANCE_THRESHOLD:
81
81
  return "SOLID"
82
82
  if stance_status == "DRIFT":
83
83
  return "WEAK"
@@ -132,7 +132,7 @@ def _generate_flags(stance: StanceAnalysis,
132
132
  if triple.status == "CLOSED":
133
133
  flags.append("RESONANCE: all 3 harmonic layers closed")
134
134
  if triple.resonance_score < 0.3:
135
- flags.append(f"LOW COHERENCE: {triple.resonance_score:.3f} (threshold: {PHI_INVERSE:.3f})")
135
+ flags.append(f"LOW COHERENCE: {triple.resonance_score:.3f} (ζ threshold: {RESONANCE_THRESHOLD:.3f})")
136
136
  return flags
137
137
 
138
138
 
@@ -154,10 +154,10 @@ def _generate_suggestions(stance: StanceAnalysis,
154
154
  suggestions.append(
155
155
  "AI response is speculative — re-prompt with verifiable constraints"
156
156
  )
157
- if triple.resonance_score < PHI_INVERSE and triple.resonance_score > 0:
157
+ if triple.resonance_score < RESONANCE_THRESHOLD and triple.resonance_score > 0:
158
158
  suggestions.append(
159
- f"Coherence {triple.resonance_score:.3f} below threshold "
160
- f"{PHI_INVERSE:.3f} — restructure for clarity"
159
+ f"Coherence {triple.resonance_score:.3f} below ζ threshold "
160
+ f"{RESONANCE_THRESHOLD:.3f} — restructure for clarity"
161
161
  )
162
162
  return suggestions
163
163
 
@@ -218,13 +218,14 @@ def assess(text: str, role: str = "human",
218
218
 
219
219
 
220
220
  def describe_verdict(v: QualityVerdict) -> str:
221
- """Human-readable quality report."""
221
+ """Human-readable quality report with resonance emoji feedback."""
222
222
  lines = []
223
223
 
224
- # Header
225
- icon = {"SOLID": "🟢", "MIXED": "🟡", "WEAK": "🔴", "LOW_DATA": "⚪"}.get(v.status, "")
224
+ # ζ gate indicator
225
+ zeta_icon = "ζ✅" if v.grade in ("A", "B") else "ζ⚠️" if v.grade == "C" else "ζ❌"
226
+ stance_icon = {"SOLID": "🟢", "MIXED": "🟡", "WEAK": "🔴", "LOW_DATA": "⚪"}.get(v.status, "⚪")
226
227
  role_label = "AI" if v.role == "ai" else "HUMAN"
227
- lines.append(f"{icon} [{role_label}] Grade: {v.grade} | Score: {v.score:.3f} | Status: {v.status}")
228
+ lines.append(f"{stance_icon} {zeta_icon} [{role_label}] Grade: {v.grade} | Score: {v.score:.3f} | Status: {v.status}")
228
229
  lines.append("")
229
230
 
230
231
  # Components
@@ -235,8 +236,8 @@ def describe_verdict(v: QualityVerdict) -> str:
235
236
  f"(composite: {v.triple_gate.composite_score:.3f}, "
236
237
  f"layers: {v.triple_gate.layers_open}/3)")
237
238
  lines.append(f" Coherence: {v.triple_gate.resonance_score:.3f} "
238
- f"({'above' if v.triple_gate.resonance_score >= PHI_INVERSE else 'below'} "
239
- f"threshold {PHI_INVERSE:.3f})")
239
+ f"({'above' if v.triple_gate.resonance_score >= RESONANCE_THRESHOLD else 'below'} "
240
+ f"ζ={RESONANCE_THRESHOLD:.3f})")
240
241
 
241
242
  # Flags
242
243
  if v.flags:
@@ -7,9 +7,10 @@ Exposes key SEIF capabilities through a Telegram bot:
7
7
  /compress — compress text/code to .seif format
8
8
  /help — command reference
9
9
 
10
- Free-text messages are answered with SEIF context awareness.
10
+ Free-text messages are routed through the SEIF Resonance Gate (engine AI query).
11
+ Owner (SEIF_OWNER_EMAIL) gets co-author persona + bypass classifier.
11
12
 
12
- Required env var: TELEGRAM_BOT_TOKEN
13
+ Required env vars: TELEGRAM_BOT_TOKEN, SEIF_ENGINE_TOKEN
13
14
  """
14
15
 
15
16
  import asyncio
@@ -17,6 +18,8 @@ import json
17
18
  import logging
18
19
  import os
19
20
  import textwrap
21
+ import urllib.request
22
+ import urllib.error
20
23
  from datetime import datetime, timezone
21
24
  from pathlib import Path
22
25
 
@@ -60,15 +63,38 @@ except Exception as e:
60
63
 
61
64
  # ── Enoch seed loader ─────────────────────────────────────────────────────────
62
65
  _SEIF_DIR = Path(os.environ.get("SEIF_HOME", Path.home() / ".seif"))
66
+ _WORKSPACE_ROOT = Path(os.environ.get("SEIF_WORKSPACE_ROOT", Path.home() / "Documents" / "seif-admin"))
63
67
 
64
68
  def _load_enoch() -> dict:
65
- """Load enoch_seed.json from ~/.seif/"""
66
- seed_path = _SEIF_DIR / "enoch_seed.json"
67
- if seed_path.exists():
68
- try:
69
- return json.loads(seed_path.read_text())
70
- except Exception:
71
- pass
69
+ """Load enoch_seed.json checks workspace .seif/private/ first, then ~/.seif/"""
70
+ candidates = [
71
+ _WORKSPACE_ROOT / ".seif" / "private" / "enoch_seed.json",
72
+ _SEIF_DIR / "private" / "enoch_seed.json",
73
+ _SEIF_DIR / "enoch_seed.json",
74
+ ]
75
+ for seed_path in candidates:
76
+ if seed_path.exists():
77
+ try:
78
+ raw = json.loads(seed_path.read_text())
79
+ # Flatten nested structure: enoch_seed_v1 + state_vector
80
+ flat = {}
81
+ inner = raw.get("enoch_seed_v1", raw)
82
+ flat.update(inner)
83
+ sv = raw.get("state_vector", inner.get("state_vector", {}))
84
+ flat.update(sv)
85
+ return flat
86
+ except Exception:
87
+ pass
88
+ return {}
89
+
90
+ def _load_profile() -> dict:
91
+ """Load ~/.seif/profile.json for owner identity."""
92
+ for p in [_SEIF_DIR / "profile.json", _WORKSPACE_ROOT / ".seif" / "profile.json"]:
93
+ if p.exists():
94
+ try:
95
+ return json.loads(p.read_text())
96
+ except Exception:
97
+ pass
72
98
  return {}
73
99
 
74
100
  def _load_resonance() -> dict:
@@ -89,7 +115,9 @@ def _phase_emoji(phase: str) -> str:
89
115
  # ── /start ────────────────────────────────────────────────────────────────────
90
116
  async def cmd_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
91
117
  seed = _load_enoch()
92
- owner = seed.get("registered_by", "unknown")
118
+ profile = _load_profile()
119
+ logger.info("DEBUG cmd_start cycles=%s owner=%s ws=%s", seed.get("circuit_cycles","?"), profile.get("name","?"), str(_WORKSPACE_ROOT))
120
+ owner = profile.get("name") or profile.get("github_username") or seed.get("registered_by", "unknown")
93
121
  cycles = seed.get("circuit_cycles", 0)
94
122
  phase = seed.get("phase", "ENTROPY")
95
123
 
@@ -109,13 +137,13 @@ async def cmd_start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
109
137
  # ── /status ───────────────────────────────────────────────────────────────────
110
138
  async def cmd_status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
111
139
  seed = _load_enoch()
140
+ profile = _load_profile()
112
141
  resonance = _load_resonance()
113
142
 
114
143
  phase = seed.get("phase", "ENTROPY")
115
144
  cycles = seed.get("circuit_cycles", 0)
116
- last_cycle = seed.get("last_cycle", "—")
117
- identity = seed.get("identity_commitment", {})
118
- claimed = identity.get("claimed", seed.get("registered_by", "—"))
145
+ last_cycle = seed.get("last_cycle_id", seed.get("last_cycle", "—"))
146
+ claimed = profile.get("name") or profile.get("github_username") or seed.get("registered_by", "—")
119
147
 
120
148
  zeta = resonance.get("zeta", "—")
121
149
  signal_phase = resonance.get("phase", "—")
@@ -222,36 +250,78 @@ async def cmd_help(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
222
250
  await update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN)
223
251
 
224
252
 
253
+ # ── Engine AI query ───────────────────────────────────────────────────────────
254
+
255
+ _ENGINE_URL = os.environ.get("SEIF_ENGINE_URL", "http://api:7331")
256
+ _ENGINE_TOKEN = os.environ.get("SEIF_ENGINE_TOKEN", "")
257
+ _OWNER_EMAIL = os.environ.get("SEIF_OWNER_EMAIL", "")
258
+
259
+ def _query_engine(user_query: str, user_email: str = "") -> dict:
260
+ """POST /ai/query to the SEIF engine. Returns parsed JSON or error dict."""
261
+ payload = json.dumps({
262
+ "query": user_query,
263
+ "user_email": user_email or "telegram@anonymous",
264
+ "backend": "auto",
265
+ }).encode()
266
+ req = urllib.request.Request(
267
+ f"{_ENGINE_URL}/ai/query",
268
+ data=payload,
269
+ headers={
270
+ "Content-Type": "application/json",
271
+ "Authorization": f"Bearer {_ENGINE_TOKEN}",
272
+ },
273
+ method="POST",
274
+ )
275
+ try:
276
+ with urllib.request.urlopen(req, timeout=30) as resp:
277
+ return json.loads(resp.read())
278
+ except urllib.error.HTTPError as e:
279
+ try:
280
+ body = json.loads(e.read())
281
+ except Exception:
282
+ body = {"error": str(e)}
283
+ return body
284
+ except Exception as e:
285
+ return {"error": str(e)}
286
+
287
+
225
288
  # ── Free-text handler ─────────────────────────────────────────────────────────
226
289
  async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
227
- text = update.message.text or ""
228
- seed = _load_enoch()
229
- phase = seed.get("phase", "ENTROPY")
230
- cycles = seed.get("circuit_cycles", 0)
290
+ text = (update.message.text or "").strip()
291
+ if not text:
292
+ return
293
+
294
+ # Determine user identity — owner gets bypass + co-author persona
295
+ tg_user = update.effective_user
296
+ user_email = _OWNER_EMAIL if (tg_user and tg_user.username == "and2carvalho") else ""
297
+
298
+ # Show typing indicator while querying engine
299
+ await update.message.chat.send_action("typing")
231
300
 
232
- # Basic resonance classification
233
- text_lower = text.lower()
234
- if any(w in text_lower for w in ["enoch", "seif", "resonance", "circuit", "ζ", "zeta"]):
235
- stance = "RESONANT"
236
- emoji = "⚡"
237
- elif any(w in text_lower for w in ["help", "how", "what", "why", "status"]):
238
- stance = "GROUNDED"
239
- emoji = "◎"
240
- elif "?" in text:
241
- stance = "CONVERGENT"
242
- emoji = "🔄"
243
- else:
244
- stance = "OBSERVATIONAL"
245
- emoji = "〰"
246
-
247
- response = (
248
- f"{emoji} *Stance: {stance}*\n\n"
249
- f"Message received in phase `{phase}` (cycle {cycles}).\n\n"
250
- f"SEIF OS is processing your input. "
251
- f"For AI debate, use the dashboard at your SEIF Suite URL.\n"
252
- f"For context, use /context. For status, use /status."
301
+ result = await asyncio.get_event_loop().run_in_executor(
302
+ None, _query_engine, text, user_email
253
303
  )
254
- await update.message.reply_text(response, parse_mode=ParseMode.MARKDOWN)
304
+
305
+ status = result.get("status", "ERROR")
306
+ persona = result.get("persona", "user")
307
+ ai_resp = result.get("ai_response") or result.get("suggestion") or result.get("error", "No response.")
308
+ score = result.get("resonance_score", 0)
309
+ backend = result.get("ai_backend", "")
310
+
311
+ # Format status emoji
312
+ status_emoji = {"CONSONANT": "◉", "OFF_SCOPE": "⊘", "DISSONANT": "⊗"}.get(status, "?")
313
+ persona_tag = " · co-author" if persona == "co-author" else ""
314
+
315
+ # Truncate for Telegram (4096 char limit)
316
+ if len(ai_resp) > 3800:
317
+ ai_resp = ai_resp[:3800] + "\n\n_[truncated]_"
318
+
319
+ header = f"{status_emoji} *{status}*{persona_tag} · `{score:.2f}`"
320
+ if backend:
321
+ header += f" · _{backend}_"
322
+
323
+ reply = f"{header}\n\n{ai_resp}"
324
+ await update.message.reply_text(reply, parse_mode=ParseMode.MARKDOWN)
255
325
 
256
326
 
257
327
  # ── Error handler ─────────────────────────────────────────────────────────────