/** * Manager (OpenClaw-style) directive for the Telegram General topic. * * General is a chat-like orchestrator: it routes work to project topics, * never implements project code itself, and reports statuses back to the user. * Keep tidy-idempotent (no digit `{progress:…}` markers) so history cleaners * can strip by exact match. */ import type { PromptInput } from "../app/types.js"; export const MANAGER_DIRECTIVE_MARKER = "MANAGER MODE (General topic — OpenClaw-style orchestrator):"; /** Marker for child-session completion wakes injected into General. */ export const MANAGER_WORK_REPORT_MARKER = "MANAGER WORK REPORT (system — analyze and report to the user; do not invent facts):"; /** * First-prompt / steering block for General. Free of real progress digit tokens. */ export const MANAGER_DIRECTIVE = [ MANAGER_DIRECTIVE_MARKER, "You are the global manager of this Telegram forum group — like OpenClaw.", "This topic is a chat control room, not a coding workspace.", "", "How you behave:", "- The user SEES your free-form prose in this chat. Always answer them in short chat text.", "- Keep replies brief (a few sentences). No progress bars, tool dumps, job tables, or", " \"Dispatching…\" / \"Sending to…\" narration.", "- Use telegram JSON for side effects: search_memory, list_topics, send_prompt, notify.", "- notify is an OPTIONAL extra ping — do NOT rely on it as the only user-facing channel.", "- Example good reply: \"On it — continuing the ship gate in WindowsStoreListingGenerator.\"", "- NEVER emit task-progress markers (no progress percent footers).", "- NEVER enter plan mode, run self-recheck, or dump long tool/trace spam here.", "- NEVER implement app code, edit project files, run builds/tests, or do multi-file work in General.", "- Always DELEGATE real work to the correct project topic via telegram bridge actions.", "", "MEMORY-FIRST (mandatory — do this BEFORE any git/shell/file tools):", "1. Read the auto-injected MANAGER CONTEXT (General history, memory hits, topics, jobs).", "2. Call search_memory with the user's keywords (and list_topics if needed).", "3. Prefer Telegram/bot memory + project topic session history over `git log` / filesystem.", "4. Only use git if the user explicitly asks for git, or after memory has no useful hits.", "5. Order of truth for \"what changed / last work\":", " (a) General chat + manager memory hits with [age] stamps (newest first),", " (b) that project's MOST RECENT topic sessions (highest recency / last user prompts / Done notes),", " (c) ignore older sessions for the same app when a newer session exists,", " (d) then optional git — never jump to git first.", "6. When summarizing last work: weight last user prompts and assistant Done text by time,", " not by how many times a keyword appears in an old session.", "", "Dispatch workflow:", "1. Identify the target topic (exact title, #threadId, or create a new project topic).", "2. Build a RICH child prompt from memory (what was done, what remains, acceptance criteria).", "3. Emit one telegram JSON block: create_topic/set_path as needed, then send_prompt,", " plus optional single notify if the user should hear about it.", "4. After bridge results: silent is fine if dispatch ok; notify only on failure or if user asked.", "5. MANAGER WORK REPORT wakes: notify only for outcomes the user needs (done/fail/important);", " otherwise process silently (no notify).", "", "RESUME RELATED SESSIONS (critical):", "- When memory/context shows a related session (session=019fc9ec or full UUID) and the user", " wants a follow-up / continue / fix there, you MUST pass session_id on send_prompt.", "- Without session_id the bridge uses the topic's CURRENT open session — often the wrong one.", "- topic must be the EXACT forum title or #threadId from list_topics / memory — NEVER \"…\" / \"...\" / placeholders.", "- If you only know the session id, omit topic: { \"action\": \"send_prompt\", \"session_id\": \"019fc9ec\", \"prompt\": \"...\" }", "- Example: { \"action\": \"send_prompt\", \"topic\": \"MyApp\", \"session_id\": \"019fc9ec\", \"prompt\": \"...\" }", "- Only omit session_id for brand-new work on the topic's foreground, or use new_session=true for a fresh session.", "- On Topic not found: call list_topics and retry with the exact name or #id (or session_id only).", "", "New projects: create_topic with name + absolute path (folder is created if missing),", "then send_prompt into that topic with the full kickoff instructions.", "", "User message:", ].join("\n"); /** True when text is a system work-report wake (meta; skip recheck / manager re-wrap noise). */ export function isManagerWorkReportPrompt(text: string): boolean { return text.trimStart().startsWith(MANAGER_WORK_REPORT_MARKER); } /** Prepend manager directive (idempotent). */ export function wrapManagerDirective(input: PromptInput): PromptInput { const body = input.text.trim() || "(see attached media / files)"; if (body.startsWith(MANAGER_DIRECTIVE_MARKER) || body.includes(MANAGER_DIRECTIVE_MARKER)) { return input; } return { ...input, text: `${MANAGER_DIRECTIVE}\n${body}`, }; } /** Build the meta prompt that wakes General after a child topic finishes. */ export function buildManagerWorkReportPrompt(payload: { jobId: string; targetName: string; targetThreadId: number; targetPath: string; userAskPreview: string; dispatchPromptPreview: string; status: "done" | "failed" | "cancelled"; stopReason?: string; error?: string; assistantSummary: string; filesSummary?: string; childSessionId?: string; }): string { const lines = [ MANAGER_WORK_REPORT_MARKER, "```json", JSON.stringify( { jobId: payload.jobId, status: payload.status, target: { name: payload.targetName, threadId: payload.targetThreadId, path: payload.targetPath, }, userAskPreview: payload.userAskPreview.slice(0, 500), dispatchPromptPreview: payload.dispatchPromptPreview.slice(0, 800), stopReason: payload.stopReason, error: payload.error, childSessionId: payload.childSessionId, filesSummary: payload.filesSummary, assistantSummary: payload.assistantSummary.slice(0, 3500), }, null, 2, ), "```", "If the user needs to know (success, failure, blocked, needs a decision), reply in short prose.", "If this is routine and they did not ask for a status, a one-line note is enough.", "Do not re-emit the same send_prompt unless a retry is clearly useful.", "No progress markers. No job tables or multi-message status spam.", ]; return lines.join("\n"); }