Skip to content

Commit a58db3c

Browse files
author
SIN-Agent
committed
fix: add missing transport + behavior modules, create BRAIN.md
Extension v5.0.0 was broken — missing 4 files caused 'Service worker registration failed. Status code: 3'. Created: - src/transports/ws.js: WebSocket transport with 60s keep-alive ping - src/transports/external.js: external messaging stub - src/transports/native.js: native messaging stub - src/tools/behavior.js: behavior tools stub - BRAIN.md: architecture, setup, curl commands, known errors WebSocket now sends {"method":"ping"} every 60s to prevent bridge server from marking extension as disconnected (>90s threshold). Also updated .serena/project.yml
1 parent 186e75e commit a58db3c

6 files changed

Lines changed: 308 additions & 26 deletions

File tree

.serena/project.yml

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ project_name: "OpenSIN-Bridge-issue-27"
66
# al bash clojure cpp csharp
77
# csharp_omnisharp dart elixir elm erlang
88
# fortran fsharp go groovy haskell
9-
# java julia kotlin lua markdown
9+
# haxe java julia kotlin lua
10+
# markdown
1011
# matlab nix pascal perl php
1112
# php_phpactor powershell python python_jedi r
1213
# rego ruby ruby_solargraph rust scala
@@ -57,48 +58,52 @@ ignored_paths: []
5758
# Added on 2025-04-18
5859
read_only: false
5960

60-
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
61+
# list of tool names to exclude.
62+
# This extends the existing exclusions (e.g. from the global configuration)
63+
#
6164
# Below is the complete list of tools for convenience.
6265
# To make sure you have the latest list of tools, and to view their descriptions,
6366
# execute `uv run scripts/print_tool_overview.py`.
6467
#
65-
# * `activate_project`: Activates a project by name.
68+
# * `activate_project`: Activates a project based on the project name or path.
6669
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
6770
# * `create_text_file`: Creates/overwrites a file in the project directory.
68-
# * `delete_lines`: Deletes a range of lines within a file.
69-
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
71+
# * `delete_memory`: Delete a memory file. Should only happen if a user asks for it explicitly,
72+
# for example by saying that the information retrieved from a memory file is no longer correct
73+
# or no longer relevant for the project.
74+
# * `edit_memory`: Replaces content matching a regular expression in a memory.
7075
# * `execute_shell_command`: Executes a shell command.
71-
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
72-
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
73-
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
76+
# * `find_file`: Finds files in the given relative paths
77+
# * `find_referencing_symbols`: Finds symbols that reference the given symbol using the language server backend
78+
# * `find_symbol`: Performs a global (or local) search using the language server backend.
7479
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
7580
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
76-
# * `initial_instructions`: Gets the initial instructions for the current project.
77-
# Should only be used in settings where the system prompt cannot be set,
78-
# e.g. in clients you have no control over, like Claude Desktop.
81+
# * `initial_instructions`: Provides instructions Serena usage (i.e. the 'Serena Instructions Manual')
82+
# for clients that do not read the initial instructions when the MCP server is connected.
7983
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
80-
# * `insert_at_line`: Inserts content at a given line in a file.
8184
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
8285
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
83-
# * `list_memories`: Lists memories in Serena's project-specific memory store.
86+
# * `list_memories`: List available memories. Any memory can be read using the `read_memory` tool.
8487
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
85-
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
8688
# * `read_file`: Reads a file within the project directory.
87-
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
88-
# * `remove_project`: Removes a project from the Serena configuration.
89-
# * `replace_lines`: Replaces a range of lines within a file with new content.
90-
# * `replace_symbol_body`: Replaces the full definition of a symbol.
91-
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
89+
# * `read_memory`: Read the content of a memory file. This tool should only be used if the information
90+
# is relevant to the current task. You can infer whether the information
91+
# is relevant from the memory file name.
92+
# You should not read the same memory file multiple times in the same conversation.
93+
# * `rename_memory`: Renames or moves a memory. Moving between project and global scope is supported
94+
# (e.g., renaming "global/foo" to "bar" moves it from global to project scope).
95+
# * `rename_symbol`: Renames a symbol throughout the codebase using language server refactoring capabilities.
96+
# For JB, we use a separate tool.
97+
# * `replace_content`: Replaces content in a file (optionally using regular expressions).
98+
# * `replace_symbol_body`: Replaces the full definition of a symbol using the language server backend.
99+
# * `safe_delete_symbol`:
92100
# * `search_for_pattern`: Performs a search for a pattern in the project.
93-
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
94-
# * `switch_modes`: Activates modes by providing a list of their names
95-
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
96-
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
97-
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
98-
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
101+
# * `write_memory`: Write some information (utf-8-encoded) about this project that can be useful for future tasks to a memory in md format.
102+
# The memory name should be meaningful.
99103
excluded_tools: []
100104

101-
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
105+
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
106+
# This extends the existing inclusions (e.g. from the global configuration).
102107
included_optional_tools: []
103108

104109
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
@@ -133,3 +138,17 @@ symbol_info_budget:
133138
# list of regex patterns which, when matched, mark a memory entry as read‑only.
134139
# Extends the list from the global configuration, merging the two lists.
135140
read_only_memory_patterns: []
141+
142+
# list of regex patterns for memories to completely ignore.
143+
# Matching memories will not appear in list_memories or activate_project output
144+
# and cannot be accessed via read_memory or write_memory.
145+
# To access ignored memory files, use the read_file tool on the raw file path.
146+
# Extends the list from the global configuration, merging the two lists.
147+
# Example: ["_archive/.*", "_episodes/.*"]
148+
ignored_memory_patterns: []
149+
150+
# advanced configuration option allowing to configure language server-specific options.
151+
# Maps the language key to the options.
152+
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
153+
# No documentation on options means no options are available.
154+
ls_specific_settings: {}

BRAIN.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# BRAIN.md — OpenSIN Bridge Knowledge Base
2+
3+
> **Stand: 28. April 2026, 21:15 Uhr**
4+
> Extension v5.0.0, Server v2.9.2
5+
6+
---
7+
8+
## 1. Architektur
9+
10+
```
11+
Chrome Extension (v5.0.0) ──WebSocket──→ Bridge Server (v2.9.2, Port 7777) ──HTTP──→ Worker
12+
│ │
13+
src/background/ server.js
14+
service-worker.js /mcp (HTTP)
15+
│ /health
16+
transports/ws.js ──→ ws://localhost:7777/extension
17+
```
18+
19+
---
20+
21+
## 2. Extension laden (MV3)
22+
23+
1. Chrome → `chrome://extensions/`
24+
2. Developer Mode aktivieren
25+
3. "Entpackte Erweiterung laden"
26+
4. Ordner: `/Users/jeremy/dev/OpenSIN-Bridge/extension/`
27+
28+
**Fehler "Service worker registration failed. Status code: 3":**
29+
→ 4 Dateien fehlen standardmäßig und müssen ERSTELLT werden:
30+
31+
```
32+
src/transports/ws.js ← WebSocket Transport (mit Keep-Alive-Ping alle 60s)
33+
src/transports/external.js ← External Messaging Stub
34+
src/transports/native.js ← Native Messaging Stub
35+
src/tools/behavior.js ← Behavior Tools Stub
36+
```
37+
38+
---
39+
40+
## 3. Server starten
41+
42+
```bash
43+
cd /Users/jeremy/dev/OpenSIN-Bridge
44+
PORT=7777 node server.js
45+
```
46+
47+
Gesundheitscheck:
48+
```bash
49+
curl http://localhost:7777/health
50+
# → {"status":"ok","extensionConnected":true,"toolsCount":91}
51+
```
52+
53+
---
54+
55+
## 4. Bridge per curl steuern (1 Command = 1 Aktion)
56+
57+
```bash
58+
# Navigieren
59+
curl -s http://localhost:7777/mcp -X POST -H "Content-Type: application/json" \
60+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"navigate","arguments":{"url":"https://heypiggy.com"}},"id":1}'
61+
62+
# Screenshot
63+
curl -s http://localhost:7777/mcp -X POST -H "Content-Type: application/json" \
64+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"screenshot"},"id":2}'
65+
66+
# Klick
67+
curl -s http://localhost:7777/mcp -X POST -H "Content-Type: application/json" \
68+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"click_element","arguments":{"selector":".survey-item"}},"id":3}'
69+
70+
# Text eingeben
71+
curl -s http://localhost:7777/mcp -X POST -H "Content-Type: application/json" \
72+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"type_text","arguments":{"selector":"input","text":"Hello"}},"id":4}'
73+
74+
# JavaScript ausführen
75+
curl -s http://localhost:7777/mcp -X POST -H "Content-Type: application/json" \
76+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"execute_script","arguments":{"script":"document.title"}},"id":5}'
77+
```
78+
79+
---
80+
81+
## 5. Bekannte Fehler & Fixes
82+
83+
| Fehler | Ursache | Fix |
84+
|--------|---------|-----|
85+
| Service worker Status code: 3 | Transport-Dateien fehlen | `ws.js`, `external.js`, `native.js`, `behavior.js` erstellen |
86+
| "Extension disconnected" trotz `extensionConnected:true` | Keep-Alive-Ping fehlt (>90s) | ws.js sendet alle 60s `{"method":"ping"}` |
87+
| WebSocket connected, kein Ping | `initLifecycle` hat keinen WebSocket-Ping — nur SW-Wakeup | ws.js `onopen``setInterval` Ping |
88+
| HF Spaces Bridge zeigt `extensionConnected: null` | Extension verbindet zu localhost, nicht HF Spaces | Extension Config auf `wss://openjerro-...` ändern ODER lokalen Server starten |
89+
90+
---
91+
92+
## 6. Environment
93+
94+
```bash
95+
# Lokaler Bridge Server
96+
PORT=7777 node server.js # HTTP auf :7777, WS auf :7777/extension
97+
98+
# ODER: HF Spaces (Cloud)
99+
BRIDGE_MCP_URL=https://openjerro-opensin-bridge-mcp.hf.space/mcp
100+
```
101+
102+
**Default der Extension:** `ws://localhost:7777/extension` (config.js Zeile 39)
103+
104+
---
105+
106+
## 7. Heute erstellte/gefixte Dateien
107+
108+
```
109+
✅ src/transports/ws.js — WebSocket + 60s Keep-Alive-Ping
110+
✅ src/transports/external.js — External messaging stub
111+
✅ src/transports/native.js — Native messaging stub
112+
✅ src/tools/behavior.js — Behavior tools stub
113+
✅ src/core/lifecycle.js — behavior-store-Import auskommentiert
114+
```
115+
116+
---
117+
118+
## 8. ABHÄNGIGKEIT: Heypiggy Worker
119+
120+
Der `A2A-SIN-Worker-heypiggy` nutzt die Bridge via `BRIDGE_MCP_URL`.
121+
122+
**Wichtig:** Ohne Extension läuft GAR NICHTS. Der Worker macht HTTP-Calls an die Bridge, die Bridge leitet per WebSocket an die Extension, die Extension führt in Chrome aus.
123+
124+
```
125+
Worker → curl http://localhost:7777/mcp → Bridge Server → WebSocket → Extension → Chrome
126+
```
127+
128+
---
129+
130+
*Letzte Aktualisierung: 28. April 2026*

extension/src/tools/behavior.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Behavior tools stub — human-like interaction patterns.
3+
*/
4+
export function register(router) {
5+
router.register("behavior.delay", async () => ({ ok: true }));
6+
router.register("behavior.scroll", async () => ({ ok: true }));
7+
router.register("behavior.hover", async () => ({ ok: true }));
8+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/** External runtime messaging transport — stub. */
2+
export function attach({ router }) {
3+
chrome.runtime.onMessageExternal.addListener((msg, sender, sendResponse) => {
4+
try {
5+
const result = router(msg);
6+
sendResponse(result);
7+
} catch (e) {
8+
sendResponse({ error: e.message });
9+
}
10+
return true;
11+
});
12+
}

extension/src/transports/native.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** Native messaging transport — stub (no host configured). */
2+
export function create({ router, clientId }) {
3+
return {
4+
send: () => {},
5+
close: () => {},
6+
isConnected: () => false,
7+
};
8+
}

extension/src/transports/ws.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* WebSocket transport — connects to local bridge server.
3+
*
4+
* Opens a WebSocket to ws://localhost:7777/extension and relays
5+
* JSON-RPC messages between the Chrome extension and the bridge server.
6+
*/
7+
import { CONFIG } from "../core/config.js";
8+
import { createLogger } from "../core/logger.js";
9+
10+
const log = createLogger("ws");
11+
12+
export function create({ router, clientId }) {
13+
const url = CONFIG.wsUrl || "ws://localhost:7777/extension";
14+
log.info(`Connecting to ${url} (client: ${clientId})`);
15+
16+
let ws = null;
17+
let reconnectTimer = null;
18+
let keepAliveTimer = null;
19+
let reconnectAttempts = 0;
20+
21+
function connect() {
22+
try {
23+
ws = new WebSocket(url);
24+
25+
ws.onopen = () => {
26+
log.info("WebSocket connected");
27+
reconnectAttempts = 0;
28+
// Send handshake
29+
ws.send(JSON.stringify({
30+
jsonrpc: "2.0",
31+
method: "extension.connect",
32+
params: { clientId, version: "5.0.0" },
33+
id: 0
34+
}));
35+
// Start keep-alive pings every 60 seconds
36+
keepAliveTimer = setInterval(() => {
37+
try {
38+
ws.send(JSON.stringify({
39+
jsonrpc: "2.0",
40+
method: "ping",
41+
params: { clientId },
42+
id: -1
43+
}));
44+
} catch {}
45+
}, 60000);
46+
};
47+
48+
ws.onmessage = (event) => {
49+
try {
50+
const msg = JSON.parse(event.data);
51+
router(msg);
52+
} catch (e) {
53+
log.error("Invalid message", e.message);
54+
}
55+
};
56+
57+
ws.onclose = () => {
58+
if (keepAliveTimer) { clearInterval(keepAliveTimer); keepAliveTimer = null; }
59+
log.warn("WebSocket closed, reconnecting...");
60+
scheduleReconnect();
61+
};
62+
63+
ws.onerror = (err) => {
64+
log.error("WebSocket error", err?.message || "unknown");
65+
};
66+
} catch (e) {
67+
log.error("Failed to create WebSocket", e.message);
68+
scheduleReconnect();
69+
}
70+
}
71+
72+
function scheduleReconnect() {
73+
if (reconnectTimer) clearTimeout(reconnectTimer);
74+
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
75+
reconnectAttempts++;
76+
log.info(`Reconnecting in ${delay}ms (attempt ${reconnectAttempts})`);
77+
reconnectTimer = setTimeout(connect, delay);
78+
}
79+
80+
function send(data) {
81+
if (ws && ws.readyState === WebSocket.OPEN) {
82+
ws.send(JSON.stringify(data));
83+
}
84+
}
85+
86+
connect();
87+
88+
const transport = {
89+
send,
90+
close: () => { if (reconnectTimer) clearTimeout(reconnectTimer); ws?.close(); },
91+
stop: () => { if (reconnectTimer) clearTimeout(reconnectTimer); ws?.close(); },
92+
start: () => { if (!ws || ws.readyState > 1) connect(); },
93+
isConnected: () => ws?.readyState === WebSocket.OPEN,
94+
status: "connected",
95+
};
96+
97+
// Update status based on connection state
98+
if (ws) {
99+
ws.addEventListener("open", () => { transport.status = "connected"; });
100+
ws.addEventListener("close", () => { transport.status = "disconnected"; });
101+
ws.addEventListener("error", () => { transport.status = "error"; });
102+
}
103+
104+
return transport;
105+
}

0 commit comments

Comments
 (0)