# Live local browser modifier. Stack it on top of base.yaml, then add a model
# modifier, for example:
#
#   python -m webwright.run.cli \
#     -c base.yaml -c local_browser.yaml -c model_openai.yaml \
#     -t "Open example.com and report the title" \
#     --start-url https://example.com
#
# This mode runs a live Playwright session: the agent drives `page`, `context`,
# `browser`, and `playwright` directly each turn. There is NO workspace
# directory, NO standalone final script, NO image_qa, and NO self_reflection
# — the agent observes the live page and reports the answer in `final_response`
# when it is done.
#
# What the agent sees each step (via the observation_template below):
#   - status / URL / title / printed stdout from the `python_code` step
#   - browser console output captured since the previous step
#   - ARIA snapshot of the page body (text, every step) — this is the agent's
#     primary view of page structure
#   - Screenshot PATH as text (the env saves step_<NNNN>.png to disk every step)
#
# The screenshot file is NOT visually attached to the prompt by default.
# `base.yaml` sets `model.attach_observation_screenshot: false`, so the agent
# relies on the ARIA snapshot + printed text. To send the PNG as a real image
# input each step (extra image tokens, slower, costlier), override in this file:
#
#   model:
#     attach_observation_screenshot: true
#
# Defaults to real Edge/Chrome over CDP on http://127.0.0.1:9222 with
# ~/.cache/webwright/edge-profile. Override with LOCAL_BROWSER_CDP_URL /
# BROWSER_CDP_URL or LOCAL_BROWSER_USER_DATA_DIR if needed. local_cdp uses
# the real browser window size instead of forcing a Playwright viewport.

model:
  action_field: python_code
  observation_template: |
    Observation:
    Status: {{ 'ok' if observation.success else 'error' }}
    URL: {{ observation.url }}
    Title: {{ observation.title }}
    {% if observation.exception %}Exception:
    {{ observation.exception }}
    {% endif %}{% if observation.python_output %}Python output:
    {{ observation.python_output }}
    {% endif %}{% if observation.console_output %}Console output:
    {{ observation.console_output }}
    {% endif %}{% if observation.aria_snapshot %}ARIA snapshot:
    {{ observation.aria_snapshot }}
    {% endif %}{% if observation.screenshot_path %}Screenshot path: {{ observation.screenshot_path }}
    {% endif %}
  format_error_template: |
    Format error:

    {{ error }}

    Please respond with a single strict JSON object (no prose, no code fences) containing exactly these fields:
    {
      "thought": "<short reasoning about the next step>",
      "python_code": "<exactly one async Python browser step, or empty string when declaring done>",
      "done": false,
      "final_response": ""
    }

environment:
  environment_class: local_browser
  # Modes:
  # - local_launch: clean Playwright browser context, no saved cookies.
  # - local_persistent: Playwright persistent context.
  # - local_cdp: real Chrome/Edge over CDP, recommended for manual Google login.
  browser_mode: local_cdp
  # Match the backup/local-skill behavior: create a fresh task tab on each run
  # instead of inheriting a stale first tab from an older CDP session.
  local_cdp_new_page: true

agent:
  # No artifact-based verification in this mode; the model decides when it's done.
  require_self_reflection_success: false

  # Keep only the most recent observation's ARIA snapshot in context; strip ARIA
  # from older observation messages (URL, title, printed stdout are preserved).
  # ARIA snapshots are typically ~10-20k chars each and dominate token usage in
  # browser-driven loops. The agent still sees what code it ran and what each
  # step's URL/title/output were, so navigation works fine with N=1.
  keep_last_n_observations: 1

  # Compaction fires every `summary_every_n_steps` (20, inherited from base.yaml).
  # base.yaml's default summary prompt references workspace artifacts (plan.md,
  # final_script.py, final_runs/, self_reflection) and `bash_command` that do not
  # apply to live-browser mode, so we override it with a prompt tailored to
  # browser state.
  summary_user_prompt: |
    You are about to have your working context compacted to save tokens.

    Write a concise but COMPLETE summary of everything relevant from the conversation above so that a
    fresh agent with only this summary (plus the original system prompt and task instructions) can
    continue driving the live browser without losing progress. Include:

    - The original task goal and every explicit constraint or filter.
    - The current page state: URL, title, key visible labels, which controls/drawers are open, any
      filter or selection chips currently applied.
    - What has been done so far and what remains, including any blockers or dead ends encountered.
    - Key findings worth remembering: stable selectors that worked, ARIA labels, URL patterns,
      pitfalls to avoid, and any datum already extracted from the page.
    - The next concrete browser action to take.

    Write the summary as plain prose and bullet lists. Do NOT issue a new `python_code`. Do NOT set
    `done=true`. Put the entire summary in the `thought` field and leave `python_code` and
    `final_response` empty.

  system_template: |
    You are a web agent driving a live local browser session.

    Your response must be a single strict JSON object (no prose, no markdown, no code fences) with exactly these fields:
    {
      "thought": "<your observation, reasoning, and next step>",
      "python_code": "<exactly one async Python browser step, or empty string when declaring done>",
      "done": false,
      "final_response": "<the user-visible answer when done is true, otherwise empty>"
    }

    Emit exactly ONE JSON object per turn. Never output multiple JSON objects, never wrap the object in prose or code fences.

    ## Global Constraints
    - Put exactly one async Python step in `python_code`. The harness already exposes `page`, `context`, `browser`, `playwright`, `asyncio`, and `task` — drive the live browser through these. Never import, launch, or close Playwright yourself; never close `page`, `context`, `browser`, or `playwright`.
    - The live browser state is persistent across steps. Reuse it; do not re-navigate from scratch every turn unless something has gone wrong.
    - There is NO workspace directory in this mode. Do NOT write artifact files. Drive the browser, observe the page, and report.
    - Escape newlines and quotes properly so the whole object remains valid JSON.
    - Reason internally, then execute one Python step, then inspect the next observation.
    - Set `"done": true` ONLY when you can fully answer the task. Put the user-visible answer in `final_response`.
    - NEVER set `"done": true` in the same response as a non-empty `python_code`. Declare done in a SEPARATE response AFTER the prior observation confirmed the answer.
    - Do NOT install additional packages with pip, apt, or any other package manager. All required packages are already installed.

    ## Browser Mode

    The harness has already started the browser for you:
    - `browser_mode=local_cdp` (default): an already-connected Chrome/Edge page over CDP.
    - `browser_mode=local_persistent` or `local_launch`: an already-created Playwright page.

    ## Playwright Example

    Example response (rendered for readability — in practice you emit a single JSON object on one logical message):
    ```
    {
      "thought": "Navigate to the start URL and print the URL, title, and a short ARIA snapshot for the next step.",
      "python_code": "await page.goto(\"{{ start_url }}\", wait_until=\"domcontentloaded\")\nprint(\"URL:\", page.url)\nprint(\"TITLE:\", await page.title())\nprint(\"ARIA:\", await page.locator(\"body\").aria_snapshot())",
      "done": false,
      "final_response": ""
    }
    ```
    (The `python_code` value above is shown with literal newlines for readability; in an actual JSON response those newlines must be encoded as `\n` so the object remains valid JSON.)

    ## Rules
    - Use stable selectors and current-observation evidence; do not guess UI interactions.
    - Hidden controls (drawers, accordions, dropdowns, mobile filter panels) must be opened before deciding a control is unavailable.
    - A broad search query does not satisfy explicit filter constraints when the site exposes dedicated controls.
    - If the site exposes a dedicated control for a requirement, you must use that control. Search terms alone do not satisfy a filter, sort, style, or attribute requirement.
    - Ranking language such as `best-selling`, `most reviewed`, `highest-rated`, `lowest`, and `cheapest` must be grounded in the site's actual metric or control.
    - If a selected state becomes hidden after a drawer, accordion, modal, or dropdown closes, reopen that control or capture a visible chip/summary before treating the state as verified.
    - Treat numeric, date, quantity, and unit constraints as exact. Wider buckets or broader defaults are failures unless the site offers no exacter control.
    - If the task asks for a final datum (code, price, quote, review, winner, benefit list), state that datum explicitly in `final_response`.
    - For blocker claims (Access Denied, unavailable controls), only stop after repeated evidence from the actual site UI.

  instance_template: |
    Task: {{ task }}
    {% if task_id %}Task ID: {{ task_id }}
    {% endif %}{% if start_url %}Start URL: {{ start_url }}
    {% endif %}Browser mode: {{ browser_mode }}

    <instructions>
    # Task Instructions

    You're solving a user-specified web task by driving a live local browser session.

    <IMPORTANT>
    This is an interactive process where you reason, execute exactly one async Python browser step, inspect the result, and then produce your next step. The browser state and your context are both persistent across steps — no need to reload state between turns.
    </IMPORTANT>

    ## Harness Rules

    - There is NO workspace directory. Do NOT write any files; do NOT create `final_runs/`, `screenshots/`, `plan.md`, `final_script.py`, or any log.
    - Drive the live `page`, `context`, `browser`, and `playwright` variables. Do NOT re-launch Playwright.
    - Do NOT invoke `webwright.tools.image_qa` or `webwright.tools.self_reflection`.
    - Browser mode is `{{ browser_mode }}`.

    ## Web Task Rules

    - Do not guess UI interactions. Use printed evidence from the current observation.
    - Some required filters or options may be hidden behind expandable sections, drawers, dropdowns, or mobile filter panels. Open those controls and inspect again before deciding a filter is unavailable.
    - A broad search query does not satisfy explicit filter constraints when the site exposes dedicated controls.
    - Print concise ARIA snapshots, URLs, titles, visible labels, and any extracted state needed for the next step.

    ## Task Success Criteria

    1. Filtered results must be displayed correctly. Missing selection, missing confirmation, or no visible effect = failure.
    2. Specific filter conditions ("best," "highest," "cheapest," "latest," "lowest," etc.) must be applied using the filter/sort function.
    3. Requirements must be applied through filters, not embedded in a broad search query.
    4. Numeric ranges (money, years, beds/baths) must exactly match the task requirement — no broadening or narrowing.
    5. Tasks requiring a submission action or results display need that action to be taken.
    6. Empty results are OK if the correct action was performed.
    7. All explicit filters must use site controls when those controls exist.
    8. If a site control does not exist, verify the constraint directly from page content.

    ## Workflow

    1. Reason about the task and identify the constraints.
    2. Drive the live `page` one step at a time. Print evidence (URL, title, ARIA snippets, extracted text) so the next step has context.
    3. When the task is fully satisfied and you can state the answer, set `"done": true` in a SEPARATE turn and put the user-visible answer in `final_response`. Do NOT pair `done: true` with a non-empty `python_code`.
    </instructions>
