omni-cortex 1.12.1__py3-none-any.whl → 1.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/chat_service.py +217 -16
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/database.py +224 -1
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/main.py +126 -37
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/models.py +31 -1
- {omni_cortex-1.12.1.dist-info → omni_cortex-1.13.0.dist-info}/METADATA +1 -1
- omni_cortex-1.13.0.dist-info/RECORD +26 -0
- omni_cortex-1.12.1.dist-info/RECORD +0 -26
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/.env.example +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/backfill_summaries.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/image_service.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/logging_config.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/project_config.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/project_scanner.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/prompt_security.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/pyproject.toml +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/security.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/uv.lock +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/websocket_manager.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/post_tool_use.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/pre_tool_use.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/session_utils.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/stop.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/subagent_stop.py +0 -0
- {omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/user_prompt.py +0 -0
- {omni_cortex-1.12.1.dist-info → omni_cortex-1.13.0.dist-info}/WHEEL +0 -0
- {omni_cortex-1.12.1.dist-info → omni_cortex-1.13.0.dist-info}/entry_points.txt +0 -0
- {omni_cortex-1.12.1.dist-info → omni_cortex-1.13.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -45,31 +45,51 @@ def is_available() -> bool:
|
|
|
45
45
|
def build_style_context_prompt(style_profile: dict) -> str:
|
|
46
46
|
"""Build a prompt section describing user's communication style."""
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
# Handle both camelCase (new format) and snake_case (old format)
|
|
49
|
+
tone_dist = style_profile.get("toneDistribution") or style_profile.get("tone_distribution", {})
|
|
49
50
|
tone_list = ", ".join(tone_dist.keys()) if tone_dist else "neutral"
|
|
50
|
-
avg_words = style_profile.get("avg_word_count", 20)
|
|
51
|
-
|
|
51
|
+
avg_words = style_profile.get("avgWordCount") or style_profile.get("avg_word_count", 20)
|
|
52
|
+
question_pct = style_profile.get("questionPercentage") or (style_profile.get("question_frequency", 0) * 100)
|
|
53
|
+
primary_tone = style_profile.get("primaryTone") or style_profile.get("primary_tone", "direct")
|
|
52
54
|
|
|
53
|
-
markers = style_profile.get("key_markers", [])
|
|
55
|
+
markers = style_profile.get("styleMarkers") or style_profile.get("key_markers", [])
|
|
54
56
|
markers_text = "\n".join(f"- {m}" for m in markers) if markers else "- Direct and clear"
|
|
55
57
|
|
|
58
|
+
# Get sample messages for concrete examples
|
|
59
|
+
samples = style_profile.get("sampleMessages") or style_profile.get("sample_messages", [])
|
|
60
|
+
samples_text = ""
|
|
61
|
+
if samples:
|
|
62
|
+
samples_text = "\n**Examples of how the user actually writes:**\n"
|
|
63
|
+
for i, sample in enumerate(samples[:3], 1):
|
|
64
|
+
# Truncate long samples
|
|
65
|
+
truncated = sample[:200] + "..." if len(sample) > 200 else sample
|
|
66
|
+
samples_text += f'{i}. "{truncated}"\n'
|
|
67
|
+
|
|
56
68
|
return f"""
|
|
57
|
-
## User Communication Style
|
|
69
|
+
## IMPORTANT: User Communication Style Mode ENABLED
|
|
58
70
|
|
|
59
|
-
|
|
71
|
+
You MUST write ALL responses in the user's personal communication style. This is NOT optional - every response should sound like the user wrote it themselves.
|
|
60
72
|
|
|
61
|
-
**
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
**User's Writing Profile:**
|
|
74
|
+
- Primary Tone: {primary_tone}
|
|
75
|
+
- Typical Message Length: ~{int(avg_words)} words per message
|
|
76
|
+
- Common Tones: {tone_list}
|
|
77
|
+
- Question Usage: {int(question_pct)}% of their messages include questions
|
|
64
78
|
|
|
65
|
-
**
|
|
79
|
+
**Style Markers to Emulate:**
|
|
66
80
|
{markers_text}
|
|
67
|
-
|
|
68
|
-
**Guidelines:**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
{samples_text}
|
|
82
|
+
**MANDATORY Guidelines:**
|
|
83
|
+
1. Write as if YOU are the user speaking - use their voice, not a formal assistant voice
|
|
84
|
+
2. Match their casual/formal level - if they use contractions and slang, you should too
|
|
85
|
+
3. Mirror their sentence structure and rhythm
|
|
86
|
+
4. Use similar vocabulary and expressions they would use
|
|
87
|
+
5. If their style is conversational, be conversational (e.g., "Right, so here's the deal...")
|
|
88
|
+
6. If their style is direct, be direct and skip unnecessary pleasantries
|
|
89
|
+
7. Do NOT use phrases like "Based on the memories" or "According to the data" if that's not how they write
|
|
90
|
+
8. Study the example messages above and mimic that exact writing style
|
|
91
|
+
|
|
92
|
+
Remember: The user has enabled "Write in My Style" mode. Your response should sound EXACTLY like something they would write themselves.
|
|
73
93
|
"""
|
|
74
94
|
|
|
75
95
|
|
|
@@ -369,3 +389,184 @@ async def ask_about_memories(
|
|
|
369
389
|
"sources": sources,
|
|
370
390
|
"error": None,
|
|
371
391
|
}
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
# Platform-specific formatting guidance
|
|
395
|
+
PLATFORM_FORMATS = {
|
|
396
|
+
"skool_post": "Skool community post - can be longer, use formatting, be educational",
|
|
397
|
+
"dm": "Direct message - conversational, personal, concise",
|
|
398
|
+
"email": "Email - professional greeting/closing, clear structure",
|
|
399
|
+
"comment": "Comment reply - brief, direct, engaging",
|
|
400
|
+
"general": "General response - balanced approach",
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
# Response templates with structural guidance
|
|
404
|
+
TEMPLATES = {
|
|
405
|
+
"answer": "Directly answer their question with clear explanation",
|
|
406
|
+
"guide": "Provide step-by-step guidance or recommendations",
|
|
407
|
+
"redirect": "Acknowledge and redirect to a relevant resource",
|
|
408
|
+
"acknowledge": "Acknowledge their point and add follow-up question",
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def build_compose_prompt(
|
|
413
|
+
incoming_message: str,
|
|
414
|
+
style_profile: dict,
|
|
415
|
+
context_type: str,
|
|
416
|
+
template: Optional[str],
|
|
417
|
+
tone_level: int,
|
|
418
|
+
memory_context: str,
|
|
419
|
+
) -> str:
|
|
420
|
+
"""Build the prompt for composing a response in user's style.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
incoming_message: The message to respond to
|
|
424
|
+
style_profile: User's style profile dictionary
|
|
425
|
+
context_type: Platform context (skool_post, dm, email, comment, general)
|
|
426
|
+
template: Optional response template (answer, guide, redirect, acknowledge)
|
|
427
|
+
tone_level: Tone formality level (0-100)
|
|
428
|
+
memory_context: Relevant memories formatted as context
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
Complete prompt for response generation
|
|
432
|
+
"""
|
|
433
|
+
# Get platform-specific formatting guidance
|
|
434
|
+
platform_guidance = PLATFORM_FORMATS.get(context_type, PLATFORM_FORMATS["general"])
|
|
435
|
+
|
|
436
|
+
# Get template guidance
|
|
437
|
+
template_guidance = ""
|
|
438
|
+
if template:
|
|
439
|
+
template_guidance = f"\n**Response Structure:** {TEMPLATES.get(template, '')}"
|
|
440
|
+
|
|
441
|
+
# Convert tone level to guidance
|
|
442
|
+
if tone_level < 25:
|
|
443
|
+
tone_guidance = "Very casual and relaxed - use slang, contractions, informal language"
|
|
444
|
+
elif tone_level < 50:
|
|
445
|
+
tone_guidance = "Casual but clear - conversational with some structure"
|
|
446
|
+
elif tone_level < 75:
|
|
447
|
+
tone_guidance = "Professional but approachable - clear and organized"
|
|
448
|
+
else:
|
|
449
|
+
tone_guidance = "Very professional and formal - polished and structured"
|
|
450
|
+
|
|
451
|
+
# Build style context
|
|
452
|
+
style_context = build_style_context_prompt(style_profile)
|
|
453
|
+
|
|
454
|
+
# Build the complete prompt
|
|
455
|
+
prompt = f"""{style_context}
|
|
456
|
+
|
|
457
|
+
## RESPONSE COMPOSITION TASK
|
|
458
|
+
|
|
459
|
+
You need to respond to the following message:
|
|
460
|
+
|
|
461
|
+
<incoming_message>
|
|
462
|
+
{xml_escape(incoming_message)}
|
|
463
|
+
</incoming_message>
|
|
464
|
+
|
|
465
|
+
**Context:** {platform_guidance}
|
|
466
|
+
**Tone Level:** {tone_guidance}{template_guidance}
|
|
467
|
+
|
|
468
|
+
"""
|
|
469
|
+
|
|
470
|
+
# Add memory context if provided
|
|
471
|
+
if memory_context:
|
|
472
|
+
prompt += f"""
|
|
473
|
+
**Relevant Knowledge from Your Memories:**
|
|
474
|
+
|
|
475
|
+
<memories>
|
|
476
|
+
{memory_context}
|
|
477
|
+
</memories>
|
|
478
|
+
|
|
479
|
+
Use this information naturally in your response if relevant. Don't explicitly cite "memories" - just use the knowledge as if you remember it.
|
|
480
|
+
|
|
481
|
+
"""
|
|
482
|
+
|
|
483
|
+
prompt += """
|
|
484
|
+
**Your Task:**
|
|
485
|
+
1. Write a response to the incoming message in YOUR voice (the user's voice)
|
|
486
|
+
2. Use the knowledge from your memories naturally if relevant
|
|
487
|
+
3. Match the tone level specified above
|
|
488
|
+
4. Follow the platform context guidelines
|
|
489
|
+
5. Sound exactly like something you would write yourself
|
|
490
|
+
|
|
491
|
+
Write the response now:"""
|
|
492
|
+
|
|
493
|
+
return prompt
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
async def compose_response(
|
|
497
|
+
db_path: str,
|
|
498
|
+
incoming_message: str,
|
|
499
|
+
context_type: str = "general",
|
|
500
|
+
template: Optional[str] = None,
|
|
501
|
+
tone_level: int = 50,
|
|
502
|
+
include_memories: bool = True,
|
|
503
|
+
style_profile: Optional[dict] = None,
|
|
504
|
+
) -> dict:
|
|
505
|
+
"""Compose a response to an incoming message in the user's style.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
db_path: Path to the database file
|
|
509
|
+
incoming_message: The message to respond to
|
|
510
|
+
context_type: Platform context (skool_post, dm, email, comment, general)
|
|
511
|
+
template: Optional response template (answer, guide, redirect, acknowledge)
|
|
512
|
+
tone_level: Tone formality level (0-100)
|
|
513
|
+
include_memories: Whether to include relevant memories
|
|
514
|
+
style_profile: User's style profile dictionary
|
|
515
|
+
|
|
516
|
+
Returns:
|
|
517
|
+
Dict with response, sources, and metadata
|
|
518
|
+
"""
|
|
519
|
+
if not is_available():
|
|
520
|
+
return {
|
|
521
|
+
"response": "Chat is not available. Please configure GEMINI_API_KEY or GOOGLE_API_KEY environment variable.",
|
|
522
|
+
"sources": [],
|
|
523
|
+
"error": "api_key_missing",
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
client = get_client()
|
|
527
|
+
if not client:
|
|
528
|
+
return {
|
|
529
|
+
"response": "Failed to initialize Gemini client.",
|
|
530
|
+
"sources": [],
|
|
531
|
+
"error": "client_init_failed",
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
# Get relevant memories if requested
|
|
535
|
+
memory_context = ""
|
|
536
|
+
sources = []
|
|
537
|
+
if include_memories:
|
|
538
|
+
memory_context, sources = _get_memories_and_sources(db_path, incoming_message, max_memories=5)
|
|
539
|
+
|
|
540
|
+
# Get or compute style profile
|
|
541
|
+
if not style_profile:
|
|
542
|
+
from database import compute_style_profile_from_messages
|
|
543
|
+
style_profile = compute_style_profile_from_messages(db_path)
|
|
544
|
+
|
|
545
|
+
# Build the compose prompt
|
|
546
|
+
prompt = build_compose_prompt(
|
|
547
|
+
incoming_message=incoming_message,
|
|
548
|
+
style_profile=style_profile,
|
|
549
|
+
context_type=context_type,
|
|
550
|
+
template=template,
|
|
551
|
+
tone_level=tone_level,
|
|
552
|
+
memory_context=memory_context,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
try:
|
|
556
|
+
response = client.models.generate_content(
|
|
557
|
+
model="gemini-2.0-flash",
|
|
558
|
+
contents=prompt,
|
|
559
|
+
)
|
|
560
|
+
composed_response = response.text
|
|
561
|
+
except Exception as e:
|
|
562
|
+
return {
|
|
563
|
+
"response": f"Failed to generate response: {str(e)}",
|
|
564
|
+
"sources": sources,
|
|
565
|
+
"error": "generation_failed",
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return {
|
|
569
|
+
"response": composed_response,
|
|
570
|
+
"sources": sources,
|
|
571
|
+
"error": None,
|
|
572
|
+
}
|
|
@@ -1189,10 +1189,14 @@ def get_user_messages(
|
|
|
1189
1189
|
except (json.JSONDecodeError, TypeError):
|
|
1190
1190
|
pass
|
|
1191
1191
|
|
|
1192
|
+
# Get primary tone (first in the list) for frontend compatibility
|
|
1193
|
+
primary_tone = tone_indicators[0] if tone_indicators else None
|
|
1194
|
+
|
|
1192
1195
|
messages.append({
|
|
1193
1196
|
"id": row["id"],
|
|
1194
1197
|
"session_id": row["session_id"],
|
|
1195
|
-
"
|
|
1198
|
+
"created_at": row["timestamp"], # Frontend expects created_at
|
|
1199
|
+
"timestamp": row["timestamp"], # Keep for backward compatibility
|
|
1196
1200
|
"content": row["content"],
|
|
1197
1201
|
"word_count": row["word_count"],
|
|
1198
1202
|
"char_count": row["char_count"],
|
|
@@ -1200,6 +1204,7 @@ def get_user_messages(
|
|
|
1200
1204
|
"has_code_blocks": bool(row["has_code_blocks"]),
|
|
1201
1205
|
"has_questions": bool(row["has_questions"]),
|
|
1202
1206
|
"has_commands": bool(row["has_commands"]),
|
|
1207
|
+
"tone": primary_tone, # Frontend expects single tone string
|
|
1203
1208
|
"tone_indicators": tone_indicators,
|
|
1204
1209
|
"project_path": row["project_path"],
|
|
1205
1210
|
})
|
|
@@ -1428,3 +1433,221 @@ def _row_to_sample(row) -> dict:
|
|
|
1428
1433
|
"has_questions": bool(row["has_questions"]),
|
|
1429
1434
|
"tone_indicators": tone_indicators,
|
|
1430
1435
|
}
|
|
1436
|
+
|
|
1437
|
+
|
|
1438
|
+
def get_style_samples_by_category(db_path: str, samples_per_tone: int = 3) -> dict:
|
|
1439
|
+
"""Get sample user messages grouped by style category.
|
|
1440
|
+
|
|
1441
|
+
Maps tone_indicators to frontend categories:
|
|
1442
|
+
- professional: direct, polite, formal tones
|
|
1443
|
+
- casual: casual tones
|
|
1444
|
+
- technical: technical tones
|
|
1445
|
+
- creative: unique patterns, inquisitive tones
|
|
1446
|
+
|
|
1447
|
+
Args:
|
|
1448
|
+
db_path: Path to database
|
|
1449
|
+
samples_per_tone: Max samples per category
|
|
1450
|
+
|
|
1451
|
+
Returns:
|
|
1452
|
+
Dict with professional, casual, technical, creative lists
|
|
1453
|
+
"""
|
|
1454
|
+
conn = get_connection(db_path)
|
|
1455
|
+
|
|
1456
|
+
# Check if user_messages table exists
|
|
1457
|
+
table_check = conn.execute(
|
|
1458
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='user_messages'"
|
|
1459
|
+
).fetchone()
|
|
1460
|
+
|
|
1461
|
+
if not table_check:
|
|
1462
|
+
conn.close()
|
|
1463
|
+
return {
|
|
1464
|
+
"professional": [],
|
|
1465
|
+
"casual": [],
|
|
1466
|
+
"technical": [],
|
|
1467
|
+
"creative": []
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
result = {
|
|
1471
|
+
"professional": [],
|
|
1472
|
+
"casual": [],
|
|
1473
|
+
"technical": [],
|
|
1474
|
+
"creative": []
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
# Mapping from tone_indicators to categories
|
|
1478
|
+
tone_to_category = {
|
|
1479
|
+
"direct": "professional",
|
|
1480
|
+
"polite": "professional",
|
|
1481
|
+
"formal": "professional",
|
|
1482
|
+
"casual": "casual",
|
|
1483
|
+
"technical": "technical",
|
|
1484
|
+
"inquisitive": "creative",
|
|
1485
|
+
"urgent": "professional",
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
# Get all messages with tone indicators
|
|
1489
|
+
cursor = conn.execute(
|
|
1490
|
+
"""SELECT content, tone_indicators FROM user_messages
|
|
1491
|
+
WHERE tone_indicators IS NOT NULL AND tone_indicators != '[]'
|
|
1492
|
+
ORDER BY timestamp DESC LIMIT 200"""
|
|
1493
|
+
)
|
|
1494
|
+
|
|
1495
|
+
for row in cursor.fetchall():
|
|
1496
|
+
content = row["content"]
|
|
1497
|
+
try:
|
|
1498
|
+
tones = json.loads(row["tone_indicators"]) if row["tone_indicators"] else []
|
|
1499
|
+
except (json.JSONDecodeError, TypeError):
|
|
1500
|
+
tones = []
|
|
1501
|
+
|
|
1502
|
+
# Map to categories
|
|
1503
|
+
for tone in tones:
|
|
1504
|
+
category = tone_to_category.get(tone.lower(), "creative")
|
|
1505
|
+
if len(result[category]) < samples_per_tone:
|
|
1506
|
+
# Truncate content for preview
|
|
1507
|
+
preview = content[:200] + "..." if len(content) > 200 else content
|
|
1508
|
+
if preview not in result[category]:
|
|
1509
|
+
result[category].append(preview)
|
|
1510
|
+
break # Only add to first matching category
|
|
1511
|
+
|
|
1512
|
+
# Fill any empty categories with recent messages
|
|
1513
|
+
if any(len(v) == 0 for v in result.values()):
|
|
1514
|
+
cursor = conn.execute(
|
|
1515
|
+
"SELECT content FROM user_messages ORDER BY timestamp DESC LIMIT ?",
|
|
1516
|
+
(samples_per_tone * 4,)
|
|
1517
|
+
)
|
|
1518
|
+
fallback_messages = [
|
|
1519
|
+
row["content"][:200] + "..." if len(row["content"]) > 200 else row["content"]
|
|
1520
|
+
for row in cursor.fetchall()
|
|
1521
|
+
]
|
|
1522
|
+
|
|
1523
|
+
for category in result:
|
|
1524
|
+
if len(result[category]) == 0 and fallback_messages:
|
|
1525
|
+
# Take messages for empty categories
|
|
1526
|
+
for msg in fallback_messages[:samples_per_tone]:
|
|
1527
|
+
if msg not in [m for v in result.values() for m in v]:
|
|
1528
|
+
result[category].append(msg)
|
|
1529
|
+
|
|
1530
|
+
conn.close()
|
|
1531
|
+
return result
|
|
1532
|
+
|
|
1533
|
+
|
|
1534
|
+
def compute_style_profile_from_messages(db_path: str) -> Optional[dict]:
|
|
1535
|
+
"""Compute a style profile from user_messages table.
|
|
1536
|
+
|
|
1537
|
+
This is used when no pre-computed profile exists.
|
|
1538
|
+
|
|
1539
|
+
Returns format expected by frontend StyleProfileCard:
|
|
1540
|
+
- total_messages: int
|
|
1541
|
+
- avg_word_count: float
|
|
1542
|
+
- primary_tone: str
|
|
1543
|
+
- question_percentage: float
|
|
1544
|
+
- tone_distribution: dict[str, int]
|
|
1545
|
+
- style_markers: list[str]
|
|
1546
|
+
"""
|
|
1547
|
+
conn = get_connection(db_path)
|
|
1548
|
+
|
|
1549
|
+
# Check if user_messages table exists
|
|
1550
|
+
table_check = conn.execute(
|
|
1551
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='user_messages'"
|
|
1552
|
+
).fetchone()
|
|
1553
|
+
|
|
1554
|
+
if not table_check:
|
|
1555
|
+
conn.close()
|
|
1556
|
+
return None
|
|
1557
|
+
|
|
1558
|
+
# Get total count and averages
|
|
1559
|
+
stats = conn.execute(
|
|
1560
|
+
"""SELECT
|
|
1561
|
+
COUNT(*) as total,
|
|
1562
|
+
AVG(word_count) as avg_words,
|
|
1563
|
+
AVG(char_count) as avg_chars,
|
|
1564
|
+
SUM(CASE WHEN has_questions = 1 THEN 1 ELSE 0 END) as question_count
|
|
1565
|
+
FROM user_messages"""
|
|
1566
|
+
).fetchone()
|
|
1567
|
+
|
|
1568
|
+
if not stats or stats["total"] == 0:
|
|
1569
|
+
conn.close()
|
|
1570
|
+
return None
|
|
1571
|
+
|
|
1572
|
+
total_messages = stats["total"]
|
|
1573
|
+
avg_word_count = stats["avg_words"] or 0
|
|
1574
|
+
question_percentage = (stats["question_count"] / total_messages * 100) if total_messages > 0 else 0
|
|
1575
|
+
|
|
1576
|
+
# Compute tone distribution
|
|
1577
|
+
tone_distribution = {}
|
|
1578
|
+
cursor = conn.execute(
|
|
1579
|
+
"SELECT tone_indicators FROM user_messages WHERE tone_indicators IS NOT NULL AND tone_indicators != '[]'"
|
|
1580
|
+
)
|
|
1581
|
+
for row in cursor.fetchall():
|
|
1582
|
+
try:
|
|
1583
|
+
tones = json.loads(row["tone_indicators"]) if row["tone_indicators"] else []
|
|
1584
|
+
for tone in tones:
|
|
1585
|
+
tone_lower = tone.lower()
|
|
1586
|
+
tone_distribution[tone_lower] = tone_distribution.get(tone_lower, 0) + 1
|
|
1587
|
+
except (json.JSONDecodeError, TypeError):
|
|
1588
|
+
pass
|
|
1589
|
+
|
|
1590
|
+
# Determine primary tone (most common)
|
|
1591
|
+
primary_tone = "direct"
|
|
1592
|
+
if tone_distribution:
|
|
1593
|
+
primary_tone = max(tone_distribution, key=tone_distribution.get)
|
|
1594
|
+
|
|
1595
|
+
# Generate style markers based on the data
|
|
1596
|
+
style_markers = []
|
|
1597
|
+
|
|
1598
|
+
if avg_word_count < 15:
|
|
1599
|
+
style_markers.append("Concise")
|
|
1600
|
+
elif avg_word_count > 40:
|
|
1601
|
+
style_markers.append("Detailed")
|
|
1602
|
+
else:
|
|
1603
|
+
style_markers.append("Balanced length")
|
|
1604
|
+
|
|
1605
|
+
if question_percentage > 40:
|
|
1606
|
+
style_markers.append("Question-driven")
|
|
1607
|
+
elif question_percentage < 10:
|
|
1608
|
+
style_markers.append("Statement-focused")
|
|
1609
|
+
|
|
1610
|
+
# Check for code usage
|
|
1611
|
+
code_stats = conn.execute(
|
|
1612
|
+
"SELECT SUM(CASE WHEN has_code_blocks = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as code_pct FROM user_messages"
|
|
1613
|
+
).fetchone()
|
|
1614
|
+
if code_stats and code_stats["code_pct"] and code_stats["code_pct"] > 20:
|
|
1615
|
+
style_markers.append("Code-heavy")
|
|
1616
|
+
|
|
1617
|
+
# Add primary tone to markers
|
|
1618
|
+
tone_labels = {
|
|
1619
|
+
"direct": "Direct",
|
|
1620
|
+
"polite": "Polite",
|
|
1621
|
+
"technical": "Technical",
|
|
1622
|
+
"casual": "Casual",
|
|
1623
|
+
"inquisitive": "Inquisitive",
|
|
1624
|
+
"urgent": "Urgent",
|
|
1625
|
+
}
|
|
1626
|
+
if primary_tone in tone_labels:
|
|
1627
|
+
style_markers.append(tone_labels[primary_tone])
|
|
1628
|
+
|
|
1629
|
+
if not style_markers:
|
|
1630
|
+
style_markers.append("Building profile...")
|
|
1631
|
+
|
|
1632
|
+
# Get sample messages to show the AI how the user actually writes
|
|
1633
|
+
sample_messages = []
|
|
1634
|
+
cursor = conn.execute(
|
|
1635
|
+
"""SELECT content FROM user_messages
|
|
1636
|
+
WHERE length(content) > 20 AND length(content) < 500
|
|
1637
|
+
AND has_commands = 0
|
|
1638
|
+
ORDER BY timestamp DESC LIMIT 5"""
|
|
1639
|
+
)
|
|
1640
|
+
for row in cursor.fetchall():
|
|
1641
|
+
sample_messages.append(row["content"])
|
|
1642
|
+
|
|
1643
|
+
conn.close()
|
|
1644
|
+
|
|
1645
|
+
return {
|
|
1646
|
+
"totalMessages": total_messages,
|
|
1647
|
+
"avgWordCount": round(avg_word_count, 1),
|
|
1648
|
+
"primaryTone": primary_tone,
|
|
1649
|
+
"questionPercentage": round(question_percentage, 1),
|
|
1650
|
+
"toneDistribution": tone_distribution,
|
|
1651
|
+
"styleMarkers": style_markers,
|
|
1652
|
+
"sampleMessages": sample_messages,
|
|
1653
|
+
}
|
{omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/main.py
RENAMED
|
@@ -54,6 +54,8 @@ from database import (
|
|
|
54
54
|
get_skill_usage,
|
|
55
55
|
get_style_profile,
|
|
56
56
|
get_style_samples,
|
|
57
|
+
get_style_samples_by_category,
|
|
58
|
+
compute_style_profile_from_messages,
|
|
57
59
|
get_timeline,
|
|
58
60
|
get_tool_usage,
|
|
59
61
|
get_type_distribution,
|
|
@@ -73,6 +75,8 @@ from models import (
|
|
|
73
75
|
BulkDeleteRequest,
|
|
74
76
|
ChatRequest,
|
|
75
77
|
ChatResponse,
|
|
78
|
+
ComposeRequest,
|
|
79
|
+
ComposeResponse,
|
|
76
80
|
ConversationSaveRequest,
|
|
77
81
|
ConversationSaveResponse,
|
|
78
82
|
FilterParams,
|
|
@@ -1023,7 +1027,11 @@ async def chat_with_memories(
|
|
|
1023
1027
|
style_context = None
|
|
1024
1028
|
if request.use_style:
|
|
1025
1029
|
try:
|
|
1026
|
-
|
|
1030
|
+
# First try computed profile from user_messages (richer data)
|
|
1031
|
+
style_context = compute_style_profile_from_messages(project)
|
|
1032
|
+
# Fall back to stored profile if no user_messages
|
|
1033
|
+
if not style_context:
|
|
1034
|
+
style_context = get_style_profile(project)
|
|
1027
1035
|
except Exception:
|
|
1028
1036
|
pass # Graceful fallback if no style data
|
|
1029
1037
|
|
|
@@ -1061,7 +1069,11 @@ async def stream_chat(
|
|
|
1061
1069
|
style_context = None
|
|
1062
1070
|
if use_style:
|
|
1063
1071
|
try:
|
|
1064
|
-
|
|
1072
|
+
# First try computed profile from user_messages (richer data)
|
|
1073
|
+
style_context = compute_style_profile_from_messages(project)
|
|
1074
|
+
# Fall back to stored profile if no user_messages
|
|
1075
|
+
if not style_context:
|
|
1076
|
+
style_context = get_style_profile(project)
|
|
1065
1077
|
except Exception:
|
|
1066
1078
|
pass # Graceful fallback if no style data
|
|
1067
1079
|
|
|
@@ -1110,6 +1122,60 @@ async def save_chat_conversation(
|
|
|
1110
1122
|
raise
|
|
1111
1123
|
|
|
1112
1124
|
|
|
1125
|
+
@app.post("/api/compose-response", response_model=ComposeResponse)
|
|
1126
|
+
@rate_limit("10/minute")
|
|
1127
|
+
async def compose_response_endpoint(
|
|
1128
|
+
request: ComposeRequest,
|
|
1129
|
+
project: str = Query(..., description="Path to the database file"),
|
|
1130
|
+
):
|
|
1131
|
+
"""Compose a response to an incoming message in the user's style."""
|
|
1132
|
+
try:
|
|
1133
|
+
if not Path(project).exists():
|
|
1134
|
+
log_error("/api/compose-response", FileNotFoundError("Database not found"))
|
|
1135
|
+
raise HTTPException(status_code=404, detail="Database not found")
|
|
1136
|
+
|
|
1137
|
+
# Get style profile
|
|
1138
|
+
style_profile = compute_style_profile_from_messages(project)
|
|
1139
|
+
|
|
1140
|
+
# Compose the response
|
|
1141
|
+
result = await chat_service.compose_response(
|
|
1142
|
+
db_path=project,
|
|
1143
|
+
incoming_message=request.incoming_message,
|
|
1144
|
+
context_type=request.context_type,
|
|
1145
|
+
template=request.template,
|
|
1146
|
+
tone_level=request.tone_level,
|
|
1147
|
+
include_memories=request.include_memories,
|
|
1148
|
+
style_profile=style_profile,
|
|
1149
|
+
)
|
|
1150
|
+
|
|
1151
|
+
if result.get("error"):
|
|
1152
|
+
log_error("/api/compose-response", Exception(result["error"]))
|
|
1153
|
+
raise HTTPException(status_code=500, detail=result["error"])
|
|
1154
|
+
|
|
1155
|
+
# Build response model
|
|
1156
|
+
import uuid
|
|
1157
|
+
from datetime import datetime
|
|
1158
|
+
response = ComposeResponse(
|
|
1159
|
+
id=str(uuid.uuid4()),
|
|
1160
|
+
response=result["response"],
|
|
1161
|
+
sources=result["sources"],
|
|
1162
|
+
style_applied=bool(style_profile and style_profile.get("total_messages", 0) > 0),
|
|
1163
|
+
tone_level=request.tone_level,
|
|
1164
|
+
template_used=request.template,
|
|
1165
|
+
incoming_message=request.incoming_message,
|
|
1166
|
+
context_type=request.context_type,
|
|
1167
|
+
created_at=datetime.now().isoformat(),
|
|
1168
|
+
)
|
|
1169
|
+
|
|
1170
|
+
log_success("/api/compose-response", context=request.context_type, tone=request.tone_level)
|
|
1171
|
+
return response
|
|
1172
|
+
except HTTPException:
|
|
1173
|
+
raise
|
|
1174
|
+
except Exception as e:
|
|
1175
|
+
log_error("/api/compose-response", e)
|
|
1176
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
1177
|
+
|
|
1178
|
+
|
|
1113
1179
|
# --- Image Generation Endpoints ---
|
|
1114
1180
|
|
|
1115
1181
|
|
|
@@ -1262,6 +1328,7 @@ async def list_user_messages(
|
|
|
1262
1328
|
)
|
|
1263
1329
|
|
|
1264
1330
|
total_count = get_user_message_count(project, session_id=session_id)
|
|
1331
|
+
has_more = (offset + len(messages)) < total_count
|
|
1265
1332
|
|
|
1266
1333
|
log_success("/api/user-messages", count=len(messages), total=total_count)
|
|
1267
1334
|
return UserMessagesResponse(
|
|
@@ -1269,6 +1336,7 @@ async def list_user_messages(
|
|
|
1269
1336
|
total_count=total_count,
|
|
1270
1337
|
limit=limit,
|
|
1271
1338
|
offset=offset,
|
|
1339
|
+
has_more=has_more,
|
|
1272
1340
|
)
|
|
1273
1341
|
except HTTPException:
|
|
1274
1342
|
raise
|
|
@@ -1321,79 +1389,100 @@ async def delete_user_messages_bulk_endpoint(
|
|
|
1321
1389
|
raise HTTPException(status_code=500, detail=str(e))
|
|
1322
1390
|
|
|
1323
1391
|
|
|
1324
|
-
@app.get("/api/style
|
|
1392
|
+
@app.get("/api/style/profile")
|
|
1325
1393
|
async def get_style_profile_endpoint(
|
|
1326
1394
|
project: str = Query(..., description="Path to the database file"),
|
|
1327
1395
|
project_path: Optional[str] = Query(None, description="Project-specific profile path, or None for global"),
|
|
1328
1396
|
):
|
|
1329
1397
|
"""Get user style profile for style analysis.
|
|
1330
1398
|
|
|
1331
|
-
Returns
|
|
1332
|
-
-
|
|
1333
|
-
-
|
|
1334
|
-
-
|
|
1335
|
-
-
|
|
1336
|
-
-
|
|
1399
|
+
Returns style metrics computed from user messages:
|
|
1400
|
+
- total_messages: Total message count
|
|
1401
|
+
- avg_word_count: Average words per message
|
|
1402
|
+
- primary_tone: Most common tone (direct, polite, technical, etc.)
|
|
1403
|
+
- question_percentage: Percentage of messages containing questions
|
|
1404
|
+
- tone_distribution: Count of messages by tone
|
|
1405
|
+
- style_markers: Descriptive labels for writing style
|
|
1337
1406
|
"""
|
|
1338
1407
|
try:
|
|
1339
1408
|
if not Path(project).exists():
|
|
1340
1409
|
raise HTTPException(status_code=404, detail="Database not found")
|
|
1341
1410
|
|
|
1411
|
+
# First try to get pre-computed profile from user_style_profiles table
|
|
1342
1412
|
profile = get_style_profile(project, project_path=project_path)
|
|
1343
1413
|
|
|
1414
|
+
# If no stored profile, compute from user_messages
|
|
1415
|
+
if not profile:
|
|
1416
|
+
profile = compute_style_profile_from_messages(project)
|
|
1417
|
+
|
|
1418
|
+
# If still no profile (no user_messages), return empty structure
|
|
1344
1419
|
if not profile:
|
|
1345
|
-
# Return empty profile structure if none exists
|
|
1346
1420
|
return {
|
|
1347
|
-
"
|
|
1348
|
-
"
|
|
1349
|
-
"
|
|
1350
|
-
"
|
|
1351
|
-
"
|
|
1352
|
-
"
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1421
|
+
"totalMessages": 0,
|
|
1422
|
+
"avgWordCount": 0,
|
|
1423
|
+
"primaryTone": "direct",
|
|
1424
|
+
"questionPercentage": 0,
|
|
1425
|
+
"toneDistribution": {},
|
|
1426
|
+
"styleMarkers": ["No data available yet"],
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
# Convert stored profile format to frontend expected format if needed
|
|
1430
|
+
if "totalMessages" in profile:
|
|
1431
|
+
# Already in camelCase format from compute_style_profile_from_messages
|
|
1432
|
+
pass
|
|
1433
|
+
elif "id" in profile:
|
|
1434
|
+
# Convert stored profile (from user_style_profiles table) to frontend format
|
|
1435
|
+
tone_dist = {}
|
|
1436
|
+
# Stored profile doesn't have tone_distribution, so compute it
|
|
1437
|
+
computed = compute_style_profile_from_messages(project)
|
|
1438
|
+
if computed:
|
|
1439
|
+
tone_dist = computed.get("toneDistribution", {})
|
|
1440
|
+
primary_tone = computed.get("primaryTone", "direct")
|
|
1441
|
+
style_markers = computed.get("styleMarkers", [])
|
|
1442
|
+
else:
|
|
1443
|
+
primary_tone = "direct"
|
|
1444
|
+
style_markers = []
|
|
1445
|
+
|
|
1446
|
+
profile = {
|
|
1447
|
+
"totalMessages": profile.get("total_messages", 0),
|
|
1448
|
+
"avgWordCount": profile.get("avg_word_count", 0) or 0,
|
|
1449
|
+
"primaryTone": primary_tone,
|
|
1450
|
+
"questionPercentage": (profile.get("question_frequency", 0) or 0) * 100,
|
|
1451
|
+
"toneDistribution": tone_dist,
|
|
1452
|
+
"styleMarkers": style_markers or profile.get("greeting_patterns", []) or [],
|
|
1364
1453
|
}
|
|
1365
1454
|
|
|
1366
|
-
log_success("/api/style
|
|
1455
|
+
log_success("/api/style/profile", has_profile=True, total_messages=profile.get("totalMessages", 0))
|
|
1367
1456
|
return profile
|
|
1368
1457
|
except HTTPException:
|
|
1369
1458
|
raise
|
|
1370
1459
|
except Exception as e:
|
|
1371
|
-
log_error("/api/style
|
|
1460
|
+
log_error("/api/style/profile", e)
|
|
1372
1461
|
raise HTTPException(status_code=500, detail=str(e))
|
|
1373
1462
|
|
|
1374
1463
|
|
|
1375
|
-
@app.get("/api/style
|
|
1464
|
+
@app.get("/api/style/samples")
|
|
1376
1465
|
async def get_style_samples_endpoint(
|
|
1377
1466
|
project: str = Query(..., description="Path to the database file"),
|
|
1378
|
-
|
|
1467
|
+
samples_per_tone: int = Query(3, ge=1, le=10, description="Max samples per tone category"),
|
|
1379
1468
|
):
|
|
1380
1469
|
"""Get sample user messages for style analysis preview.
|
|
1381
1470
|
|
|
1382
|
-
Returns
|
|
1383
|
-
including recent messages, messages with code blocks, and longer messages.
|
|
1471
|
+
Returns messages grouped by style category (professional, casual, technical, creative).
|
|
1384
1472
|
"""
|
|
1385
1473
|
try:
|
|
1386
1474
|
if not Path(project).exists():
|
|
1387
1475
|
raise HTTPException(status_code=404, detail="Database not found")
|
|
1388
1476
|
|
|
1389
|
-
samples =
|
|
1477
|
+
samples = get_style_samples_by_category(project, samples_per_tone=samples_per_tone)
|
|
1390
1478
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1479
|
+
total_count = sum(len(v) for v in samples.values())
|
|
1480
|
+
log_success("/api/style/samples", count=total_count)
|
|
1481
|
+
return samples
|
|
1393
1482
|
except HTTPException:
|
|
1394
1483
|
raise
|
|
1395
1484
|
except Exception as e:
|
|
1396
|
-
log_error("/api/style
|
|
1485
|
+
log_error("/api/style/samples", e)
|
|
1397
1486
|
raise HTTPException(status_code=500, detail=str(e))
|
|
1398
1487
|
|
|
1399
1488
|
|
|
@@ -294,7 +294,8 @@ class UserMessage(BaseModel):
|
|
|
294
294
|
|
|
295
295
|
id: str
|
|
296
296
|
session_id: Optional[str] = None
|
|
297
|
-
timestamp: str
|
|
297
|
+
timestamp: Optional[str] = None # Backward compatibility
|
|
298
|
+
created_at: Optional[str] = None # Frontend expects created_at
|
|
298
299
|
content: str
|
|
299
300
|
word_count: Optional[int] = None
|
|
300
301
|
char_count: Optional[int] = None
|
|
@@ -302,6 +303,7 @@ class UserMessage(BaseModel):
|
|
|
302
303
|
has_code_blocks: bool = False
|
|
303
304
|
has_questions: bool = False
|
|
304
305
|
has_commands: bool = False
|
|
306
|
+
tone: Optional[str] = None # Primary tone for frontend
|
|
305
307
|
tone_indicators: list[str] = []
|
|
306
308
|
project_path: Optional[str] = None
|
|
307
309
|
|
|
@@ -328,6 +330,7 @@ class UserMessagesResponse(BaseModel):
|
|
|
328
330
|
total_count: int
|
|
329
331
|
limit: int
|
|
330
332
|
offset: int
|
|
333
|
+
has_more: bool = False # Whether more results are available
|
|
331
334
|
|
|
332
335
|
|
|
333
336
|
class StyleSample(BaseModel):
|
|
@@ -368,3 +371,30 @@ class BulkDeleteRequest(BaseModel):
|
|
|
368
371
|
"""Request body for bulk delete operations."""
|
|
369
372
|
|
|
370
373
|
message_ids: list[str] = Field(..., min_length=1, max_length=100)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# --- Response Composer Models ---
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
class ComposeRequest(BaseModel):
|
|
380
|
+
"""Request for composing a response in user's style."""
|
|
381
|
+
|
|
382
|
+
incoming_message: str = Field(..., min_length=1, max_length=5000)
|
|
383
|
+
context_type: str = Field(default="general") # skool_post, dm, email, comment, general
|
|
384
|
+
template: Optional[str] = None # answer, guide, redirect, acknowledge
|
|
385
|
+
tone_level: int = Field(default=50, ge=0, le=100) # 0=casual, 100=professional
|
|
386
|
+
include_memories: bool = Field(default=True)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class ComposeResponse(BaseModel):
|
|
390
|
+
"""Response from compose endpoint."""
|
|
391
|
+
|
|
392
|
+
id: str
|
|
393
|
+
response: str
|
|
394
|
+
sources: list[ChatSource]
|
|
395
|
+
style_applied: bool
|
|
396
|
+
tone_level: int
|
|
397
|
+
template_used: Optional[str]
|
|
398
|
+
incoming_message: str
|
|
399
|
+
context_type: str
|
|
400
|
+
created_at: str
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omni-cortex
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.13.0
|
|
4
4
|
Summary: Give Claude Code a perfect memory - auto-logs everything, searches smartly, and gets smarter over time
|
|
5
5
|
Project-URL: Homepage, https://github.com/AllCytes/Omni-Cortex
|
|
6
6
|
Project-URL: Repository, https://github.com/AllCytes/Omni-Cortex
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/hooks/post_tool_use.py,sha256=zdaKChi8zOghRlHswisCBSQE3kW1MtmM6AFfI_ivvpI,16581
|
|
2
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/hooks/pre_tool_use.py,sha256=3_V6Qw5m40eGrMmm5i94vINzeVxmcJvivdPa69H3AOI,8585
|
|
3
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/hooks/session_utils.py,sha256=3SKPCytqWuRPOupWdzmwBoKBDJqtLcT1Nle_pueDQUY,5746
|
|
4
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/hooks/stop.py,sha256=UroliJsyIS9_lj29-1d_r-80V4AfTMUFCaOjJZv3lwM,6976
|
|
5
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/hooks/subagent_stop.py,sha256=V9HQSFGNOfkg8ZCstPEy4h5V8BP4AbrVr8teFzN1kNk,3314
|
|
6
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/hooks/user_prompt.py,sha256=WNHJvhnkb9rXQ_HDpr6eLpM5vwy1Y1xl1EUoqyNC-x8,6859
|
|
7
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/.env.example,sha256=9xS7-UiWlMddRwzlyyyKNHAMlNTsgH-2sPV266guJpQ,372
|
|
8
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/backfill_summaries.py,sha256=ElchfcBv4pmVr2PsePCgFlCyuvf4_jDJj_C3AmMhu7U,8973
|
|
9
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/chat_service.py,sha256=QGNxVX-9bJw4kot6mPieGD2QIbmzvPYSGDGOpv3p_-Y,18567
|
|
10
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/database.py,sha256=_sWqLjx_mWOxqNpfbv-bChtPfQkHzUNzly1pGu_zPKI,54199
|
|
11
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/image_service.py,sha256=NP6ojFpHb6iNTYRkXqYu1CL6WvooZpZ54mjLiWSWG_g,19205
|
|
12
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/logging_config.py,sha256=WnunFGET9zlsn9WBpVsio2zI7BiUQanE0xzAQQxIhII,3944
|
|
13
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/main.py,sha256=-GwRioHjuUaMiP1gNuyqzs6LUxvIgOUHyirCcfQ6pRs,59364
|
|
14
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/models.py,sha256=_gQoBaavttuRgLIvhCQsZ0zmuON6aKWbAFhdB1YFVbM,11164
|
|
15
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/project_config.py,sha256=ZxGoeRpHvN5qQyf2hRxrAZiHrPSwdQp59f0di6O1LKM,4352
|
|
16
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/project_scanner.py,sha256=lwFXS8iJbOoxf7FAyo2TjH25neaMHiJ8B3jS57XxtDI,5713
|
|
17
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/prompt_security.py,sha256=LcdZhYy1CfpSq_4BPO6lMJ15phc2ZXLUSBAnAvODVCI,3423
|
|
18
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/pyproject.toml,sha256=9pbbGQXLe1Xd06nZAtDySCHIlfMWvPaB-C6tGZR6umc,502
|
|
19
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/security.py,sha256=nQsoPE0n5dtY9ive00d33W1gL48GgK7C5Ae0BK2oW2k,3479
|
|
20
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/uv.lock,sha256=miB9zGGSirBkjDE-OZTPCnv43Yc98xuAz_Ne8vTNFHg,186004
|
|
21
|
+
omni_cortex-1.13.0.data/data/share/omni-cortex/dashboard/backend/websocket_manager.py,sha256=gNQLd94AcC-InumGQmUolREhiogCzilYWpLN8SRZjHI,3645
|
|
22
|
+
omni_cortex-1.13.0.dist-info/METADATA,sha256=N9ZCvUc2F0jnkuXvtXl1ISgDipNbwvtsMHritpaSDVo,15712
|
|
23
|
+
omni_cortex-1.13.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
24
|
+
omni_cortex-1.13.0.dist-info/entry_points.txt,sha256=rohx4mFH2ffZmMb9QXPZmFf-ZGjA3jpKVDVeET-ttiM,150
|
|
25
|
+
omni_cortex-1.13.0.dist-info/licenses/LICENSE,sha256=oG_397owMmi-Umxp5sYocJ6RPohp9_bDNnnEu9OUphg,1072
|
|
26
|
+
omni_cortex-1.13.0.dist-info/RECORD,,
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/hooks/post_tool_use.py,sha256=zdaKChi8zOghRlHswisCBSQE3kW1MtmM6AFfI_ivvpI,16581
|
|
2
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/hooks/pre_tool_use.py,sha256=3_V6Qw5m40eGrMmm5i94vINzeVxmcJvivdPa69H3AOI,8585
|
|
3
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/hooks/session_utils.py,sha256=3SKPCytqWuRPOupWdzmwBoKBDJqtLcT1Nle_pueDQUY,5746
|
|
4
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/hooks/stop.py,sha256=UroliJsyIS9_lj29-1d_r-80V4AfTMUFCaOjJZv3lwM,6976
|
|
5
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/hooks/subagent_stop.py,sha256=V9HQSFGNOfkg8ZCstPEy4h5V8BP4AbrVr8teFzN1kNk,3314
|
|
6
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/hooks/user_prompt.py,sha256=WNHJvhnkb9rXQ_HDpr6eLpM5vwy1Y1xl1EUoqyNC-x8,6859
|
|
7
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/.env.example,sha256=9xS7-UiWlMddRwzlyyyKNHAMlNTsgH-2sPV266guJpQ,372
|
|
8
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/backfill_summaries.py,sha256=ElchfcBv4pmVr2PsePCgFlCyuvf4_jDJj_C3AmMhu7U,8973
|
|
9
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/chat_service.py,sha256=5vUzNL3AIfkqVMwooXEqCSkWAkN1HP0vToN1sn3x3Z4,11285
|
|
10
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/database.py,sha256=WwxgXVo5gztFjaKj-iANYgK4tOGGPARsHg28hkJtADQ,46494
|
|
11
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/image_service.py,sha256=NP6ojFpHb6iNTYRkXqYu1CL6WvooZpZ54mjLiWSWG_g,19205
|
|
12
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/logging_config.py,sha256=WnunFGET9zlsn9WBpVsio2zI7BiUQanE0xzAQQxIhII,3944
|
|
13
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/main.py,sha256=ezIgMX3WtwQoJnrxi3M1I-gRSPh69Qmv6F0va7tSbxs,55122
|
|
14
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/models.py,sha256=VymhQz6GCPo5d7wyn_Yg1njKugGbzx5--bnVP42MyBg,10111
|
|
15
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/project_config.py,sha256=ZxGoeRpHvN5qQyf2hRxrAZiHrPSwdQp59f0di6O1LKM,4352
|
|
16
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/project_scanner.py,sha256=lwFXS8iJbOoxf7FAyo2TjH25neaMHiJ8B3jS57XxtDI,5713
|
|
17
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/prompt_security.py,sha256=LcdZhYy1CfpSq_4BPO6lMJ15phc2ZXLUSBAnAvODVCI,3423
|
|
18
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/pyproject.toml,sha256=9pbbGQXLe1Xd06nZAtDySCHIlfMWvPaB-C6tGZR6umc,502
|
|
19
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/security.py,sha256=nQsoPE0n5dtY9ive00d33W1gL48GgK7C5Ae0BK2oW2k,3479
|
|
20
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/uv.lock,sha256=miB9zGGSirBkjDE-OZTPCnv43Yc98xuAz_Ne8vTNFHg,186004
|
|
21
|
-
omni_cortex-1.12.1.data/data/share/omni-cortex/dashboard/backend/websocket_manager.py,sha256=gNQLd94AcC-InumGQmUolREhiogCzilYWpLN8SRZjHI,3645
|
|
22
|
-
omni_cortex-1.12.1.dist-info/METADATA,sha256=AOOi2hbe_RTrqeyPZvN9go13VyndPomtxhQw7lPGX7k,15712
|
|
23
|
-
omni_cortex-1.12.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
24
|
-
omni_cortex-1.12.1.dist-info/entry_points.txt,sha256=rohx4mFH2ffZmMb9QXPZmFf-ZGjA3jpKVDVeET-ttiM,150
|
|
25
|
-
omni_cortex-1.12.1.dist-info/licenses/LICENSE,sha256=oG_397owMmi-Umxp5sYocJ6RPohp9_bDNnnEu9OUphg,1072
|
|
26
|
-
omni_cortex-1.12.1.dist-info/RECORD,,
|
|
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
|
{omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/dashboard/backend/uv.lock
RENAMED
|
File without changes
|
|
File without changes
|
{omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/post_tool_use.py
RENAMED
|
File without changes
|
{omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/pre_tool_use.py
RENAMED
|
File without changes
|
{omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/session_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/subagent_stop.py
RENAMED
|
File without changes
|
{omni_cortex-1.12.1.data → omni_cortex-1.13.0.data}/data/share/omni-cortex/hooks/user_prompt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|