Squash Odysseus development history

This commit is contained in:
pewdiepie-archdaemon
2026-09-11 06:04:19 +00:00
parent c9dd68d890
commit 84aa9a91de
871 changed files with 265870 additions and 27854 deletions
+136 -40
View File
@@ -77,6 +77,8 @@ async def do_manage_skills(content: str, owner: Optional[str] = None) -> Dict:
if action == "view":
if not name:
return {"error": "name is required for view", "exit_code": 1}
if args.get("path"):
return {"error": "view reads SKILL.md only; use view_ref with name and path to read a supporting file.", "exit_code": 1}
md = sm.read_skill_md(name, owner=owner)
if md is None:
return {"error": f"Skill {name!r} not found", "exit_code": 1}
@@ -104,17 +106,11 @@ async def do_manage_skills(content: str, owner: Optional[str] = None) -> Dict:
proc = args.get("steps") or []
if not proc and not args.get("body_extra") and not args.get("solution"):
return {"error": "procedure (or solution body) is required", "exit_code": 1}
# Same auto-publish gate as the extractor path — when the user
# has auto_approve_skills on and the caller didn't pin an explicit
# status, publish immediately. Audit later demotes/removes on fail.
# Newly learned procedures are always audited before they are allowed
# into chat context. The automatic audit promotes passing skills.
_status_arg = args.get("status")
if not _status_arg:
try:
from routes.prefs_routes import _load_for_user as _load_prefs
_prefs = _load_prefs(owner) or {}
_status_arg = "published" if _prefs.get("auto_approve_skills", True) else "draft"
except Exception:
_status_arg = "draft"
_status_arg = "draft"
entry = sm.add_skill(
name=args.get("name"),
description=(args.get("description") or args.get("title") or "").strip(),
@@ -162,7 +158,17 @@ async def do_manage_skills(content: str, owner: Optional[str] = None) -> Dict:
return {"error": "name is required for edit", "exit_code": 1}
new_content = args.get("content")
if not isinstance(new_content, str) or not new_content.strip():
return {"error": "content (full SKILL.md) is required for edit", "exit_code": 1}
metadata_updates = {
key: args[key] for key in (
"description", "category", "when_to_use", "version", "confidence",
"tags", "platforms", "requires_toolsets", "fallback_for_toolsets",
"procedure", "pitfalls", "verification",
) if key in args
}
if not metadata_updates:
return {"error": "content (full SKILL.md) or an editable metadata field is required for edit", "exit_code": 1}
ok = sm.update_skill(name, metadata_updates, owner=owner)
return {"results": f"Edited skill `{name}`."} if ok else {"error": "Skill not found or update failed", "exit_code": 1}
try:
sk_new = Skill.from_markdown(new_content)
except Exception as e:
@@ -184,6 +190,8 @@ async def do_manage_skills(content: str, owner: Optional[str] = None) -> Dict:
new_str = args.get("new_string", "")
if not isinstance(old, str) or not old:
return {"error": "old_string is required and must be non-empty", "exit_code": 1}
if not isinstance(new_str, str):
return {"error": "new_string must be a string; use an empty string to remove text", "exit_code": 1}
md = sm.read_skill_md(name, owner=owner)
if md is None:
return {"error": f"Skill {name!r} not found", "exit_code": 1}
@@ -211,8 +219,9 @@ async def do_manage_skills(content: str, owner: Optional[str] = None) -> Dict:
updates = {"status": "published"}
if args.get("confidence") is not None:
updates["confidence"] = max(0.0, min(1.0, float(args["confidence"])))
sm.update_skill(name, updates, owner=owner)
return {"results": f"✅ Published `{name}`. It now appears in the skills index for future turns."}
if not sm.update_skill(name, updates, owner=owner):
return {"error": "Skill could not be published; no update was saved.", "exit_code": 1}
return {"results": f"Published `{name}`. Automatic use remains subject to skill audit and approval settings."}
if action == "delete":
if not name:
@@ -271,6 +280,19 @@ def _skill_dump(sk) -> Dict:
# Task management tool
# ---------------------------------------------------------------------------
def _task_date_utc(value):
"""Parse the advertised one-off ISO datetime into the DB's naive UTC."""
from datetime import datetime, timezone
if not isinstance(value, str) or not value.strip():
raise ValueError("scheduled_date is required for a one-off task")
try:
parsed = datetime.fromisoformat(value.strip().replace('Z', '+00:00'))
except ValueError as exc:
raise ValueError("scheduled_date must be an ISO datetime") from exc
if parsed.tzinfo is not None:
parsed = parsed.astimezone(timezone.utc).replace(tzinfo=None)
return parsed
async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
"""Handle manage_tasks tool calls: CRUD on scheduled tasks."""
import uuid as _uuid
@@ -308,13 +330,79 @@ async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
args["scheduled_day"] = days[day]
db = SessionLocal()
try:
def _task_by_id_or_exact_name(required_for: str):
task_id = args.get("task_id")
if task_id:
task = db.query(ScheduledTask).filter(ScheduledTask.id == task_id).first()
if task:
return task, None
try:
_uuid.UUID(str(task_id))
looks_like_uuid = True
except (TypeError, ValueError):
looks_like_uuid = False
if looks_like_uuid:
return None, {"error": f"Task {task_id} not found", "exit_code": 1}
q = db.query(ScheduledTask).filter(ScheduledTask.name == str(task_id).strip())
if owner:
q = q.filter(ScheduledTask.owner == owner)
matches = q.order_by(ScheduledTask.created_at.desc()).all()
if len(matches) == 1:
return matches[0], None
if len(matches) > 1:
return None, {
"error": f"Task name '{task_id}' matched {len(matches)} tasks; use task_id",
"exit_code": 1,
}
return None, {"error": f"Task {task_id} not found", "exit_code": 1}
name = str(args.get("name") or "").strip()
if not name:
return None, {"error": f"task_id is required for {required_for}", "exit_code": 1}
q = db.query(ScheduledTask).filter(ScheduledTask.name == name)
if owner:
q = q.filter(ScheduledTask.owner == owner)
matches = q.order_by(ScheduledTask.created_at.desc()).all()
if not matches:
return None, {"error": f"Task named '{name}' not found", "exit_code": 1}
if len(matches) > 1:
return None, {
"error": f"Task name '{name}' matched {len(matches)} tasks; use task_id",
"exit_code": 1,
}
return matches[0], None
if action == "list":
q = db.query(ScheduledTask)
if owner:
q = q.filter(ScheduledTask.owner == owner)
status_filter = str(args.get("status") or "").strip().lower()
if status_filter:
q = q.filter(ScheduledTask.status == status_filter)
name_filter = str(args.get("name") or "").strip()
query_filter = str(
args.get("query")
or args.get("search")
or args.get("pattern")
or args.get("prompt")
or args.get("match")
or ""
).strip()
if name_filter:
q = q.filter(ScheduledTask.name == name_filter)
elif query_filter:
from sqlalchemy import or_
q = q.filter(or_(
ScheduledTask.name.contains(query_filter),
ScheduledTask.prompt.contains(query_filter),
))
tasks = q.order_by(ScheduledTask.created_at.desc()).all()
if not tasks:
return {"response": "No scheduled tasks found.", "exit_code": 0}
suffix = f" matching '{name_filter or query_filter}'" if (name_filter or query_filter) else ""
return {"response": f"No scheduled tasks found{suffix}.", "exit_code": 0}
lines = [f"Found {len(tasks)} tasks:"]
for idx, t in enumerate(tasks, 1):
@@ -326,6 +414,8 @@ async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
if t.next_run:
bits.append(f"next {t.next_run.isoformat()}Z")
detail = ", ".join(bits)
if t.prompt:
detail = f"{detail}; prompt: {t.prompt}"
lines.append(f"{idx}. {t.name} ({t.id}) — {detail}")
return {"response": "\n".join(lines), "exit_code": 0}
@@ -340,12 +430,17 @@ async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
# Compute next_run for schedule triggers
next_run = None
scheduled_date = None
if trigger_type == "schedule":
schedule = args.get("schedule", "daily")
if schedule == "once":
scheduled_date = _task_date_utc(args.get("scheduled_date"))
next_run = compute_next_run(
schedule, args.get("scheduled_time", "09:00"),
args.get("scheduled_day"),
args.get("scheduled_day"), scheduled_date,
)
if schedule == "once" and next_run is None:
return {"error": "scheduled_date must be in the future", "exit_code": 1}
task_id = str(_uuid.uuid4())
# Guard each fallback with `or`: args.get("prompt", default) returns
@@ -359,9 +454,10 @@ async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
prompt=args.get("prompt"),
task_type=task_type,
action=args.get("action_name"),
schedule=args.get("schedule") if trigger_type == "schedule" else None,
schedule=args.get("schedule", "daily") if trigger_type == "schedule" else None,
scheduled_time=args.get("scheduled_time", "09:00") if trigger_type == "schedule" else None,
scheduled_day=args.get("scheduled_day"),
scheduled_date=scheduled_date,
trigger_type=trigger_type,
trigger_event=args.get("trigger_event"),
trigger_count=args.get("trigger_count"),
@@ -375,12 +471,9 @@ async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
return {"response": f"Created task '{name}' (id: {task_id})", "task_id": task_id, "exit_code": 0}
elif action == "edit":
task_id = args.get("task_id")
if not task_id:
return {"error": "task_id is required for edit", "exit_code": 1}
task = db.query(ScheduledTask).filter(ScheduledTask.id == task_id).first()
if not task:
return {"error": f"Task {task_id} not found", "exit_code": 1}
task, error = _task_by_id_or_exact_name("edit")
if error:
return error
# Strict ownership: the old `task.owner and task.owner != owner`
# skipped the check on an owner-less task (created in no-login mode
# or before the legacy-owner sweep), letting any authenticated user
@@ -415,22 +508,28 @@ async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
setattr(task, field, args[field])
changed.append(field)
schedule_changed = True
if "scheduled_date" in args:
task.scheduled_date = _task_date_utc(args["scheduled_date"])
changed.append("scheduled_date")
schedule_changed = True
if schedule_changed and (task.trigger_type or "schedule") == "schedule":
if task.schedule == "once" and task.scheduled_date is None:
raise ValueError("scheduled_date is required for a one-off task")
task.next_run = compute_next_run(
task.schedule, task.scheduled_time, task.scheduled_day,
task.scheduled_date,
)
if task.schedule == "once" and task.next_run is None:
raise ValueError("scheduled_date must be in the future")
db.commit()
return {"response": f"Updated task '{task.name}': {', '.join(changed)}", "exit_code": 0}
elif action == "delete":
task_id = args.get("task_id")
if not task_id:
return {"error": "task_id is required for delete", "exit_code": 1}
task = db.query(ScheduledTask).filter(ScheduledTask.id == task_id).first()
if not task:
return {"error": f"Task {task_id} not found", "exit_code": 1}
task, error = _task_by_id_or_exact_name("delete")
if error:
return error
if owner and task.owner != owner:
return {"error": "Access denied", "exit_code": 1}
name = task.name
@@ -439,12 +538,9 @@ async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
return {"response": f"Deleted task '{name}'", "exit_code": 0}
elif action in ("pause", "resume"):
task_id = args.get("task_id")
if not task_id:
return {"error": f"task_id is required for {action}", "exit_code": 1}
task = db.query(ScheduledTask).filter(ScheduledTask.id == task_id).first()
if not task:
return {"error": f"Task {task_id} not found", "exit_code": 1}
task, error = _task_by_id_or_exact_name(action)
if error:
return error
if owner and task.owner != owner:
return {"error": "Access denied", "exit_code": 1}
@@ -455,24 +551,24 @@ async def do_manage_tasks(content: str, owner: Optional[str] = None) -> Dict:
if (task.trigger_type or "schedule") == "schedule":
task.next_run = compute_next_run(
task.schedule, task.scheduled_time, task.scheduled_day,
task.scheduled_date,
)
if task.schedule == "once" and task.next_run is None:
raise ValueError("A future scheduled_date is required to resume this one-off task")
db.commit()
return {"response": f"Task '{task.name}' {action}d", "exit_code": 0}
elif action == "run":
task_id = args.get("task_id")
if not task_id:
return {"error": "task_id is required for run", "exit_code": 1}
task = db.query(ScheduledTask).filter(ScheduledTask.id == task_id).first()
if not task:
return {"error": f"Task {task_id} not found", "exit_code": 1}
task, error = _task_by_id_or_exact_name("run")
if error:
return error
if owner and task.owner != owner:
return {"error": "Access denied", "exit_code": 1}
from src.event_bus import get_task_scheduler
scheduler = get_task_scheduler()
if scheduler:
started = await scheduler.run_task_now(task_id)
started = await scheduler.run_task_now(task.id)
if started:
return {"response": f"Task '{task.name}' triggered", "exit_code": 0}
else: