Skip to content

Commit ecc332e

Browse files
author
Curry
committed
fix: recover CI and runtime contracts
1 parent 890f814 commit ecc332e

45 files changed

Lines changed: 1301 additions & 471 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ jobs:
154154
with:
155155
python-version-file: ".python-version"
156156

157+
- name: Setup uv
158+
uses: astral-sh/setup-uv@v7
159+
157160
- name: Setup pnpm for frontend contract checks
158161
uses: pnpm/action-setup@v4
159162
with:
@@ -277,8 +280,8 @@ jobs:
277280
# rot (dropped columns with no re-add, wrong op order, etc.).
278281
- name: alembic downgrade/upgrade smoke test
279282
run: |
280-
alembic downgrade p3q4r5s6t7u8
281-
alembic upgrade head
283+
uv run alembic downgrade p3q4r5s6t7u8
284+
uv run alembic upgrade head
282285
283286
- name: Native intelligence and event spine PostgreSQL conformance
284287
run: >-

‎agent/Dockerfile‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ ARG PI_VERSION=0.83.0
6161
COPY scripts/patch-opencli.js /tmp/patch-opencli.js
6262
RUN npm install -g @jackwener/opencli@${OPENCLI_VERSION} \
6363
@earendil-works/pi-coding-agent@${PI_VERSION} \
64+
@browserbridge/bbx@${BBX_VERSION} \
6465
&& node /tmp/patch-opencli.js \
6566
&& rm /tmp/patch-opencli.js \
6667
&& rm -rf /root/.npm
@@ -98,6 +99,9 @@ RUN set -eux; \
9899
COPY scripts/resolve-browser-runtime-bundle.mjs /usr/local/bin/resolve-browser-runtime-bundle.mjs
99100
COPY scripts/ensure-script-host.mjs /usr/local/bin/ensure-script-host.mjs
100101
COPY scripts/ensure-violentmonkey-userscripts-access.mjs /usr/local/bin/ensure-violentmonkey-userscripts-access.mjs
102+
COPY --from=browser-bridge-extension /src/browser-bridge/dist/browser-bridge-extension /opt/browser-bridge-extension
103+
COPY --from=browser-bridge-extension /src/browser-bridge/dist/browser-bridge-extension-id /etc/browser-bridge-extension-id
104+
COPY chrome/nginx-cdp.conf.template /etc/nginx/conf.d/cdp.conf.template
101105

102106
# Agent server source plus the runtime adapters it imports at startup.
103107
WORKDIR /app

‎agent/entrypoint.sh‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ if [ -n "${BROWSER_STARTUP_PAGES:-}" ]; then
2828
STARTUP_PAGE_OUTPUT="$(node -e 'const pages=JSON.parse(process.argv[1]); if(!Array.isArray(pages)||pages.length>10||pages.some((item)=>typeof item!=="string"||!/^https?:\/\//.test(item))) process.exit(1); process.stdout.write(pages.join("\n"));' "$BROWSER_STARTUP_PAGES")"
2929
if [ -n "$STARTUP_PAGE_OUTPUT" ]; then mapfile -t STARTUP_PAGES <<< "$STARTUP_PAGE_OUTPUT"; fi
3030
fi
31+
if [ "${#STARTUP_PAGES[@]}" -eq 0 ]; then STARTUP_PAGES=(https://www.doubao.com/chat); fi
3132

3233
if [ "$HAVE_CHROME" = "true" ]; then
3334
export OPENCLI_CDP_ENDPOINT="http://localhost:9222"
@@ -41,12 +42,34 @@ if [ "$HAVE_CHROME" = "true" ]; then
4142
Xvfb :99 -screen 0 1280x900x24 -nolisten tcp &
4243
export DISPLAY=:99
4344
sleep 1
45+
export CHROME_HOSTNAME="${CHROME_HOSTNAME:-${HOSTNAME:-agent-1}}"
46+
envsubst '${CHROME_HOSTNAME}' \
47+
< /etc/nginx/conf.d/cdp.conf.template \
48+
> /etc/nginx/conf.d/cdp.conf
49+
nginx -g 'daemon off;' &
50+
x11vnc -display :99 -nopw -listen 0.0.0.0 -xkb -forever -shared &
51+
websockify --web /usr/share/novnc 6080 localhost:5900 &
52+
53+
BBX_EXTENSION_ID="$(tr -d '\r\n' < /etc/browser-bridge-extension-id)"
54+
if [ -n "$BBX_EXTENSION_ID" ]; then
55+
bbx install "$BBX_EXTENSION_ID" --browser chromium \
56+
|| echo "[agent] WARNING: Browser Bridge native host install failed"
57+
else
58+
echo "[agent] WARNING: Browser Bridge extension ID is missing"
59+
fi
60+
(while true; do
61+
bbx-daemon
62+
echo "[agent] BBX daemon exited, restarting in 1s..."
63+
sleep 1
64+
done) &
65+
echo "[agent] BBX daemon started on ${BBX_TCP_HOST:-127.0.0.1}:${BBX_TCP_PORT:-19826}"
66+
4467
find "$CHROME_PROFILE" -name 'SingletonLock' -o -name 'SingletonCookie' -o -name 'SingletonSocket' 2>/dev/null | xargs rm -f 2>/dev/null || true
4568

4669
DAEMON_JS="$(npm root -g)/@jackwener/opencli/dist/src/daemon.js"
4770
if [ -f "$DAEMON_JS" ]; then
4871
(while true; do
49-
OPENCLI_DAEMON_LISTEN=127.0.0.1 node "$DAEMON_JS"
72+
env -u OPENCLI_DAEMON_PORT OPENCLI_DAEMON_LISTEN=127.0.0.1 node "$DAEMON_JS"
5073
echo "[agent] Bridge daemon exited, restarting in 1s..."
5174
sleep 1
5275
done) &
@@ -57,7 +80,7 @@ if [ "$HAVE_CHROME" = "true" ]; then
5780

5881
start_chrome() {
5982
find "$CHROME_PROFILE" -name 'SingletonLock' -o -name 'SingletonCookie' -o -name 'SingletonSocket' 2>/dev/null | xargs rm -f 2>/dev/null || true
60-
chromium --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --remote-allow-origins='*' --no-sandbox --disable-dev-shm-usage --user-data-dir="$CHROME_PROFILE" "${CHROME_EXTRA_FLAGS[@]}" "$@"
83+
chromium --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --remote-allow-origins='*' --no-sandbox --disable-dev-shm-usage --no-first-run --no-default-browser-check --disable-session-crashed-bubble --user-data-dir="$CHROME_PROFILE" --profile-directory=Default "${CHROME_EXTRA_FLAGS[@]}" --window-size=1280,900 "$@"
6184
}
6285
run_runtime_self_check() {
6386
for _ in $(seq 1 30); do

‎backend/agent_runtime_dispatch.py‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,19 @@ async def invoke_runtime(
208208
if config_errors:
209209
raise HTTPException(status_code=400, detail="; ".join(config_errors))
210210
terminal_event: dict | None = None
211-
async for event in adapter.invoke(task):
212-
if not isinstance(event, dict):
213-
raise HTTPException(status_code=502, detail="runtime produced an invalid event")
214-
if terminal_event is not None:
215-
raise HTTPException(
216-
status_code=502,
217-
detail="runtime produced events after its terminal event",
218-
)
219-
if event.get("type") in {"done", "error"}:
220-
terminal_event = event
211+
try:
212+
async for event in adapter.invoke(task):
213+
if not isinstance(event, dict):
214+
raise HTTPException(status_code=502, detail="runtime produced an invalid event")
215+
if terminal_event is not None:
216+
raise HTTPException(
217+
status_code=502,
218+
detail="runtime produced events after its terminal event",
219+
)
220+
if event.get("type") in {"done", "error"}:
221+
terminal_event = event
222+
except RuntimeInvocationError as exc:
223+
raise HTTPException(status_code=502, detail=str(exc)) from exc
221224
if terminal_event is None:
222225
raise HTTPException(
223226
status_code=502,

‎backend/agent_server.py‎

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,14 @@ def _bundle_declared_runtimes() -> set[str]:
135135
capabilities = manifest.get("capabilities", [])
136136
if not isinstance(capabilities, list):
137137
return set()
138-
return {
139-
capability.get("runtime")
140-
for capability in capabilities
141-
if isinstance(capability, dict) and isinstance(capability.get("runtime"), str)
142-
}
138+
declared: set[str] = set()
139+
for capability in capabilities:
140+
if not isinstance(capability, dict):
141+
continue
142+
runtime = capability.get("runtime", "opentabs")
143+
if isinstance(runtime, str):
144+
declared.add(runtime)
145+
return declared
143146

144147

145148
def _available_agent_runtimes() -> list[str]:
@@ -150,8 +153,28 @@ def _available_agent_runtimes() -> list[str]:
150153
return runtimes
151154

152155

153-
_EDGE_RUNTIME_CONFIG_KEYS = frozenset(
154-
{"binary", "args", "env", "cwd", "project_root", "provider_dir", "usage_file"}
156+
_EDGE_RUNTIME_TASK_CONFIG_KEYS = frozenset(
157+
{
158+
"action",
159+
"agent_id",
160+
"base_delay",
161+
"breaker_threshold",
162+
"chrome",
163+
"input_wait_seconds",
164+
"local",
165+
"max_attempts",
166+
"model",
167+
"pack",
168+
"permission_mode",
169+
"plugin",
170+
"poll_interval",
171+
"provider",
172+
"response_timeout_seconds",
173+
"settle_seconds",
174+
"suggested_wait_seconds",
175+
"tab_id",
176+
"timeout_seconds",
177+
}
155178
)
156179

157180

@@ -525,14 +548,19 @@ async def _register_via_ws(advertise_url: str) -> None:
525548
_proxy = _HTTPS_PROXY or _HTTP_PROXY or None
526549
# Computed once (not per reconnect attempt): runtime availability includes
527550
# fixed-binary compatibility probes and does not change without a restart.
551+
runtimes = _available_agent_runtimes()
552+
runtime_capabilities = available_runtime_capabilities()
553+
for runtime in runtimes:
554+
runtime_capabilities.setdefault(runtime, [])
528555
register_payload = json.dumps(
529556
{
530557
"type": "register",
531558
"agent_url": advertise_url,
532559
"mode": _AGENT_MODE,
533560
"node_type": _AGENT_DEPLOY_TYPE,
534561
"label": _AGENT_LABEL,
535-
"runtimes": _available_agent_runtimes(),
562+
"runtimes": runtimes,
563+
"runtime_capabilities": runtime_capabilities,
536564
"profile_kind": _BROWSER_PROFILE_KIND,
537565
}
538566
)
@@ -687,7 +715,7 @@ async def invoke_runtime_http(
687715
status_code=403,
688716
detail=f"runtime {req.runtime!r} is not declared by the installed bundle",
689717
)
690-
unsafe_keys = sorted(_EDGE_RUNTIME_CONFIG_KEYS.intersection(req.config))
718+
unsafe_keys = sorted(set(req.config) - _EDGE_RUNTIME_TASK_CONFIG_KEYS)
691719
if unsafe_keys:
692720
raise HTTPException(
693721
status_code=400,

‎backend/api/v1/__init__.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
v1_router.include_router(browser_containers.router)
7171
v1_router.include_router(browsers.router)
7272
v1_router.include_router(browsers.runtime_router)
73-
v1_router.include_router(agent_conversations.router)
7473
v1_router.include_router(chat.router)
7574
v1_router.include_router(control.router)
7675
v1_router.include_router(browser_spaces.router)
@@ -111,3 +110,6 @@
111110
v1_router.include_router(delivery_execution_routes.router)
112111
v1_router.include_router(controlled_receiver_routes.router)
113112
v1_router.include_router(research_graph_v2_routes.router)
113+
v1_router.include_router(internal_agent_runs.router)
114+
v1_router.include_router(internal_automations.router)
115+
v1_router.include_router(internal_collaboration.router)

0 commit comments

Comments
 (0)