mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-16 03:42:20 +02:00
feat(harness): authority, turn contract, tools, clean_agent_preview
Chat/agent harness work: request-authority model, turn contract, tool
policy/parsing/execution, clean_agent_preview streaming/thinking, ask_user
persistence, session probes, and scheduler.
Source: vendor/photocraft (ad1cff44) harness files, 3-way merged onto
upstream/dev (a8c147b2). Harness core files were independently developed
in both lineages; resolution policy documented in pr_work/C_conflicts.md
(vendor kept where ahead, upstream kept where vendor is behind; full delta
preserved in pr_work/C_upstream_delta.patch).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a8c147b238
commit
364229f2dc
+37
-37
@@ -1185,6 +1185,9 @@ def _fixture_email_record(row: dict, uid_num: int, owner: str) -> dict:
|
||||
"subject": subject,
|
||||
"from": sender_name or sender_addr or sender,
|
||||
"from_address": sender_addr,
|
||||
"to": str(row.get("to") or headers.get("To") or ""),
|
||||
"cc": str(row.get("cc") or headers.get("Cc") or ""),
|
||||
"references": str(row.get("references") or headers.get("References") or ""),
|
||||
"date": date_str,
|
||||
"date_epoch": date_epoch,
|
||||
"summary": body[:240],
|
||||
@@ -1198,7 +1201,7 @@ def _fixture_email_record(row: dict, uid_num: int, owner: str) -> dict:
|
||||
"folder": folder,
|
||||
"is_read": bool(row.get("read")),
|
||||
"is_done": bool(row.get("done") or row.get("answered")),
|
||||
"is_favorite": bool(row.get("favorite") or row.get("flagged") or row.get("starred")),
|
||||
"is_favorite": bool(row.get("favorite", row.get("flagged") or row.get("starred"))),
|
||||
"spam_label": str(row.get("spam_label") or ""),
|
||||
"spam_score": int(row.get("spam_score") or 0),
|
||||
"list_unsubscribe": str(row.get("list_unsubscribe") or headers.get("List-Unsubscribe") or ""),
|
||||
@@ -1522,15 +1525,17 @@ def _fixture_read_email(uid=None, message_id=None, folder="INBOX", account=None)
|
||||
return None
|
||||
if not _fixture_owner_has_rows(_current_owner()):
|
||||
return None
|
||||
for item in _fixture_email_rows(_current_owner()):
|
||||
if not _fixture_row_matches_account(item, account):
|
||||
continue
|
||||
if not _fixture_folder_matches(item.get("folder"), folder):
|
||||
continue
|
||||
if uid and str(item.get("uid")) == str(uid):
|
||||
return item
|
||||
if message_id and str(item.get("message_id")) == str(message_id):
|
||||
return item
|
||||
if not uid and not message_id:
|
||||
return {"error": "Email UID or Message-ID is required"}
|
||||
matches = [item for item in _fixture_email_rows(_current_owner())
|
||||
if _fixture_row_matches_account(item, account)
|
||||
and _fixture_folder_matches(item.get("folder"), folder)
|
||||
and (not uid or str(item.get("uid")) == str(uid))
|
||||
and (not message_id or str(item.get("message_id")) == str(message_id))]
|
||||
if len(matches) > 1:
|
||||
return {"error": "Email identity is ambiguous; specify its account and folder"}
|
||||
if matches:
|
||||
return matches[0]
|
||||
return {"error": f"Email not found with UID/Message-ID: {uid or message_id}"}
|
||||
|
||||
|
||||
@@ -1557,6 +1562,7 @@ def _fixture_update_email(uid=None, source_folder="INBOX", account=None, **updat
|
||||
if not isinstance(rows, list):
|
||||
return False
|
||||
owner = _current_owner()
|
||||
matches = []
|
||||
for index, row in enumerate(rows, start=1):
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
@@ -1568,14 +1574,19 @@ def _fixture_update_email(uid=None, source_folder="INBOX", account=None, **updat
|
||||
continue
|
||||
if not _fixture_folder_matches(row.get("folder") or "INBOX", source_folder):
|
||||
continue
|
||||
for key, value in updates.items():
|
||||
if value is None:
|
||||
row.pop(key, None)
|
||||
else:
|
||||
row[key] = value
|
||||
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||||
return True
|
||||
return False
|
||||
if not _fixture_row_matches_account(_fixture_email_record(row, index, owner or row_owner), account):
|
||||
continue
|
||||
matches.append(row)
|
||||
if len(matches) != 1:
|
||||
return False
|
||||
row = matches[0]
|
||||
for key, value in updates.items():
|
||||
if value is None:
|
||||
row.pop(key, None)
|
||||
else:
|
||||
row[key] = value
|
||||
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||||
return True
|
||||
|
||||
|
||||
# ── Tool implementations ──
|
||||
@@ -2925,7 +2936,9 @@ def _create_email_draft_document(
|
||||
|
||||
def _draft_reply_to_email(uid, body, folder="INBOX", reply_all=False, account=None, title=None):
|
||||
"""Create a threaded Odysseus reply draft document. Does not send."""
|
||||
fixture = _fixture_email_action_target(uid=uid, folder=folder, account=account)
|
||||
fixture = _fixture_read_email(uid=uid, folder=folder, account=account)
|
||||
if _fixture_email_enabled() and (fixture is None or fixture.get("error")):
|
||||
return fixture or {"error": "No email fixture is available for the authenticated owner"}
|
||||
if fixture is not None:
|
||||
sender = str(fixture.get("from_address") or fixture.get("from") or "")
|
||||
_, sender_addr = email.utils.parseaddr(sender)
|
||||
@@ -3178,7 +3191,9 @@ def _reply_to_email(uid, body, folder="INBOX", reply_all=False, account=None):
|
||||
|
||||
def _set_flag(uid, folder, flag, add=True, account=None):
|
||||
"""Add or remove an IMAP flag (e.g. \\Seen, \\Answered, \\Deleted)."""
|
||||
if _fixture_email_action_target(uid=uid, folder=folder, account=account) is not None:
|
||||
if _fixture_email_enabled():
|
||||
if _fixture_email_action_target(uid=uid, folder=folder, account=account) is None:
|
||||
return False
|
||||
if flag == "\\Seen":
|
||||
return _fixture_update_email(uid=uid, source_folder=folder, account=account, read=bool(add))
|
||||
if flag == "\\Answered":
|
||||
@@ -3504,21 +3519,6 @@ def _block_sender(sender=None, uids=None, folder="INBOX", account=None, reason="
|
||||
}
|
||||
|
||||
|
||||
def _attachment_dir(folder, uid, account_id) -> Path:
|
||||
"""Owner/account-scoped extraction directory (see src.mail_attachment_paths).
|
||||
|
||||
`folder` comes from tool arguments and the IMAP server's own mailbox names
|
||||
(`/` hierarchies, absolute or `..` segments), so it must not become a path;
|
||||
the old `MAIL_ATTACHMENTS_DIR/{folder}_{uid}` wrote outside the root and
|
||||
shared one directory between every owner's "INBOX 42".
|
||||
"""
|
||||
from src.mail_attachment_paths import attachment_scope_dir
|
||||
|
||||
return attachment_scope_dir(
|
||||
MAIL_ATTACHMENTS_DIR, folder, uid, owner=_current_owner(), account_id=account_id,
|
||||
)
|
||||
|
||||
|
||||
def _download_attachment(uid, index, folder="INBOX", account=None):
|
||||
"""Extract a specific attachment to disk and return its local path."""
|
||||
fixture = _fixture_attachment_source(uid, index, folder=folder, account=account)
|
||||
@@ -3527,7 +3527,7 @@ def _download_attachment(uid, index, folder="INBOX", account=None):
|
||||
filename = str(att.get("filename") or f"attachment-{index}.txt")
|
||||
safe_name = re.sub(r"[^\w\s\-.]", "_", filename).strip() or f"attachment-{index}.txt"
|
||||
content = str(att.get("content") or "")
|
||||
target_dir = _attachment_dir(folder, uid, _row.get("account_id") or account)
|
||||
target_dir = Path(MAIL_ATTACHMENTS_DIR) / re.sub(r"[^A-Za-z0-9._-]", "_", f"{folder}_{uid}")
|
||||
path = target_dir / safe_name
|
||||
size = len(content.encode("utf-8"))
|
||||
try:
|
||||
@@ -3560,7 +3560,7 @@ def _download_attachment(uid, index, folder="INBOX", account=None):
|
||||
raw = msg_data[0][1]
|
||||
msg = email.message_from_bytes(raw)
|
||||
|
||||
target_dir = _attachment_dir(folder, uid, _load_config(account).get("account_id") or account)
|
||||
target_dir = Path(MAIL_ATTACHMENTS_DIR) / f"{folder}_{uid}"
|
||||
filepath = _extract_attachment_to_disk(msg, index, target_dir)
|
||||
if not filepath:
|
||||
return {"error": f"Attachment index {index} not found"}
|
||||
|
||||
Reference in New Issue
Block a user