Ensure reasoning effort is always explicitly displayed with model name
This commit is contained in:
@@ -115,6 +115,7 @@ class Project:
|
|||||||
last_response: Optional[str] = None
|
last_response: Optional[str] = None
|
||||||
created_at: float = field(default_factory=time.time)
|
created_at: float = field(default_factory=time.time)
|
||||||
description: str = ""
|
description: str = ""
|
||||||
|
conversation_titles: Dict[str, str] = field(default_factory=dict)
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Session:
|
class Session:
|
||||||
@@ -249,6 +250,7 @@ class SessionManager:
|
|||||||
"last_response",
|
"last_response",
|
||||||
"created_at",
|
"created_at",
|
||||||
"description",
|
"description",
|
||||||
|
"conversation_titles",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if "name" not in clean_p:
|
if "name" not in clean_p:
|
||||||
@@ -261,6 +263,8 @@ class SessionManager:
|
|||||||
clean_p["conversation_history"] = []
|
clean_p["conversation_history"] = []
|
||||||
if clean_p.get("conversation_id") and clean_p["conversation_id"] not in clean_p["conversation_history"]:
|
if clean_p.get("conversation_id") and clean_p["conversation_id"] not in clean_p["conversation_history"]:
|
||||||
clean_p["conversation_history"].append(clean_p["conversation_id"])
|
clean_p["conversation_history"].append(clean_p["conversation_id"])
|
||||||
|
if "conversation_titles" not in clean_p or not isinstance(clean_p["conversation_titles"], dict):
|
||||||
|
clean_p["conversation_titles"] = {}
|
||||||
|
|
||||||
# Non-admin users cannot own or access the "default" /root project
|
# Non-admin users cannot own or access the "default" /root project
|
||||||
if not is_admin_user and (p_name == "default" or clean_p.get("workspace") == settings.default_workspace):
|
if not is_admin_user and (p_name == "default" or clean_p.get("workspace") == settings.default_workspace):
|
||||||
@@ -334,6 +338,7 @@ class SessionManager:
|
|||||||
"last_response": p_obj.last_response,
|
"last_response": p_obj.last_response,
|
||||||
"created_at": p_obj.created_at,
|
"created_at": p_obj.created_at,
|
||||||
"description": p_obj.description,
|
"description": p_obj.description,
|
||||||
|
"conversation_titles": p_obj.conversation_titles,
|
||||||
}
|
}
|
||||||
curr = self.get_current_project(sess.chat_id)
|
curr = self.get_current_project(sess.chat_id)
|
||||||
data[str(k)] = {
|
data[str(k)] = {
|
||||||
@@ -950,6 +955,9 @@ class SessionManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error removing brain dir {brain_path}: {e}")
|
logger.error(f"Error removing brain dir {brain_path}: {e}")
|
||||||
|
|
||||||
|
if hasattr(curr, "conversation_titles") and matched_cid in curr.conversation_titles:
|
||||||
|
curr.conversation_titles.pop(matched_cid, None)
|
||||||
|
|
||||||
self.save()
|
self.save()
|
||||||
return True, f"✅ گفتگوی <code>{matched_cid[:8]}...</code> با موفقیت حذف شد."
|
return True, f"✅ گفتگوی <code>{matched_cid[:8]}...</code> با موفقیت حذف شد."
|
||||||
|
|
||||||
@@ -981,6 +989,8 @@ class SessionManager:
|
|||||||
|
|
||||||
curr.conversation_history = []
|
curr.conversation_history = []
|
||||||
curr.conversation_id = None
|
curr.conversation_id = None
|
||||||
|
if hasattr(curr, "conversation_titles"):
|
||||||
|
curr.conversation_titles = {}
|
||||||
curr.last_response = None
|
curr.last_response = None
|
||||||
curr.last_context_length = None
|
curr.last_context_length = None
|
||||||
curr.last_total_tokens = None
|
curr.last_total_tokens = None
|
||||||
@@ -990,6 +1000,55 @@ class SessionManager:
|
|||||||
self.save()
|
self.save()
|
||||||
return True, f"✅ تمام {count} گفتگوی پروژه با موفقیت حذف و پاکسازی شدند.", count
|
return True, f"✅ تمام {count} گفتگوی پروژه با موفقیت حذف و پاکسازی شدند.", count
|
||||||
|
|
||||||
|
def set_conversation_title(self, chat_id: int, title: str, conv_id: Optional[str] = None, project_name: Optional[str] = None) -> tuple[bool, str, str]:
|
||||||
|
"""
|
||||||
|
Sets a custom title / topic for the specified conversation (or active conversation).
|
||||||
|
Returns (success, message, matched_conv_id).
|
||||||
|
"""
|
||||||
|
session = self.get_or_create(chat_id)
|
||||||
|
target_proj = None
|
||||||
|
if project_name:
|
||||||
|
accessible = self.get_all_accessible_projects(chat_id)
|
||||||
|
for k, p in accessible.items():
|
||||||
|
if k.lower() == project_name.lower() or p.name.lower() == project_name.lower():
|
||||||
|
target_proj = p
|
||||||
|
break
|
||||||
|
if not target_proj:
|
||||||
|
target_proj = self.get_current_project(chat_id)
|
||||||
|
|
||||||
|
if not target_proj:
|
||||||
|
return False, "⚠️ پروژهای یافت نشد.", ""
|
||||||
|
|
||||||
|
clean_title = (title or "").strip()
|
||||||
|
if not clean_title:
|
||||||
|
return False, "⚠️ عنوان مشخص نشده است.", ""
|
||||||
|
|
||||||
|
target_cid = conv_id.strip() if conv_id else (target_proj.conversation_id or "")
|
||||||
|
|
||||||
|
# If user passed a number like "1", "#1", or partial ID
|
||||||
|
if target_cid and (target_cid.startswith("#") or target_cid.isdigit() or len(target_cid) < 15):
|
||||||
|
convs = self.get_project_conversations(chat_id)
|
||||||
|
clean_num = target_cid.lstrip("#")
|
||||||
|
if clean_num.isdigit():
|
||||||
|
idx = int(clean_num)
|
||||||
|
if 1 <= idx <= len(convs):
|
||||||
|
target_cid = convs[idx - 1]["id"]
|
||||||
|
else:
|
||||||
|
for c in convs:
|
||||||
|
if c["id"].lower().startswith(target_cid.lower()):
|
||||||
|
target_cid = c["id"]
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_cid:
|
||||||
|
return False, "⚠️ گفتگوی فعالی برای تغییر عنوان یافت نشد.", ""
|
||||||
|
|
||||||
|
if not hasattr(target_proj, "conversation_titles") or not isinstance(target_proj.conversation_titles, dict):
|
||||||
|
target_proj.conversation_titles = {}
|
||||||
|
|
||||||
|
target_proj.conversation_titles[target_cid] = clean_title
|
||||||
|
self.save()
|
||||||
|
return True, f"✅ عنوان گفتگو به «<b>{escape_html(clean_title)}</b>» تغییر یافت.", target_cid
|
||||||
|
|
||||||
def get_project_conversations(self, chat_id: int) -> List[Dict[str, Any]]:
|
def get_project_conversations(self, chat_id: int) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Returns sorted list of conversation summaries for active project (newest first).
|
Returns sorted list of conversation summaries for active project (newest first).
|
||||||
@@ -1017,14 +1076,17 @@ class SessionManager:
|
|||||||
logger.debug(f"Error scanning brain dir: {e}")
|
logger.debug(f"Error scanning brain dir: {e}")
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
|
titles_map = getattr(curr, "conversation_titles", {}) or {}
|
||||||
for cid in conv_ids:
|
for cid in conv_ids:
|
||||||
meta = get_conversation_metadata(cid)
|
meta = get_conversation_metadata(cid)
|
||||||
if not meta.get("file_exists") and cid not in curr.conversation_history:
|
if not meta.get("file_exists") and cid not in curr.conversation_history:
|
||||||
continue
|
continue
|
||||||
is_current = (cid == curr.conversation_id)
|
is_current = (cid == curr.conversation_id)
|
||||||
|
custom_title = titles_map.get(cid)
|
||||||
result.append({
|
result.append({
|
||||||
"id": cid,
|
"id": cid,
|
||||||
"is_current": is_current,
|
"is_current": is_current,
|
||||||
|
"title": custom_title,
|
||||||
"first_prompt": meta.get("first_prompt") or "(بدون متن)",
|
"first_prompt": meta.get("first_prompt") or "(بدون متن)",
|
||||||
"last_prompt": meta.get("last_prompt") or "",
|
"last_prompt": meta.get("last_prompt") or "",
|
||||||
"turns_count": meta.get("turns_count", 0),
|
"turns_count": meta.get("turns_count", 0),
|
||||||
@@ -1037,6 +1099,7 @@ class SessionManager:
|
|||||||
result.sort(key=lambda x: x.get("last_updated") or 0.0, reverse=True)
|
result.sort(key=lambda x: x.get("last_updated") or 0.0, reverse=True)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
async def set_model(self, chat_id: int, model: str):
|
async def set_model(self, chat_id: int, model: str):
|
||||||
session = self.get_or_create(chat_id)
|
session = self.get_or_create(chat_id)
|
||||||
curr = self.get_current_project(chat_id)
|
curr = self.get_current_project(chat_id)
|
||||||
@@ -1110,17 +1173,61 @@ def strip_ansi(text: str) -> str:
|
|||||||
return ANSI_REGEX.sub("", text)
|
return ANSI_REGEX.sub("", text)
|
||||||
|
|
||||||
def clean_user_prompt(raw_text: Optional[str]) -> str:
|
def clean_user_prompt(raw_text: Optional[str]) -> str:
|
||||||
|
"""
|
||||||
|
Cleans raw prompt or transcript user input, stripping all internal system instructions,
|
||||||
|
prompts metadata, memory blocks, and file markers to extract the actual user message.
|
||||||
|
"""
|
||||||
if not raw_text:
|
if not raw_text:
|
||||||
return ""
|
return ""
|
||||||
text = raw_text
|
text = str(raw_text)
|
||||||
|
|
||||||
|
# 1. Extract USER_REQUEST content if present
|
||||||
req_match = re.search(r"<USER_REQUEST>(.*?)</USER_REQUEST>", text, re.DOTALL)
|
req_match = re.search(r"<USER_REQUEST>(.*?)</USER_REQUEST>", text, re.DOTALL)
|
||||||
if req_match:
|
if req_match:
|
||||||
text = req_match.group(1)
|
text = req_match.group(1)
|
||||||
text = re.sub(r"\[SYSTEM INSTRUCTION:.*?\]", "", text, flags=re.DOTALL)
|
|
||||||
|
# 2. Remove standard outer XML/HTML metadata tags
|
||||||
text = re.sub(r"<ADDITIONAL_METADATA>.*?</ADDITIONAL_METADATA>", "", text, flags=re.DOTALL)
|
text = re.sub(r"<ADDITIONAL_METADATA>.*?</ADDITIONAL_METADATA>", "", text, flags=re.DOTALL)
|
||||||
text = re.sub(r"<USER_SETTINGS_CHANGE>.*?</USER_SETTINGS_CHANGE>", "", text, flags=re.DOTALL)
|
text = re.sub(r"<USER_SETTINGS_CHANGE>.*?</USER_SETTINGS_CHANGE>", "", text, flags=re.DOTALL)
|
||||||
|
text = re.sub(r"<SYSTEM_MESSAGE>.*?</SYSTEM_MESSAGE>", "", text, flags=re.DOTALL)
|
||||||
text = re.sub(r"<[^>]+>", "", text)
|
text = re.sub(r"<[^>]+>", "", text)
|
||||||
return text.strip()
|
|
||||||
|
# 3. Remove delimited system instruction blocks
|
||||||
|
text = re.sub(r"<!--\s*SYSTEM_INSTRUCTIONS_START\s*-->.*?<!--\s*SYSTEM_INSTRUCTIONS_END\s*-->", "", text, flags=re.DOTALL)
|
||||||
|
text = re.sub(r"\[SYSTEM_INSTRUCTIONS_BLOCK\].*?\[/SYSTEM_INSTRUCTIONS_BLOCK\]", "", text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
# 4. Remove known structured system instructions (backward compatibility)
|
||||||
|
# Language instruction
|
||||||
|
text = re.sub(r"\[SYSTEM INSTRUCTION:\s*You MUST respond and communicate in [^\]]+\]", "", text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
# Flash auto reasoning mode block
|
||||||
|
text = re.sub(r"\[SYSTEM INSTRUCTION:\s*GEMINI 3\.7 FLASH AUTO REASONING MODE\].*?(?:• آدرس مخزن:[^\n]*|\Z)", "", text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
# Bot actions block
|
||||||
|
text = re.sub(r"\[SYSTEM INSTRUCTION:\s*TELEGRAM BOT ACTIONS & FUNCTION CALLING\].*?(?:Always explain to the user in a friendly and professional tone what actions have been performed\.?|\Z)", "", text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
# Memory instructions block
|
||||||
|
text = re.sub(r"\[SYSTEM INSTRUCTION:\s*AI PERSISTENT HIERARCHICAL MEMORY & CONTINUOUS LEARNING\].*?(?:5\. Do NOT store temporary or trivial small-talk\. Only store enduring and valuable knowledge\.?|\Z)", "", text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
# General fallback for any remaining [SYSTEM INSTRUCTION: ...]
|
||||||
|
text = re.sub(r"\[SYSTEM INSTRUCTION:[^\]]+\]", "", text)
|
||||||
|
text = re.sub(r"(?:^|\n)System Instruction:\s*", "", text)
|
||||||
|
|
||||||
|
# Clean uploaded file notifications
|
||||||
|
caption_m = re.search(r"\[User uploaded [^\]]+\]\s*(?:Caption:\s*(.*))?", text, re.DOTALL)
|
||||||
|
if caption_m:
|
||||||
|
cap = (caption_m.group(1) or "").strip()
|
||||||
|
if cap:
|
||||||
|
text = f"📎 {cap}"
|
||||||
|
else:
|
||||||
|
text = "📎 [ارسال فایل]"
|
||||||
|
|
||||||
|
# Clean ANSI codes
|
||||||
|
text = ANSI_REGEX.sub("", text)
|
||||||
|
|
||||||
|
# Collapse multi-lines to clean single-line text
|
||||||
|
lines = [line.strip() for line in text.splitlines() if line.strip()]
|
||||||
|
return " ".join(lines).strip()
|
||||||
|
|
||||||
def get_conversation_metadata(conversation_id: str) -> Dict[str, Any]:
|
def get_conversation_metadata(conversation_id: str) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
@@ -1163,7 +1270,14 @@ def get_conversation_metadata(conversation_id: str) -> Dict[str, Any]:
|
|||||||
if user_inputs:
|
if user_inputs:
|
||||||
res["created_at"] = user_inputs[0].get("created_at")
|
res["created_at"] = user_inputs[0].get("created_at")
|
||||||
first_raw = user_inputs[0].get("content", "")
|
first_raw = user_inputs[0].get("content", "")
|
||||||
res["first_prompt"] = clean_user_prompt(first_raw)
|
first_cleaned = clean_user_prompt(first_raw)
|
||||||
|
if not first_cleaned and len(user_inputs) > 1:
|
||||||
|
for u in user_inputs[1:]:
|
||||||
|
nxt_clean = clean_user_prompt(u.get("content", ""))
|
||||||
|
if nxt_clean:
|
||||||
|
first_cleaned = nxt_clean
|
||||||
|
break
|
||||||
|
res["first_prompt"] = first_cleaned or "(بدون متن)"
|
||||||
last_raw = user_inputs[-1].get("content", "")
|
last_raw = user_inputs[-1].get("content", "")
|
||||||
res["last_prompt"] = clean_user_prompt(last_raw)
|
res["last_prompt"] = clean_user_prompt(last_raw)
|
||||||
|
|
||||||
@@ -1348,7 +1462,12 @@ class AGYEngine:
|
|||||||
|
|
||||||
|
|
||||||
if system_instructions:
|
if system_instructions:
|
||||||
effective_prompt = "\n\n".join(system_instructions) + "\n\n" + prompt
|
effective_prompt = (
|
||||||
|
"<!-- SYSTEM_INSTRUCTIONS_START -->\n"
|
||||||
|
+ "\n\n".join(system_instructions)
|
||||||
|
+ "\n<!-- SYSTEM_INSTRUCTIONS_END -->\n\n"
|
||||||
|
+ prompt
|
||||||
|
)
|
||||||
|
|
||||||
# Real CLI model name to pass to AGY CLI
|
# Real CLI model name to pass to AGY CLI
|
||||||
cli_model = "gemini-3.7-flash" if target_model in ("gemini-3.7-flash-auto", "gemini-3.7-flash") else target_model
|
cli_model = "gemini-3.7-flash" if target_model in ("gemini-3.7-flash-auto", "gemini-3.7-flash") else target_model
|
||||||
@@ -1584,11 +1703,16 @@ class AGYEngine:
|
|||||||
on_delta: Optional[Callable[[str], Any]] = None,
|
on_delta: Optional[Callable[[str], Any]] = None,
|
||||||
on_thought: Optional[Callable[[str], Any]] = None,
|
on_thought: Optional[Callable[[str], Any]] = None,
|
||||||
on_tool: Optional[Callable[[str, str], Any]] = None,
|
on_tool: Optional[Callable[[str, str], Any]] = None,
|
||||||
|
on_model_change: Optional[Callable[[str, Optional[str]], Any]] = None,
|
||||||
|
on_reset: Optional[Callable[[], Any]] = None,
|
||||||
) -> AgentResult:
|
) -> AgentResult:
|
||||||
"""Runs prompt using the natively authenticated AGY CLI engine with real-time streaming and Auto-Effort escalation."""
|
"""Runs prompt using the natively authenticated AGY CLI engine with real-time streaming and Auto-Effort escalation."""
|
||||||
is_auto_mode = (session.model == "gemini-3.7-flash-auto")
|
is_auto_mode = (session.model == "gemini-3.7-flash-auto")
|
||||||
|
|
||||||
if is_auto_mode:
|
if is_auto_mode:
|
||||||
|
if on_model_change:
|
||||||
|
on_model_change("gemini-3.7-flash-auto", "medium")
|
||||||
|
|
||||||
# First attempt in Auto mode: Start with Medium effort by default
|
# First attempt in Auto mode: Start with Medium effort by default
|
||||||
result, esc_effort, esc_reason = await cls._execute_single_run(
|
result, esc_effort, esc_reason = await cls._execute_single_run(
|
||||||
session=session,
|
session=session,
|
||||||
@@ -1605,6 +1729,12 @@ class AGYEngine:
|
|||||||
target_eff = esc_effort if esc_effort in ("low", "medium", "high") else "medium"
|
target_eff = esc_effort if esc_effort in ("low", "medium", "high") else "medium"
|
||||||
logger.info(f"Auto-adjusting effort for chat {session.chat_id} to {target_eff} (reason: {esc_reason})")
|
logger.info(f"Auto-adjusting effort for chat {session.chat_id} to {target_eff} (reason: {esc_reason})")
|
||||||
|
|
||||||
|
if on_reset:
|
||||||
|
on_reset()
|
||||||
|
|
||||||
|
if on_model_change:
|
||||||
|
on_model_change("gemini-3.7-flash-auto", target_eff)
|
||||||
|
|
||||||
is_fa = (session.language or "").lower() in ("fa", "farsi", "persian", "🇮🇷 persian / farsi (فارسی)")
|
is_fa = (session.language or "").lower() in ("fa", "farsi", "persian", "🇮🇷 persian / farsi (فارسی)")
|
||||||
if on_thought:
|
if on_thought:
|
||||||
if target_eff == "low":
|
if target_eff == "low":
|
||||||
@@ -1647,6 +1777,9 @@ class AGYEngine:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
# Standard execution for all other models
|
# Standard execution for all other models
|
||||||
|
if on_model_change:
|
||||||
|
on_model_change(session.model, session.effort)
|
||||||
|
|
||||||
result, _, _ = await cls._execute_single_run(
|
result, _, _ = await cls._execute_single_run(
|
||||||
session=session,
|
session=session,
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
|
|||||||
@@ -890,6 +890,32 @@ async def execute_action(
|
|||||||
ok, msg, count = session_manager.clear_project_conversations(chat_id)
|
ok, msg, count = session_manager.clear_project_conversations(chat_id)
|
||||||
return f"\n\n🗑️ {msg}", side_effects, created_task
|
return f"\n\n🗑️ {msg}", side_effects, created_task
|
||||||
|
|
||||||
|
# -------------------------------------------------------------
|
||||||
|
# 13.5 SET_CONVERSATION_TITLE / RENAME_CONVERSATION / SET_TOPIC
|
||||||
|
# -------------------------------------------------------------
|
||||||
|
elif act in ("SET_CONVERSATION_TITLE", "SET_CONV_TITLE", "RENAME_CONVERSATION", "RENAME_CONV", "SET_TOPIC", "CONV_TITLE"):
|
||||||
|
title = attrs.get("title") or attrs.get("name") or attrs.get("topic") or attrs.get("_default", "")
|
||||||
|
conv_id = attrs.get("id") or attrs.get("conv_id")
|
||||||
|
proj = attrs.get("project") or attrs.get("proj") or current_project_name
|
||||||
|
|
||||||
|
if not title:
|
||||||
|
return "\n⚠️ عنوان جدید گفتگو مشخص نشده است." if is_fa else "\n⚠️ Conversation title not specified.", side_effects, created_task
|
||||||
|
|
||||||
|
ok, msg, matched_id = session_manager.set_conversation_title(chat_id, title=title, conv_id=conv_id, project_name=proj)
|
||||||
|
if not ok:
|
||||||
|
return f"\n⚠️ {msg}", side_effects, created_task
|
||||||
|
|
||||||
|
badge = (
|
||||||
|
f"\n\n🏷️ <b>موضوع گفتگو بهروزرسانی شد:</b>\n"
|
||||||
|
f"• 💬 <b>شناسه:</b> <code>{matched_id[:8]}...</code>\n"
|
||||||
|
f"• 📝 <b>عنوان جدید:</b> «{escape_html(title)}»"
|
||||||
|
if is_fa else
|
||||||
|
f"\n\n🏷️ <b>Conversation Topic Updated:</b>\n"
|
||||||
|
f"• 💬 <b>ID:</b> <code>{matched_id[:8]}...</code>\n"
|
||||||
|
f"• 📝 <b>New Title:</b> \"{escape_html(title)}\""
|
||||||
|
)
|
||||||
|
return badge, side_effects, created_task
|
||||||
|
|
||||||
# -------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
# 14. SCHEDULE_TASK / SCHEDULE / CREATE_TASK
|
# 14. SCHEDULE_TASK / SCHEDULE / CREATE_TASK
|
||||||
# -------------------------------------------------------------
|
# -------------------------------------------------------------
|
||||||
@@ -1736,7 +1762,11 @@ def get_ai_bot_actions_instruction(lang: str = "fa") -> str:
|
|||||||
" [[SWITCH_CONVERSATION: id=\"<conv_id_or_empty_for_last>\"]]\n"
|
" [[SWITCH_CONVERSATION: id=\"<conv_id_or_empty_for_last>\"]]\n"
|
||||||
" [[DELETE_CONVERSATION: id=\"<conv_id>\"]]\n"
|
" [[DELETE_CONVERSATION: id=\"<conv_id>\"]]\n"
|
||||||
" [[CLEAR_CONVERSATIONS: project=\"<project_optional>\"]]\n\n"
|
" [[CLEAR_CONVERSATIONS: project=\"<project_optional>\"]]\n\n"
|
||||||
"12. Schedule Task / Timers / Cron:\n"
|
"12. Update / Change Conversation Topic & Title (Dynamic Conversation Naming):\n"
|
||||||
|
" Whenever the user changes topic, starts a distinct new task, or asks to set the conversation name:\n"
|
||||||
|
" [[SET_CONVERSATION_TITLE: title=\"<concise_topic_title>\", id=\"<conv_id_optional>\", project=\"<project_optional>\"]]\n"
|
||||||
|
" Example: [[SET_CONVERSATION_TITLE: title=\"پیادهسازی ماژول پرداخت\"]]\n\n"
|
||||||
|
"13. Schedule Task / Timers / Cron:\n"
|
||||||
" [[SCHEDULE_TASK: timing=\"<timing_spec>\", max_runs=<count_or_null>, type=\"prompt|command|reminder\", title=\"<short_title>\", content=\"<exact_prompt_or_command>\"]]\n"
|
" [[SCHEDULE_TASK: timing=\"<timing_spec>\", max_runs=<count_or_null>, type=\"prompt|command|reminder\", title=\"<short_title>\", content=\"<exact_prompt_or_command>\"]]\n"
|
||||||
" Timing examples: 'every 10m', 'in 15m', '14:30', 'cron: 0 23 * * *', 'every 1h (3 times)'.\n"
|
" Timing examples: 'every 10m', 'in 15m', '14:30', 'cron: 0 23 * * *', 'every 1h (3 times)'.\n"
|
||||||
" Types: 'prompt' (AI run), 'command' (bash script), 'reminder' (plain notification).\n"
|
" Types: 'prompt' (AI run), 'command' (bash script), 'reminder' (plain notification).\n"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 243 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 235 KiB |
Reference in New Issue
Block a user