Files
Software Orchestrator 5769c1cd22 Initial commit: a0_software_orchestrator v1.0
- Auto-Registration-Bug behoben (register_project/get_project_id/resolve_project Trennung)
- 25 Tests gruen (Pytest)
- block_compactor-Tool refactored (Option B: Soft-Check statt Hard-Block)
- 4 Restbaustellen gefixt
- DB-Schema: plugin_settings-Tabelle hinzugefuegt
- 3 Schattenprojekte aus DB geloescht
- Plan v3 + Refactor-Plan + Worklog dokumentiert
2026-06-16 22:13:06 +00:00

126 lines
4.8 KiB
Python
Raw Permalink 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.
"""Tool: block_resume Recovery-Übersicht am Chat-Start (DB-backed)."""
from __future__ import annotations
import json
import os
from typing import Any, Dict, List, Optional
from helpers.tool import Tool, Response
from usr.plugins.a0_software_orchestrator.helpers.compact_protocol import get_resume_payload
from usr.plugins.a0_software_orchestrator.helpers.db_state_store import (
get_project_id,
get_status, list_next_steps, list_todos, list_errors, list_worklog,
get_kv, get_block_compact,
)
from usr.plugins.a0_software_orchestrator.helpers.exceptions import PluginDBError
def _resolve_name(project_name: str = "", project_root: str = "") -> str:
if project_name:
return project_name.strip()
if project_root:
return os.path.basename(project_root.rstrip("/"))
return os.path.basename(os.getcwd())
def _build_status_line(artifacts_state: Dict[str, bool]) -> str:
parts: List[str] = []
for key, ok in artifacts_state.items():
marker = "" if ok else ""
parts.append(f"{key}={marker}")
return ", ".join(parts) if parts else "(none)"
class BlockResume(Tool):
"""Lädt die kompakte 10-Zeilen-Resume-Übersicht aus der DB.
Args (kwargs):
project_name (str): Projektname (bevorzugt).
project_root (str): Legacy wird zu basename() aufgelöst.
include_full_resume (bool): Wenn True, zusätzlich resume_md ausgeben.
include_conversation_summary (bool): Wenn True, zusätzlich conversation_summary_md.
"""
async def execute(
self,
project_name: str = "",
project_root: str = "",
include_full_resume: bool = False,
include_conversation_summary: bool = False,
**kwargs,
):
name = _resolve_name(project_name, project_root)
try:
pid = get_project_id(name)
if pid is None:
raise LookupError(f"project '{name}' is not registered")
except (ValueError, LookupError) as e:
return Response(message=f"ERROR: {e}", break_loop=False)
except PluginDBError as e:
return Response(message=f"Database error: {e}", break_loop=False)
bc = get_block_compact(pid)
status = get_status(pid)
pending_steps = list_next_steps(pid, status="pending")
open_todos = list_todos(pid, status="open")
open_errors = list_errors(pid, status="open")
recent_worklog = list_worklog(pid, limit=5)
phase = get_kv(pid, "phase", default="<unknown>")
# 10-Zeilen-Übersicht
lines: List[str] = [f"🔁 RESUME: {name} (pid={pid}) — phase={phase}"]
if bc:
lines.append(
f" last block: {bc.get('last_block_id')} @ {bc.get('last_compact_at')}"
)
lines.append(f" next block: {bc.get('next_block') or '(none)'}")
else:
lines.append(" no block_compact marker yet")
lines.append(
f" status: {status or '(none)'}"
)
lines.append(
f" pending next_steps: {len(pending_steps)}, "
f"open todos: {len(open_todos)}, open errors: {len(open_errors)}"
)
if bc:
decisions = bc.get("decisions") or []
key_findings = bc.get("key_findings") or []
open_questions = bc.get("open_questions") or []
lines.append(
f" last block decisions: {len(decisions)}, "
f"key_findings: {len(key_findings)}, "
f"open_questions: {len(open_questions)}"
)
# Optional: full resume + conversation summary
if include_full_resume and bc and bc.get("resume_md"):
lines.append("")
lines.append("=== Full resume.md ===")
lines.append(bc["resume_md"])
if include_conversation_summary and bc and bc.get("conversation_summary_md"):
lines.append("")
lines.append("=== Conversation summary ===")
lines.append(bc["conversation_summary_md"])
# Recent worklog (top 5)
if recent_worklog:
lines.append("")
lines.append("=== Recent worklog (top 5) ===")
for w in recent_worklog:
ts = w.get("created_at", "?")
blk = w.get("work_block") or "-"
agent = w.get("agent") or "-"
summary = (w.get("summary") or "")[:100]
lines.append(f" - [{ts}] {blk} ({agent}): {summary}")
# Hinweise für den Orchestrator
lines.append("")
lines.append("Hints:")
lines.append(" - Don't re-run subagents for already-done blocks (see worklog).")
lines.append(" - If pending_steps is empty, ask the user before continuing.")
lines.append(" - If open_errors > 0, address them first.")
return Response(message="\n".join(lines), break_loop=False)