cite-agent 1.2.8__tar.gz → 1.2.9__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.
Potentially problematic release.
This version of cite-agent might be problematic. Click here for more details.
- {cite_agent-1.2.8/cite_agent.egg-info → cite_agent-1.2.9}/PKG-INFO +1 -1
- cite_agent-1.2.9/cite_agent/__version__.py +1 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/cli.py +2 -3
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/enhanced_ai_agent.py +174 -14
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/session_manager.py +34 -25
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/setup_config.py +33 -24
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/updater.py +3 -3
- {cite_agent-1.2.8 → cite_agent-1.2.9/cite_agent.egg-info}/PKG-INFO +1 -1
- {cite_agent-1.2.8 → cite_agent-1.2.9}/setup.py +1 -1
- cite_agent-1.2.8/cite_agent/__version__.py +0 -1
- {cite_agent-1.2.8 → cite_agent-1.2.9}/LICENSE +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/MANIFEST.in +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/README.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/__main__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/account_client.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/agent_backend_only.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/ascii_plotting.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/auth.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/backend_only_client.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/cli_conversational.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/cli_enhanced.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/cli_workflow.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/dashboard.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/rate_limiter.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/streaming_ui.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/telemetry.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/ui.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/web_search.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/workflow.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent/workflow_integration.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent.egg-info/SOURCES.txt +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent.egg-info/dependency_links.txt +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent.egg-info/entry_points.txt +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent.egg-info/requires.txt +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/cite_agent.egg-info/top_level.txt +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/BETA_LAUNCH_CHECKLIST.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/BETA_RELEASE_CHECKLIST.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/ENHANCED_CAPABILITIES.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/GROQ_RATE_LIMITS.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/INSTALL.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/PUBLISHING_PYPI.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/SECURE_PACKAGING_GUIDE.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/SECURITY_AUDIT.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/USER_GETTING_STARTED.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/docs/playbooks/BETA_LAUNCH_PLAYBOOK.md +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/requirements.txt +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/setup.cfg +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/auth_service/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/auth_service/auth_manager.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/graph/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/graph/knowledge_graph.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/llm_service/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/llm_service/llm_manager.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/paper_service/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/paper_service/openalex.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/performance_service/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/performance_service/rust_performance.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/chatbot.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/citation_manager.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/context_manager.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/conversation_manager.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/critical_paper_detector.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/enhanced_research.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/enhanced_synthesizer.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/query_generator.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/synthesizer.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/search_service/__init__.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/search_service/indexer.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/search_service/search_engine.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/simple_enhanced_main.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/beta_launch_test_suite.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/enhanced/test_account_client.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/enhanced/test_archive_agent.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/enhanced/test_enhanced_agent_runtime.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/enhanced/test_reasoning_engine.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/enhanced/test_setup_config.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/enhanced/test_tool_framework.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/integration_test.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/test_truth_seeking_comprehensive.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_accuracy_system.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_agent_live.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_backend_local.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_cerebras_comparison.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_improved_prompt.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_qualitative_robustness.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_qualitative_system.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_truth_seeking_chinese.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_truth_seeking_comprehensive.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tests/validation/test_truth_seeking_real.py +0 -0
- {cite_agent-1.2.8 → cite_agent-1.2.9}/tools/security_audit.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.9"
|
|
@@ -91,7 +91,6 @@ class NocturnalCLI:
|
|
|
91
91
|
|
|
92
92
|
if not config.check_setup():
|
|
93
93
|
# Check if we have env vars or session file (non-interactive mode)
|
|
94
|
-
import os
|
|
95
94
|
from pathlib import Path
|
|
96
95
|
session_file = Path.home() / ".nocturnal_archive" / "session.json"
|
|
97
96
|
has_env_creds = os.getenv("NOCTURNAL_ACCOUNT_EMAIL") and os.getenv("NOCTURNAL_ACCOUNT_PASSWORD")
|
|
@@ -110,7 +109,7 @@ class NocturnalCLI:
|
|
|
110
109
|
self.console.print("[error]❌ Not authenticated. Run 'cite-agent --setup' to configure.[/error]")
|
|
111
110
|
return False
|
|
112
111
|
|
|
113
|
-
self.console.print("\n[warning]👋 Hey there, looks like this machine hasn't
|
|
112
|
+
self.console.print("\n[warning]👋 Hey there, looks like this machine hasn't been set up yet.[/warning]")
|
|
114
113
|
self.console.print("[banner]Let's get you signed in — this only takes a minute.[/banner]")
|
|
115
114
|
try:
|
|
116
115
|
if not config.interactive_setup():
|
|
@@ -748,7 +747,7 @@ Examples:
|
|
|
748
747
|
|
|
749
748
|
# Handle version
|
|
750
749
|
if args.version:
|
|
751
|
-
print("Cite Agent v1.2.
|
|
750
|
+
print("Cite Agent v1.2.9")
|
|
752
751
|
print("AI Research Assistant with real data integration")
|
|
753
752
|
return
|
|
754
753
|
|
|
@@ -134,27 +134,70 @@ class EnhancedNocturnalAgent:
|
|
|
134
134
|
|
|
135
135
|
def _load_authentication(self):
|
|
136
136
|
"""Load authentication from session file"""
|
|
137
|
-
use_local_keys = os.getenv("USE_LOCAL_KEYS", "
|
|
137
|
+
use_local_keys = os.getenv("USE_LOCAL_KEYS", "false").lower() == "true"
|
|
138
|
+
|
|
139
|
+
debug_mode = os.getenv("NOCTURNAL_DEBUG", "").lower() == "1"
|
|
140
|
+
if debug_mode:
|
|
141
|
+
print(f"🔍 _load_authentication: USE_LOCAL_KEYS={os.getenv('USE_LOCAL_KEYS')}, use_local_keys={use_local_keys}")
|
|
138
142
|
|
|
139
143
|
if not use_local_keys:
|
|
140
144
|
# Backend mode - load auth token from session
|
|
141
145
|
from pathlib import Path
|
|
142
146
|
session_file = Path.home() / ".nocturnal_archive" / "session.json"
|
|
147
|
+
if debug_mode:
|
|
148
|
+
print(f"🔍 _load_authentication: session_file exists={session_file.exists()}")
|
|
143
149
|
if session_file.exists():
|
|
144
150
|
try:
|
|
145
151
|
import json
|
|
146
152
|
with open(session_file, 'r') as f:
|
|
147
153
|
session_data = json.load(f)
|
|
148
|
-
self.auth_token = session_data.get('
|
|
149
|
-
self.user_id = session_data.get('
|
|
150
|
-
|
|
154
|
+
self.auth_token = session_data.get('auth_token')
|
|
155
|
+
self.user_id = session_data.get('account_id')
|
|
156
|
+
if debug_mode:
|
|
157
|
+
print(f"🔍 _load_authentication: loaded auth_token={self.auth_token}, user_id={self.user_id}")
|
|
158
|
+
except Exception as e:
|
|
159
|
+
if debug_mode:
|
|
160
|
+
print(f"🔍 _load_authentication: ERROR loading session: {e}")
|
|
151
161
|
self.auth_token = None
|
|
152
162
|
self.user_id = None
|
|
153
163
|
else:
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
# FALLBACK: Check if config.env has credentials but session.json is missing
|
|
165
|
+
# This handles cases where old setup didn't create session.json
|
|
166
|
+
import json
|
|
167
|
+
email = os.getenv("NOCTURNAL_ACCOUNT_EMAIL")
|
|
168
|
+
account_id = os.getenv("NOCTURNAL_ACCOUNT_ID")
|
|
169
|
+
auth_token = os.getenv("NOCTURNAL_AUTH_TOKEN")
|
|
170
|
+
|
|
171
|
+
if email and account_id and auth_token:
|
|
172
|
+
# Auto-create session.json from config.env
|
|
173
|
+
try:
|
|
174
|
+
session_data = {
|
|
175
|
+
"email": email,
|
|
176
|
+
"account_id": account_id,
|
|
177
|
+
"auth_token": auth_token,
|
|
178
|
+
"refresh_token": "auto_generated",
|
|
179
|
+
"issued_at": datetime.now(timezone.utc).isoformat()
|
|
180
|
+
}
|
|
181
|
+
session_file.parent.mkdir(parents=True, exist_ok=True)
|
|
182
|
+
session_file.write_text(json.dumps(session_data, indent=2))
|
|
183
|
+
|
|
184
|
+
self.auth_token = auth_token
|
|
185
|
+
self.user_id = account_id
|
|
186
|
+
|
|
187
|
+
if debug_mode:
|
|
188
|
+
print(f"🔍 _load_authentication: Auto-created session.json from config.env")
|
|
189
|
+
except Exception as e:
|
|
190
|
+
if debug_mode:
|
|
191
|
+
print(f"🔍 _load_authentication: Failed to auto-create session: {e}")
|
|
192
|
+
self.auth_token = None
|
|
193
|
+
self.user_id = None
|
|
194
|
+
else:
|
|
195
|
+
self.auth_token = None
|
|
196
|
+
self.user_id = None
|
|
156
197
|
else:
|
|
157
198
|
# Local keys mode
|
|
199
|
+
if debug_mode:
|
|
200
|
+
print(f"🔍 _load_authentication: Local keys mode, not loading session")
|
|
158
201
|
self.auth_token = None
|
|
159
202
|
self.user_id = None
|
|
160
203
|
self._session_topics: Dict[str, Dict[str, Any]] = {}
|
|
@@ -922,10 +965,11 @@ class EnhancedNocturnalAgent:
|
|
|
922
965
|
)
|
|
923
966
|
else:
|
|
924
967
|
intro = (
|
|
925
|
-
"You are Cite Agent, a truth-seeking research and finance AI. "
|
|
926
|
-
"PRIMARY DIRECTIVE: Accuracy > Agreeableness.
|
|
927
|
-
"You are a fact-checker and analyst
|
|
928
|
-
"You have access to research (Archive)
|
|
968
|
+
"You are Cite Agent, a truth-seeking research and finance AI with CODE EXECUTION. "
|
|
969
|
+
"PRIMARY DIRECTIVE: Accuracy > Agreeableness. Execute code for analysis, calculations, and file operations. "
|
|
970
|
+
"You are a fact-checker and analyst with a persistent shell session. "
|
|
971
|
+
"You have access to research (Archive), financial data (FinSight SEC filings), and can run Python/R/SQL/Bash. "
|
|
972
|
+
"When user asks about files, directories, or data: EXECUTE commands to find answers."
|
|
929
973
|
)
|
|
930
974
|
|
|
931
975
|
sections.append(intro)
|
|
@@ -1475,8 +1519,8 @@ class EnhancedNocturnalAgent:
|
|
|
1475
1519
|
import json
|
|
1476
1520
|
with open(session_file, 'r') as f:
|
|
1477
1521
|
session_data = json.load(f)
|
|
1478
|
-
self.auth_token = session_data.get('
|
|
1479
|
-
self.user_id = session_data.get('
|
|
1522
|
+
self.auth_token = session_data.get('auth_token')
|
|
1523
|
+
self.user_id = session_data.get('account_id')
|
|
1480
1524
|
except Exception:
|
|
1481
1525
|
self.auth_token = None
|
|
1482
1526
|
self.user_id = None
|
|
@@ -1538,6 +1582,8 @@ class EnhancedNocturnalAgent:
|
|
|
1538
1582
|
except Exception as e:
|
|
1539
1583
|
print(f"⚠️ Failed to initialize {self.llm_provider.upper()} client: {e}")
|
|
1540
1584
|
|
|
1585
|
+
# Initialize shell session for BOTH production and dev mode
|
|
1586
|
+
# Production users need code execution too (like Cursor/Aider)
|
|
1541
1587
|
if self.shell_session and self.shell_session.poll() is not None:
|
|
1542
1588
|
self.shell_session = None
|
|
1543
1589
|
|
|
@@ -1596,6 +1642,11 @@ class EnhancedNocturnalAgent:
|
|
|
1596
1642
|
This is the SECURE method - all API keys stay on server
|
|
1597
1643
|
Includes API results (Archive, FinSight) in context for better responses
|
|
1598
1644
|
"""
|
|
1645
|
+
# DEBUG: Print auth status
|
|
1646
|
+
debug_mode = os.getenv("NOCTURNAL_DEBUG", "").lower() == "1"
|
|
1647
|
+
if debug_mode:
|
|
1648
|
+
print(f"🔍 call_backend_query: auth_token={self.auth_token}, user_id={self.user_id}")
|
|
1649
|
+
|
|
1599
1650
|
if not self.auth_token:
|
|
1600
1651
|
return ChatResponse(
|
|
1601
1652
|
response="❌ Not authenticated. Please log in first.",
|
|
@@ -2654,14 +2705,123 @@ class EnhancedNocturnalAgent:
|
|
|
2654
2705
|
if debug_mode:
|
|
2655
2706
|
print(f"🔍 Web search failed: {e}")
|
|
2656
2707
|
|
|
2657
|
-
# PRODUCTION MODE:
|
|
2708
|
+
# PRODUCTION MODE: Check for shell/code execution needs FIRST
|
|
2658
2709
|
if self.client is None:
|
|
2659
|
-
|
|
2710
|
+
# Check if query needs directory/file info or exploration
|
|
2711
|
+
question_lower = request.question.lower()
|
|
2712
|
+
|
|
2713
|
+
# Basic info queries
|
|
2714
|
+
needs_shell_info = any(phrase in question_lower for phrase in [
|
|
2715
|
+
'directory', 'folder', 'where am i', 'pwd', 'current location',
|
|
2716
|
+
'list files', 'what files', 'ls', 'files in', 'show files',
|
|
2717
|
+
'data files', 'csv files', 'check if file', 'file exists'
|
|
2718
|
+
])
|
|
2719
|
+
|
|
2720
|
+
# Fuzzy search queries (find similar directories/files)
|
|
2721
|
+
needs_find = any(phrase in question_lower for phrase in [
|
|
2722
|
+
'looking for', 'find', 'search for', 'similar to',
|
|
2723
|
+
'go to', 'cd to', 'navigate to', 'or something', 'forgot the name',
|
|
2724
|
+
'look into', 'check what', 'what\'s in'
|
|
2725
|
+
])
|
|
2726
|
+
|
|
2727
|
+
if (needs_shell_info or needs_find) and self.shell_session:
|
|
2728
|
+
# Execute exploration commands
|
|
2729
|
+
try:
|
|
2730
|
+
api_results["shell_info"] = {}
|
|
2731
|
+
|
|
2732
|
+
# Always include current location
|
|
2733
|
+
pwd_output = self.execute_command("pwd")
|
|
2734
|
+
api_results["shell_info"]["current_directory"] = pwd_output.strip()
|
|
2735
|
+
|
|
2736
|
+
if needs_shell_info and not needs_find:
|
|
2737
|
+
# Just list current directory
|
|
2738
|
+
ls_output = self.execute_command("ls -lah")
|
|
2739
|
+
api_results["shell_info"]["directory_contents"] = ls_output
|
|
2740
|
+
|
|
2741
|
+
if needs_find:
|
|
2742
|
+
# Smart search: extract directory name and location hints
|
|
2743
|
+
import re
|
|
2744
|
+
|
|
2745
|
+
# Check if user is referring to previous context ("it", "there")
|
|
2746
|
+
has_pronoun = any(word in question_lower for word in ['it', 'there', 'that folder', 'that directory'])
|
|
2747
|
+
|
|
2748
|
+
if has_pronoun and len(self.conversation_history) > 0:
|
|
2749
|
+
# Look for directory path in last assistant message
|
|
2750
|
+
last_assistant = None
|
|
2751
|
+
for msg in reversed(self.conversation_history):
|
|
2752
|
+
if msg.get('role') == 'assistant':
|
|
2753
|
+
last_assistant = msg.get('content', '')
|
|
2754
|
+
break
|
|
2755
|
+
|
|
2756
|
+
if last_assistant:
|
|
2757
|
+
# Extract paths like /home/user/Downloads/cm522-main
|
|
2758
|
+
paths = re.findall(r'(/[\w/.-]+)', last_assistant)
|
|
2759
|
+
if paths:
|
|
2760
|
+
# List contents of the first path found
|
|
2761
|
+
target_path = paths[0]
|
|
2762
|
+
ls_output = self.execute_command(f"ls -lah {target_path}")
|
|
2763
|
+
api_results["shell_info"]["directory_contents"] = ls_output
|
|
2764
|
+
api_results["shell_info"]["target_path"] = target_path
|
|
2765
|
+
tools_used.append("shell_execution")
|
|
2766
|
+
# Skip generic search
|
|
2767
|
+
break
|
|
2768
|
+
|
|
2769
|
+
# Generic search if no pronoun or no path found
|
|
2770
|
+
# Common words to ignore
|
|
2771
|
+
ignore_words = {
|
|
2772
|
+
'looking', 'find', 'folder', 'directory', 'called', 'something',
|
|
2773
|
+
'forgot', 'name', 'think', 'can', 'you', 'look', 'for', 'somewhere',
|
|
2774
|
+
'computer', 'downloads', 'the', 'this', 'that', 'class', 'investment',
|
|
2775
|
+
'check', 'what', 'into'
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
# Extract potential target names (prefer short alphanumeric codes)
|
|
2779
|
+
all_words = re.findall(r'\b([a-zA-Z0-9_-]+)\b', request.question)
|
|
2780
|
+
potential_names = [w for w in all_words if len(w) >= 2 and w.lower() not in ignore_words]
|
|
2781
|
+
|
|
2782
|
+
# Detect location hints
|
|
2783
|
+
search_path = "~" # Default to home
|
|
2784
|
+
if 'downloads' in question_lower:
|
|
2785
|
+
search_path = "~/Downloads"
|
|
2786
|
+
elif 'documents' in question_lower:
|
|
2787
|
+
search_path = "~/Documents"
|
|
2788
|
+
|
|
2789
|
+
search_results = []
|
|
2790
|
+
searched_terms = []
|
|
2791
|
+
|
|
2792
|
+
for name in potential_names[:2]: # Limit to 2 best candidates
|
|
2793
|
+
if name in searched_terms:
|
|
2794
|
+
continue
|
|
2795
|
+
searched_terms.append(name)
|
|
2796
|
+
|
|
2797
|
+
# Search with increasing depth if needed
|
|
2798
|
+
find_output = self.execute_command(f"find {search_path} -maxdepth 4 -type d -iname '*{name}*' 2>/dev/null | head -20")
|
|
2799
|
+
if find_output.strip():
|
|
2800
|
+
search_results.append(f"Searched for '*{name}*' in {search_path}:\n{find_output}")
|
|
2801
|
+
|
|
2802
|
+
if search_results:
|
|
2803
|
+
api_results["shell_info"]["search_results"] = "\n\n".join(search_results)
|
|
2804
|
+
elif potential_names:
|
|
2805
|
+
api_results["shell_info"]["search_results"] = f"No directories matching '{', '.join(searched_terms)}' found in {search_path}"
|
|
2806
|
+
|
|
2807
|
+
tools_used.append("shell_execution")
|
|
2808
|
+
except Exception as e:
|
|
2809
|
+
if debug_mode:
|
|
2810
|
+
print(f"🔍 Shell execution failed: {e}")
|
|
2811
|
+
|
|
2812
|
+
# Call backend and UPDATE CONVERSATION HISTORY
|
|
2813
|
+
response = await self.call_backend_query(
|
|
2660
2814
|
query=request.question,
|
|
2661
2815
|
conversation_history=self.conversation_history[-10:],
|
|
2662
2816
|
api_results=api_results, # Include the data!
|
|
2663
2817
|
tools_used=tools_used # Pass tools list for history
|
|
2664
2818
|
)
|
|
2819
|
+
|
|
2820
|
+
# CRITICAL: Save to conversation history for context
|
|
2821
|
+
self.conversation_history.append({"role": "user", "content": request.question})
|
|
2822
|
+
self.conversation_history.append({"role": "assistant", "content": response.response})
|
|
2823
|
+
|
|
2824
|
+
return response
|
|
2665
2825
|
|
|
2666
2826
|
# DEV MODE ONLY: Direct Groq calls (only works with local API keys)
|
|
2667
2827
|
# This code path won't execute in production since self.client = None
|
|
@@ -179,36 +179,45 @@ class SessionManager:
|
|
|
179
179
|
|
|
180
180
|
def setup_environment_variables(self):
|
|
181
181
|
"""Set up environment variables for backend mode"""
|
|
182
|
-
#
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
debug = os.getenv("NOCTURNAL_DEBUG", "").lower() == "1"
|
|
196
|
-
if debug:
|
|
197
|
-
print(f"⚠️ Failed to load .env.local: {e}")
|
|
198
|
-
|
|
199
|
-
# Check if dev mode is enabled
|
|
200
|
-
dev_mode = os.getenv("CITE_AGENT_DEV_MODE", "").lower() == "true"
|
|
201
|
-
|
|
202
|
-
if not dev_mode:
|
|
203
|
-
# PRODUCTION MODE: Force backend, ensure monetization
|
|
204
|
-
# Set backend URL if not already set
|
|
182
|
+
# PRIORITY 1: Check if user has production credentials
|
|
183
|
+
# If they do, force production mode (ignore .env.local)
|
|
184
|
+
from pathlib import Path
|
|
185
|
+
session_file = Path.home() / ".nocturnal_archive" / "session.json"
|
|
186
|
+
has_session = session_file.exists()
|
|
187
|
+
has_config_creds = (
|
|
188
|
+
os.getenv("NOCTURNAL_ACCOUNT_EMAIL") and
|
|
189
|
+
os.getenv("NOCTURNAL_AUTH_TOKEN")
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
if has_session or has_config_creds:
|
|
193
|
+
# User is logged in → FORCE production mode
|
|
194
|
+
os.environ["USE_LOCAL_KEYS"] = "false"
|
|
205
195
|
if "NOCTURNAL_API_URL" not in os.environ:
|
|
206
196
|
os.environ["NOCTURNAL_API_URL"] = "https://cite-agent-api-720dfadd602c.herokuapp.com/api"
|
|
207
197
|
|
|
208
|
-
|
|
209
|
-
|
|
198
|
+
debug = os.getenv("NOCTURNAL_DEBUG", "").lower() == "1"
|
|
199
|
+
if debug:
|
|
200
|
+
print(f"🔍 Production mode: User has credentials, ignoring .env.local")
|
|
201
|
+
else:
|
|
202
|
+
# No production credentials → Allow dev mode from .env.local
|
|
203
|
+
try:
|
|
204
|
+
from dotenv import load_dotenv
|
|
205
|
+
env_local = Path.home() / ".nocturnal_archive" / ".env.local"
|
|
206
|
+
if env_local.exists():
|
|
207
|
+
load_dotenv(env_local)
|
|
208
|
+
debug = os.getenv("NOCTURNAL_DEBUG", "").lower() == "1"
|
|
209
|
+
if debug:
|
|
210
|
+
print(f"🔍 Loaded .env.local: USE_LOCAL_KEYS={os.getenv('USE_LOCAL_KEYS')}")
|
|
211
|
+
except Exception as e:
|
|
212
|
+
debug = os.getenv("NOCTURNAL_DEBUG", "").lower() == "1"
|
|
213
|
+
if debug:
|
|
214
|
+
print(f"⚠️ Failed to load .env.local: {e}")
|
|
215
|
+
|
|
216
|
+
# Default to production if no explicit dev mode
|
|
210
217
|
if "USE_LOCAL_KEYS" not in os.environ:
|
|
211
218
|
os.environ["USE_LOCAL_KEYS"] = "false"
|
|
219
|
+
if "NOCTURNAL_API_URL" not in os.environ:
|
|
220
|
+
os.environ["NOCTURNAL_API_URL"] = "https://cite-agent-api-720dfadd602c.herokuapp.com/api"
|
|
212
221
|
|
|
213
222
|
def get_session_status(self) -> Dict[str, Any]:
|
|
214
223
|
"""Get current session status for debugging"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Automatic setup and configuration for
|
|
3
|
+
Automatic setup and configuration for Cite Agent
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import os
|
|
@@ -11,7 +11,7 @@ from typing import Optional, Dict, Any, List, Tuple
|
|
|
11
11
|
from .account_client import AccountClient, AccountCredentials, AccountProvisioningError
|
|
12
12
|
|
|
13
13
|
KEY_PLACEHOLDER = "__KEYRING__"
|
|
14
|
-
KEYRING_SERVICE = "
|
|
14
|
+
KEYRING_SERVICE = "Cite Agent"
|
|
15
15
|
DEFAULT_QUERY_LIMIT = 25
|
|
16
16
|
|
|
17
17
|
# Production: Users don't need API keys - backend has them
|
|
@@ -50,15 +50,29 @@ class NocturnalConfig:
|
|
|
50
50
|
|
|
51
51
|
def interactive_setup(self) -> bool:
|
|
52
52
|
"""Interactive setup for account authentication and configuration."""
|
|
53
|
-
print("🚀
|
|
53
|
+
print("🚀 Cite Agent Beta Setup")
|
|
54
54
|
print("=" * 40)
|
|
55
55
|
print()
|
|
56
56
|
|
|
57
|
-
if
|
|
57
|
+
# Check if session exists
|
|
58
|
+
from pathlib import Path
|
|
59
|
+
session_file = Path.home() / ".nocturnal_archive" / "session.json"
|
|
60
|
+
|
|
61
|
+
# Check if we have actual credentials (not just auto-generated config)
|
|
62
|
+
existing_config = self.load_config()
|
|
63
|
+
has_credentials = (
|
|
64
|
+
existing_config.get("NOCTURNAL_ACCOUNT_EMAIL") or
|
|
65
|
+
existing_config.get("NOCTURNAL_AUTH_TOKEN")
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if session_file.exists() and has_credentials:
|
|
58
69
|
print("✅ Configuration already exists!")
|
|
59
70
|
response = input("Do you want to reconfigure? (y/N): ").strip().lower()
|
|
60
71
|
if response not in ["y", "yes"]:
|
|
61
72
|
return True
|
|
73
|
+
elif has_credentials and not session_file.exists():
|
|
74
|
+
print("⚠️ Found saved credentials but no active session.")
|
|
75
|
+
print("Let's get you authenticated...")
|
|
62
76
|
|
|
63
77
|
print("You'll use your institution-issued account to sign in. No invite codes or manual API keys required.")
|
|
64
78
|
print()
|
|
@@ -81,6 +95,18 @@ class NocturnalConfig:
|
|
|
81
95
|
print(f"❌ Could not verify your account: {exc}")
|
|
82
96
|
return False
|
|
83
97
|
|
|
98
|
+
# Save session.json for authentication
|
|
99
|
+
import json
|
|
100
|
+
session_data = {
|
|
101
|
+
"email": credentials.email,
|
|
102
|
+
"account_id": credentials.account_id,
|
|
103
|
+
"auth_token": credentials.auth_token,
|
|
104
|
+
"refresh_token": credentials.refresh_token,
|
|
105
|
+
"issued_at": credentials.issued_at
|
|
106
|
+
}
|
|
107
|
+
session_file.parent.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
session_file.write_text(json.dumps(session_data, indent=2))
|
|
109
|
+
|
|
84
110
|
print("\n🛡️ Recap of beta limitations:")
|
|
85
111
|
for item in self._beta_limitations():
|
|
86
112
|
print(f" • {item}")
|
|
@@ -103,27 +129,10 @@ class NocturnalConfig:
|
|
|
103
129
|
"NOCTURNAL_CONFIG_VERSION": "2.0.0",
|
|
104
130
|
}
|
|
105
131
|
|
|
106
|
-
optional_updates = self._configure_optional_secrets(existing_config=config)
|
|
107
|
-
config.update(optional_updates)
|
|
108
|
-
|
|
109
132
|
self.save_config(config)
|
|
110
133
|
|
|
111
134
|
print("\n✅ Configuration saved successfully!")
|
|
112
|
-
print(
|
|
113
|
-
print("\n🎉 You're ready to use Nocturnal Archive!")
|
|
114
|
-
print("\nQuick start:")
|
|
115
|
-
print("```python")
|
|
116
|
-
print("from nocturnal_archive import EnhancedNocturnalAgent, ChatRequest")
|
|
117
|
-
print("import asyncio")
|
|
118
|
-
print()
|
|
119
|
-
print("async def main():")
|
|
120
|
-
print(" agent = EnhancedNocturnalAgent()")
|
|
121
|
-
print(" await agent.initialize()")
|
|
122
|
-
print(" # Your code here...")
|
|
123
|
-
print(" await agent.close()")
|
|
124
|
-
print()
|
|
125
|
-
print("asyncio.run(main())")
|
|
126
|
-
print("```")
|
|
135
|
+
print("🎉 You're ready to use Cite Agent!")
|
|
127
136
|
|
|
128
137
|
return True
|
|
129
138
|
|
|
@@ -312,7 +321,7 @@ class NocturnalConfig:
|
|
|
312
321
|
def save_config(self, config: Dict[str, Any]):
|
|
313
322
|
"""Save configuration to file"""
|
|
314
323
|
with open(self.config_file, 'w') as f:
|
|
315
|
-
f.write("#
|
|
324
|
+
f.write("# Cite Agent Configuration\n")
|
|
316
325
|
f.write("# Generated automatically - do not edit manually\n\n")
|
|
317
326
|
|
|
318
327
|
for key, value in config.items():
|
|
@@ -406,7 +415,7 @@ def auto_setup():
|
|
|
406
415
|
return True
|
|
407
416
|
|
|
408
417
|
# If no config exists, run interactive setup
|
|
409
|
-
print("🔧
|
|
418
|
+
print("🔧 Cite Agent needs initial setup")
|
|
410
419
|
return config.interactive_setup()
|
|
411
420
|
|
|
412
421
|
def get_config():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Auto-updater for
|
|
3
|
+
Auto-updater for Cite Agent
|
|
4
4
|
Checks for updates and handles installation
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -28,7 +28,7 @@ except ImportError:
|
|
|
28
28
|
get_version = None
|
|
29
29
|
|
|
30
30
|
class NocturnalUpdater:
|
|
31
|
-
"""Handles automatic updates for
|
|
31
|
+
"""Handles automatic updates for Cite Agent"""
|
|
32
32
|
|
|
33
33
|
def __init__(self):
|
|
34
34
|
self.current_version = self.get_current_version()
|
|
@@ -185,7 +185,7 @@ def main():
|
|
|
185
185
|
"""CLI for updater"""
|
|
186
186
|
import argparse
|
|
187
187
|
|
|
188
|
-
parser = argparse.ArgumentParser(description="
|
|
188
|
+
parser = argparse.ArgumentParser(description="Cite Agent Updater")
|
|
189
189
|
parser.add_argument("--check", action="store_true", help="Check for updates")
|
|
190
190
|
parser.add_argument("--update", action="store_true", help="Update to latest version")
|
|
191
191
|
parser.add_argument("--force", action="store_true", help="Force update even if up to date")
|
|
@@ -7,7 +7,7 @@ long_description = readme_path.read_text() if readme_path.exists() else "Termina
|
|
|
7
7
|
|
|
8
8
|
setup(
|
|
9
9
|
name="cite-agent",
|
|
10
|
-
version="1.2.
|
|
10
|
+
version="1.2.9",
|
|
11
11
|
author="Cite-Agent Team",
|
|
12
12
|
author_email="contact@citeagent.dev",
|
|
13
13
|
description="Terminal AI assistant for academic research with citation verification",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.2.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cite_agent-1.2.8 → cite_agent-1.2.9}/src/services/research_service/critical_paper_detector.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|