From 9bdea753ae99533eed31eb8c92362f0d879c04b8 Mon Sep 17 00:00:00 2001 From: pewdiepie-archdaemon Date: Tue, 13 Oct 2026 13:59:04 +0000 Subject: [PATCH] fix(harness): ship coherent vendor harness cluster Rebuild PR C to ship the vendor/photocraft harness as a coherent unit (vendor implementation + vendor tests) instead of a per-file ahead/behind mix that produced broken imports and 6+ test failures. - Flip 36 harness impl/route/test files back to vendor (the per-file line-set policy misclassified user-rewritten files as 'behind'). - Flip core/models.py + src/tool_approval_scopes.py to vendor: vendor's harness (authority.py, turn_contract.py, clean_agent_preview.py) depends on vendor's older approval-scope API; upstream added CHAT_SESSION_APPROVAL_SIGNATURE_FIELD that vendor's core/models.py lacks. - Add src/shutdown.py (new vendor infra) required by routes/chat_routes.py. - Keep src/agent_tools/__init__.py at upstream (editor_tools registration lives in PR B). Upstream-only security divergence preserved in C_upstream_delta.patch and flagged for integration-time reconciliation. Co-Authored-By: Claude Opus 5.5 --- core/models.py | 10 - routes/assistant_routes.py | 9 +- routes/chat_helpers.py | 12 +- routes/cookbook_routes.py | 18 +- routes/history/history_routes.py | 12 +- routes/search/search_routes.py | 21 +- routes/session_routes.py | 65 +- routes/skills_routes.py | 37 +- routes/task/task_routes.py | 32 +- services/memory/memory_extractor.py | 11 +- src/agent_loop.py | 1191 ++++------------- src/agent_runtime/resources.py | 15 +- src/agent_tools/__init__.py | 5 - src/agent_tools/filesystem_tools.py | 686 ++-------- src/agent_tools/subprocess_tools.py | 23 +- src/app_initializer.py | 33 +- src/builtin_actions.py | 41 +- src/llm_core.py | 16 +- src/readiness.py | 11 +- src/shutdown.py | 34 + src/tool_approval_scopes.py | 125 -- src/tool_capabilities.py | 94 +- src/tool_execution.py | 347 +---- src/tool_parsing.py | 188 +-- src/tool_security.py | 15 +- src/turn_contract.py | 415 ++---- src/url_safety.py | 57 +- tests/test_agent_rounds_exhausted.py | 3 +- tests/test_aux_llm_owner_scope.py | 5 +- .../test_calendar_reminder_minutes_parsing.py | 46 +- tests/test_client_tool_routing.py | 23 +- tests/test_code_nav_tools.py | 22 +- tests/test_effect_resource_bindings.py | 10 +- tests/test_env_reference.py | 48 +- tests/test_execution_bridge.py | 75 +- tests/test_security_regressions.py | 8 +- tests/test_session_endpoint_owner_scope.py | 55 +- tests/test_tool_path_confinement.py | 16 +- tests/test_url_safety.py | 46 +- 39 files changed, 804 insertions(+), 3076 deletions(-) create mode 100644 src/shutdown.py diff --git a/core/models.py b/core/models.py index f1e1d89d5..a03e391fc 100644 --- a/core/models.py +++ b/core/models.py @@ -11,8 +11,6 @@ from typing import Dict, List, Any, Optional, TYPE_CHECKING from src.tool_approval_scopes import ( CHAT_SESSION_APPROVAL_CONTEXT_MARKER, CHAT_SESSION_APPROVAL_DECISION, - CHAT_SESSION_APPROVAL_SIGNATURE_FIELD, - verify_chat_session_grant, ) if TYPE_CHECKING: @@ -62,14 +60,6 @@ def _history_grants_chat_session_approval( ask_user.get("kind") == "tool_approval" and ask_user.get("resolved") == CHAT_SESSION_APPROVAL_DECISION and str(ask_user.get("session_id") or "") == expected_session - # Shape proves nothing here: routes that accept a - # caller-supplied metadata blob write into this same history. - and verify_chat_session_grant( - ask_user.get(CHAT_SESSION_APPROVAL_SIGNATURE_FIELD), - expected_session, - ask_user.get("approval_id"), - CHAT_SESSION_APPROVAL_DECISION, - ) ): return True return False diff --git a/routes/assistant_routes.py b/routes/assistant_routes.py index d31376dc3..f16f016e9 100644 --- a/routes/assistant_routes.py +++ b/routes/assistant_routes.py @@ -16,7 +16,6 @@ from pydantic import BaseModel from core.database import SessionLocal, CrewMember, ScheduledTask from src.auth_helpers import get_current_user -from src.endpoint_resolver import resolve_owner_registered_endpoint_url from src.owner_identity import REQUEST_SENTINEL_OWNERS from src.task_scheduler import compute_next_run @@ -179,13 +178,7 @@ def setup_assistant_routes(task_scheduler) -> APIRouter: if payload.model is not None: crew_db.model = payload.model or None if payload.endpoint_url is not None: - try: - crew_db.endpoint_url = ( - resolve_owner_registered_endpoint_url(db, payload.endpoint_url, owner) - if payload.endpoint_url else None - ) - except ValueError as exc: - raise HTTPException(400, str(exc)) from exc + crew_db.endpoint_url = payload.endpoint_url or None if payload.timezone is not None: crew_db.timezone = payload.timezone or None diff --git a/routes/chat_helpers.py b/routes/chat_helpers.py index 472128eaf..eb5fcf489 100644 --- a/routes/chat_helpers.py +++ b/routes/chat_helpers.py @@ -98,9 +98,7 @@ def clean_repeated_assistant_content(text: object) -> str: "", value, ).strip() - # No leading `\s*`: .strip() removes that whitespace anyway, and scanning - # it from every offset of a long whitespace run was quadratic (ReDoS). - value = re.sub(r"(?is)\s*$", "", value).strip() + value = re.sub(r"(?is)\s*\s*$", "", value).strip() return value _CASUAL_OPENING_RE = re.compile( @@ -1326,17 +1324,13 @@ def _normalize_thinking(text: str) -> str: # Handle garbled tags: reasoning text followed by as separator # e.g. "The user said...I should respond.\nHey! What's up?" - # Linear form of `^([\s\S]+?)\n*\s*([\s\S]*?)(?:)?\s*$`: - # the lookbehind stops the lazy prefix re-scanning a newline run from every - # offset, and the optional trailing closer is dropped after the match - # instead of being retried at every body offset (both were quadratic). garbled = re.match( - r'^([\s\S]+?)(?\s*([\s\S]*)$', + r'^([\s\S]+?)\n*\s*([\s\S]*?)(?:)?\s*$', text, re.IGNORECASE ) if garbled: before = garbled.group(1).strip() - after = re.sub(r'$', '', garbled.group(2).rstrip(), flags=re.IGNORECASE).strip() + after = garbled.group(2).strip() # Only treat as garbled if the part before looks like reasoning reasoning_starts = ( 'The user ', 'I need ', 'I should ', 'I will ', diff --git a/routes/cookbook_routes.py b/routes/cookbook_routes.py index 02b419894..e09bbbe37 100644 --- a/routes/cookbook_routes.py +++ b/routes/cookbook_routes.py @@ -701,17 +701,13 @@ def setup_cookbook_routes() -> APIRouter: return cmd repo_id = "cyankiwi/MiniMax-M3-AWQ-INT4" - hf_home = Path(os.environ.get("HF_HOME", str(Path.home() / ".cache" / "huggingface"))) - hf_cache = Path(os.environ.get("HUGGINGFACE_HUB_CACHE", str(hf_home / "hub"))) - snapshot_root = hf_cache / "models--cyankiwi--MiniMax-M3-AWQ-INT4" / "snapshots" - if body[serve_i + 1] == repo_id and snapshot_root.is_dir(): - installed_snapshots = sorted( - (p for p in snapshot_root.iterdir() if p.is_dir()), - key=lambda p: p.stat().st_mtime, - reverse=True, - ) - if installed_snapshots: - body[serve_i + 1] = str(installed_snapshots[0]) + snapshot = ( + "/home/pewds/.cache/huggingface/hub/" + "models--cyankiwi--MiniMax-M3-AWQ-INT4/" + "snapshots/4082acbbec1236d21828d55b6bb0fe02ade4ab5b" + ) + if body[serve_i + 1] == repo_id: + body[serve_i + 1] = snapshot def add_env(key: str, value: str) -> None: if not any(p.startswith(f"{key}=") for p in env_parts): diff --git a/routes/history/history_routes.py b/routes/history/history_routes.py index 890927c53..64ccb3dea 100644 --- a/routes/history/history_routes.py +++ b/routes/history/history_routes.py @@ -7,14 +7,13 @@ import logging import re from typing import Dict, Any, Optional -from fastapi import APIRouter, Request, HTTPException, Depends +from fastapi import APIRouter, Request, HTTPException from core.models import ChatMessage from core.database import SessionLocal, ChatMessage as DbChatMessage, Session as DbSession -from src.auth_helpers import effective_user, require_chat_api_token_scope +from src.auth_helpers import effective_user from src.topic_analyzer import analyze_topics from src.upload_handler import reserve_message_upload_references -from src.tool_approval_scopes import sanitize_client_message_metadata from routes.session_routes import ( _message_role, _message_text, @@ -238,10 +237,7 @@ def _keep_count_before_message(db_messages, before_msg_id: str | None) -> int | def setup_history_routes(session_manager, upload_handler=None) -> APIRouter: - router = APIRouter( - tags=["history"], - dependencies=[Depends(require_chat_api_token_scope)], - ) + router = APIRouter(tags=["history"]) def _reserve_message_uploads( request: Request, @@ -436,7 +432,7 @@ def setup_history_routes(session_manager, upload_handler=None) -> APIRouter: content = body.get("content", "") if not content: raise HTTPException(400, "content is required") - metadata = sanitize_client_message_metadata(body.get("metadata")) + metadata = body.get("metadata") _reserve_message_uploads(request, content, metadata) msg = ChatMessage(role=role, content=content, metadata=metadata) session_manager.add_message(session_id, msg) diff --git a/routes/search/search_routes.py b/routes/search/search_routes.py index a63607649..5aafa03cb 100644 --- a/routes/search/search_routes.py +++ b/routes/search/search_routes.py @@ -43,22 +43,11 @@ def setup_search_routes(config) -> APIRouter: router = APIRouter(tags=["search"]) @router.get("/search/web", response_class=HTMLResponse) - async def web_search_page(request: Request, q: str = Query("", min_length=0)) -> HTMLResponse: + async def web_search_page(q: str = Query("", min_length=0)) -> HTMLResponse: """Browser-facing search results page for clickable agent web_search rows.""" - # The site CSP allows inline script only with the per-request nonce - # (SecurityHeadersMiddleware); without it this page's script never ran. - nonce = html.escape(getattr(request.state, "csp_nonce", "") or "", quote=True) safe_q = str(q or "").strip() title = html.escape(safe_q or "Web search") - # json.dumps leaves `<`, `>` and `&` alone, so q=