agentwatcher 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
agentwatch/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """agentwatch — DevTools-style overlay for browser-based AI agents.
2
+
3
+ Public API:
4
+ from agentwatch import AgentWatch
5
+
6
+ with AgentWatch() as aw:
7
+ aw.announce(goal="Book a flight from SFO to JFK")
8
+ aw.announce(last_action="Clicked search button", next_action="Type 'SFO'")
9
+ """
10
+
11
+ from .sdk import AgentWatch, announce, start, stop
12
+
13
+ __all__ = ["AgentWatch", "announce", "start", "stop"]
14
+ __version__ = "0.1.0"
agentwatch/sdk.py ADDED
@@ -0,0 +1,130 @@
1
+ """Public SDK — spawn a WS broadcast server in a background thread and
2
+ let agent code push status events to any connected agentwatch overlay."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import asyncio
7
+ import json
8
+ import threading
9
+ from typing import Optional
10
+
11
+ import websockets
12
+
13
+
14
+ class _Broadcaster:
15
+ def __init__(self, host: str, port: int) -> None:
16
+ self.host = host
17
+ self.port = port
18
+ self.clients: set = set()
19
+ self.last_state: dict = {}
20
+ self.loop: Optional[asyncio.AbstractEventLoop] = None
21
+ self.server = None
22
+ self._thread: Optional[threading.Thread] = None
23
+ self._ready = threading.Event()
24
+
25
+ async def _handler(self, ws):
26
+ self.clients.add(ws)
27
+ if self.last_state:
28
+ try:
29
+ await ws.send(json.dumps(self.last_state))
30
+ except Exception:
31
+ pass
32
+ try:
33
+ async for _msg in ws:
34
+ pass
35
+ finally:
36
+ self.clients.discard(ws)
37
+
38
+ async def _serve(self) -> None:
39
+ self.server = await websockets.serve(self._handler, self.host, self.port)
40
+ self._ready.set()
41
+ await self.server.wait_closed()
42
+
43
+ def start(self) -> None:
44
+ def run() -> None:
45
+ self.loop = asyncio.new_event_loop()
46
+ asyncio.set_event_loop(self.loop)
47
+ try:
48
+ self.loop.run_until_complete(self._serve())
49
+ except OSError:
50
+ self._ready.set() # release waiter even if port is taken
51
+ self._thread = threading.Thread(target=run, name="agentwatch-ws", daemon=True)
52
+ self._thread.start()
53
+ self._ready.wait(timeout=2.0)
54
+
55
+ def broadcast(self, payload: dict) -> None:
56
+ if not self.loop or not self.loop.is_running():
57
+ return
58
+ merged = {**self.last_state, **payload}
59
+ self.last_state = merged
60
+ message = json.dumps(merged)
61
+
62
+ async def send_all() -> None:
63
+ dead = []
64
+ for client in list(self.clients):
65
+ try:
66
+ await client.send(message)
67
+ except Exception:
68
+ dead.append(client)
69
+ for d in dead:
70
+ self.clients.discard(d)
71
+ asyncio.run_coroutine_threadsafe(send_all(), self.loop)
72
+
73
+ def stop(self) -> None:
74
+ if self.server and self.loop:
75
+ self.loop.call_soon_threadsafe(self.server.close)
76
+
77
+
78
+ _singleton: Optional[_Broadcaster] = None
79
+
80
+
81
+ def start(host: str = "127.0.0.1", port: int = 8765) -> None:
82
+ """Start the WebSocket broadcaster (idempotent)."""
83
+ global _singleton
84
+ if _singleton is not None:
85
+ return
86
+ _singleton = _Broadcaster(host=host, port=port)
87
+ _singleton.start()
88
+
89
+
90
+ def stop() -> None:
91
+ """Stop the broadcaster, releasing the port."""
92
+ global _singleton
93
+ if _singleton is not None:
94
+ _singleton.stop()
95
+ _singleton = None
96
+
97
+
98
+ def announce(**fields) -> None:
99
+ """Push a status update to every connected agentwatch overlay.
100
+
101
+ Recognized keys (any subset): goal, last_action, next_action, step, error.
102
+ Arbitrary keys also work but won't be rendered by the default overlay.
103
+ """
104
+ if _singleton is None:
105
+ start()
106
+ assert _singleton is not None
107
+ _singleton.broadcast(fields)
108
+
109
+
110
+ class AgentWatch:
111
+ """Context manager wrapper. Use as::
112
+
113
+ with AgentWatch() as aw:
114
+ aw.announce(goal="...")
115
+ aw.announce(last_action="clicked X", next_action="type 'hello'")
116
+ """
117
+
118
+ def __init__(self, host: str = "127.0.0.1", port: int = 8765) -> None:
119
+ self.host = host
120
+ self.port = port
121
+
122
+ def __enter__(self) -> "AgentWatch":
123
+ start(self.host, self.port)
124
+ return self
125
+
126
+ def __exit__(self, *_exc) -> None:
127
+ stop()
128
+
129
+ def announce(self, **fields) -> None:
130
+ announce(**fields)
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentwatcher
3
+ Version: 0.1.0
4
+ Summary: DevTools overlay for browser-based AI agents — see what your agent is doing in real time.
5
+ Project-URL: Homepage, https://github.com/yubinkim444/agentwatch
6
+ Project-URL: Repository, https://github.com/yubinkim444/agentwatch
7
+ Project-URL: Issues, https://github.com/yubinkim444/agentwatch/issues
8
+ Author: yubinkim444
9
+ License-Expression: MIT
10
+ Keywords: ai-agents,browser-agent,browser-use,debugging,developer-tools,playwright,selenium
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Debuggers
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: websockets>=12.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ # agentwatch
25
+
26
+ > **React DevTools, for browser AI agents.**
27
+ > Drop a translucent overlay onto every tab that shows what your agent is
28
+ > trying to do, what it just clicked, and what it's about to do next.
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/agentwatch)](https://pypi.org/project/agentwatch/)
31
+ [![Chrome](https://img.shields.io/badge/Chrome-MV3-4285F4)]()
32
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)]()
33
+
34
+ ---
35
+
36
+ ## Why
37
+
38
+ Browser-use, Playwright agents, computer-use models — they're all **black
39
+ boxes** to the developer watching them. The agent moves the mouse, types
40
+ into a field, the page reloads, then it errors out, and you're left
41
+ guessing what it thought it was doing.
42
+
43
+ `agentwatch` adds a tiny floating overlay to every tab. Your agent code
44
+ pushes one-line status updates ("goal: book a flight", "last action:
45
+ clicked search", "next action: type SFO"), and the overlay shows them
46
+ live, framework-agnostic.
47
+
48
+ The result: bug screenshots that are **self-explanatory**. Demo GIFs that
49
+ viewers actually understand. Pair-debugging sessions where the human can
50
+ intervene the moment the agent goes off-rails.
51
+
52
+ ---
53
+
54
+ ## Architecture
55
+
56
+ ```
57
+ ┌──────────────────────────┐ ws://127.0.0.1:8765 ┌──────────────────────────┐
58
+ │ Your agent (any lang) │ ──────────────────────────────▶ │ Chrome MV3 extension │
59
+ │ agentwatch.announce( │ broadcasts status JSON │ injects floating overlay│
60
+ │ goal=..., last=...) │ │ on every tab │
61
+ └──────────────────────────┘ └──────────────────────────┘
62
+ ```
63
+
64
+ The SDK runs a localhost WebSocket broadcaster in a background thread.
65
+ The extension's content script connects to it on every page load. Each
66
+ `announce()` call updates the overlay in place with a green flash.
67
+
68
+ ---
69
+
70
+ ## Install (2 steps)
71
+
72
+ ### 1. Install the Chrome extension
73
+
74
+ ```bash
75
+ git clone https://github.com/yubinkim444/agentwatch.git
76
+ cd agentwatch
77
+ ```
78
+
79
+ In Chrome / Edge / Brave:
80
+ - Open `chrome://extensions`
81
+ - Toggle **Developer mode** (top-right)
82
+ - Click **Load unpacked**
83
+ - Select the `extension/` folder
84
+
85
+ A small overlay appears in the bottom-right of every tab, showing a
86
+ disconnected (yellow) dot until your agent connects.
87
+
88
+ ### 2. Install the SDK
89
+
90
+ ```bash
91
+ pip install agentwatcher
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Use it
97
+
98
+ ### Python
99
+
100
+ ```python
101
+ from agentwatch import AgentWatch
102
+
103
+ with AgentWatch() as aw:
104
+ aw.announce(goal="Book a one-way flight SFO → JFK for tomorrow")
105
+
106
+ # ...do agent stuff...
107
+
108
+ aw.announce(step=1, last_action="Opened google.com/flights",
109
+ next_action="Click the origin field")
110
+ # ...
111
+ aw.announce(step=2, last_action="Typed 'SFO'",
112
+ next_action="Click the destination field")
113
+ ```
114
+
115
+ Or imperatively, without the context manager:
116
+
117
+ ```python
118
+ import agentwatch
119
+ agentwatch.start()
120
+ agentwatch.announce(goal="...", last_action="...", next_action="...")
121
+ ```
122
+
123
+ ### Any other language
124
+
125
+ Open a WebSocket to `ws://127.0.0.1:8765` and send JSON like:
126
+
127
+ ```json
128
+ {"goal": "Book a flight", "last_action": "Clicked search", "next_action": "Type SFO"}
129
+ ```
130
+
131
+ Every connected tab's overlay updates immediately.
132
+
133
+ ---
134
+
135
+ ## Recognized fields
136
+
137
+ | Field | What the overlay shows |
138
+ |-------|------------------------|
139
+ | `goal` | Top-line objective. |
140
+ | `last_action` | What the agent just did. |
141
+ | `next_action` | What the agent is about to do. |
142
+ | `step` | Optional step counter / index. |
143
+ | `error` | Highlighted in red. The error row only appears when set. |
144
+
145
+ Any other keys are kept in the broadcast payload but not rendered by the
146
+ default overlay — fork the extension if you want to add custom rows.
147
+
148
+ ---
149
+
150
+ ## Pairs nicely with
151
+
152
+ - [`playwright`](https://playwright.dev), [`selenium`](https://selenium.dev),
153
+ [`browser-use`](https://github.com/browser-use/browser-use), any computer-use agent.
154
+ - A screen-recorder. The overlay + your tool together produce demo GIFs
155
+ that explain themselves.
156
+
157
+ ---
158
+
159
+ ## Companion projects
160
+
161
+ - **[mcp-rec](https://github.com/yubinkim444/mcp-rec)** — VCR for MCP servers.
162
+ - **[llm-cache-proxy](https://github.com/yubinkim444/llm-cache-proxy)** — disk cache for OpenAI/Anthropic API calls.
163
+ - **[promptlock](https://github.com/yubinkim444/promptlock)** — lockfile for prompts.
164
+ - **[context-diff](https://github.com/yubinkim444/context-diff)** — git diff for Claude Code context windows.
165
+
166
+ ---
167
+
168
+ ## License
169
+
170
+ MIT © yubinkim444
@@ -0,0 +1,5 @@
1
+ agentwatch/__init__.py,sha256=W3K2UdkiZ8mZCL50ePG47SQTK-bSvSogHxE2VtXgBpA,425
2
+ agentwatch/sdk.py,sha256=9w445iJfRhHO-IY5VYNEgwOBVZ2f7q0E0u6-jCsdNcI,3842
3
+ agentwatcher-0.1.0.dist-info/METADATA,sha256=YLgjeo-aqMNHqxRnGgPKiDnEbifpyV3jviEIYQMD3-A,5755
4
+ agentwatcher-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
5
+ agentwatcher-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any