oclaw/runtime/scheduler/turn_text.py
oliver 4898adf7d0 Productize scheduled success delivery, close WA pending, and clarify SQL scope grants.
Wrap successful scheduled jobs with a short WA header (+ attachment count), expire stale access pending and DM requesters on admin approve/deny, and add concrete next_steps for insufficient_scope:sql:query.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 23:52:41 +08:00

136 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from typing import Any
from runtime.scheduler.recipe import compile_playbook_instruction, recipe_has_playbook
def format_scheduled_user_reminder(prompt_text: str, *, lang: str = "en") -> str:
body = str(prompt_text or "").strip()
if not body:
return ""
if body.startswith("⏰"):
return body
if str(lang or "").lower().startswith("zh"):
return f"⏰ 提醒:{body}"
return f"⏰ Reminder: {body}"
def format_scheduled_success_summary(
*,
job_name: str = "",
job_id: str = "",
reply_text: str = "",
attachment_count: int = 0,
lang: str = "en",
max_body_chars: int = 1200,
) -> str:
"""Short user-facing success notice; keeps body but caps length for WhatsApp."""
name = str(job_name or "").strip() or str(job_id or "").strip() or "scheduled job"
body = str(reply_text or "").strip()
# Drop pure reminder fallbacks that just echo the job prompt.
if body.startswith("⏰"):
body = ""
att_n = max(0, int(attachment_count or 0))
cap = max(200, int(max_body_chars or 1200))
if body and len(body) > cap:
body = body[: cap - 3].rstrip() + "..."
if str(lang or "").lower().startswith("zh"):
head = f"[定时任务完成] {name}"
if att_n:
head += f"\n附件:{att_n} 个"
if body:
return f"{head}\n{body}"
return head + ("\n(本次无文字摘要,请查看附件。)" if att_n else "\n(本次无摘要内容。)")
head = f"[Scheduled job done] {name}"
if att_n:
head += f"\nAttachments: {att_n}"
if body:
return f"{head}\n{body}"
return head + ("\n(No text summary; see attachment(s).)" if att_n else "\n(No summary content.)")
def format_scheduled_failure_summary(
*,
job_name: str = "",
job_id: str = "",
error: str = "",
lang: str = "en",
) -> str:
"""Short user-facing failure notice for WhatsApp/WeChat scheduled delivery."""
name = str(job_name or "").strip() or str(job_id or "").strip() or "scheduled job"
err = " ".join(str(error or "").strip().split())
if len(err) > 240:
err = err[:237] + "..."
if not err:
err = "unknown error"
if str(lang or "").lower().startswith("zh"):
return f"[定时任务失败] {name}\n错误:{err}\n请检查任务配置或手动重试。"
return f"[Scheduled job failed] {name}\nError: {err}\nCheck the job config or retry manually."
def build_scheduled_turn_instruction(
*,
prompt_text: str,
mode: str,
lang: str,
recipe: dict[str, Any] | None = None,
) -> str:
"""Internal LLM instruction for proactive scheduled reminders/playbooks (not user-facing)."""
_ = str(mode or "scheduled").strip()
if recipe_has_playbook(recipe):
return compile_playbook_instruction(recipe=recipe or {}, lang=lang)
intent = str(prompt_text or "").strip()
is_en = str(lang or "").lower().startswith("en")
if is_en:
return (
"[Scheduled proactive reminder — internal instruction, not a user message]\n"
f"Reminder intent: {intent}\n"
"Write a short, friendly proactive reminder TO the user (second person). "
"Do not say you received a reminder or that you will remind someone; speak directly to the user."
)
return (
"【定时主动提醒·内部指令,不是用户发言】\n"
f"提醒意图:{intent}\n"
"请生成一条简短、自然、第二人称的主动提醒消息直接对用户说。"
"不要写「收到提醒」「好的我来提醒用户」等元对话;不要假装用户刚说了话。"
)
def scheduled_turn_system_suffix(*, lang: str, playbook: bool = False) -> str:
is_en = str(lang or "").lower().startswith("en")
if playbook:
if is_en:
return (
"\n\n[Scheduled playbook mode] You are executing a recurring workflow for the user. "
"Follow the playbook steps, use tools as needed, and deliver a useful update "
"(including save_deliverable_attachment for generated files). "
"Lead the final reply with a short English summary (3–8 lines: what ran, key counts, "
"ok/failed highlights), then optional detail. "
"Do not pretend the user just messaged you."
)
return (
"\n\n【定时工作流模式】你正在执行周期性工作流。"
"按 playbook 步骤完成任务,按需调用工具;若生成文件须 save_deliverable_attachment。"
"最终回复先给 3–8 行摘要(做了什么、关键计数、成败),再写细节。"
"不要假装用户刚刚发了消息,不要只回一句空提醒。"
)
if is_en:
return (
"\n\n[Scheduled job mode] You are sending a proactive reminder to the user. "
"Reply with the reminder text only; do not role-play as the user."
)
return (
"\n\n【定时任务模式】你正在主动向用户发送提醒。"
"只输出提醒正文,不要扮演用户,不要写「收到/好的」等对话式应答。"
)
__all__ = [
"build_scheduled_turn_instruction",
"format_scheduled_failure_summary",
"format_scheduled_success_summary",
"format_scheduled_user_reminder",
"scheduled_turn_system_suffix",
]