From 813b293c8275265f389f696a47c9c2f0f3b7d2dd Mon Sep 17 00:00:00 2001 From: webtecnica Date: Mon, 24 Aug 2026 22:25:46 -0300 Subject: [PATCH] fix(commands): remove duplicate top-level const breaking messages.js load static/commands.js and static/messages.js both declared const _BACKEND_EXECUTED_AGENT_COMMANDS at file scope. static/index.html loads them consecutively as classic defer scripts in the same global lexical realm, so the second declaration threw SyntaxError: Identifier '_BACKEND_EXECUTED_AGENT_COMMANDS' has already been declared, aborting messages.js entirely. The constant was unused in both files (a duplicate authority mirror, not a genuinely shared source), so both declarations are removed. The existing three-way parity test is preserved, and a new regression evaluates commands.js followed by messages.js in one shared classic-script vm realm, asserting both files evaluate cleanly and the dispatch consts of both files are bound. --- static/commands.js | 1 - static/messages.js | 1 - tests/test_cli_only_slash_commands.py | 87 +++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/static/commands.js b/static/commands.js index ea3ff0aa..fa787e24 100644 --- a/static/commands.js +++ b/static/commands.js @@ -1,4 +1,3 @@ -const _BACKEND_EXECUTED_AGENT_COMMANDS = ['reload-mcp','reload-skills','codex-runtime','credits']; const _WEBUI_DISPATCHABLE_AGENT_COMMANDS = new Set([ 'reload-mcp','reload-skills','codex-runtime','credits', 'moa','sessions','resume','pet' diff --git a/static/messages.js b/static/messages.js index 1a7380dd..d1dbac12 100644 --- a/static/messages.js +++ b/static/messages.js @@ -1,4 +1,3 @@ -const _BACKEND_EXECUTED_AGENT_COMMANDS = ['reload-mcp','reload-skills','codex-runtime','credits']; const _AGENT_COMMAND_ALIASES = { 'reload_mcp': 'reload-mcp', 'reload_skills': 'reload-skills', diff --git a/tests/test_cli_only_slash_commands.py b/tests/test_cli_only_slash_commands.py index f083384c..aca56d7a 100644 --- a/tests/test_cli_only_slash_commands.py +++ b/tests/test_cli_only_slash_commands.py @@ -798,6 +798,93 @@ def test_autocomplete_allowlist_is_exact_parity_with_dispatchers(): assert "_agentCmd.category==='Plugin'" in MESSAGES_JS +def _run_shared_classic_realm_js() -> dict: + """Evaluate commands.js then messages.js in ONE vm realm -- exactly how + static/index.html loads them (consecutive non-module `defer` classic + scripts share a single global lexical realm). A duplicate top-level + `const` across the two files throws during the second script's + declaration instantiation and aborts it before any statement runs, so the + second script's dispatch consts would never bind. Browser-only globals + referenced at load time are stubbed; runtime (call-time) references are + out of scope for this guard. + """ + script = textwrap.dedent( + f""" + const vm = require('vm'); + const ctx = vm.createContext({{ + console, + localStorage: {{ getItem(){{return null;}}, setItem(){{}}, removeItem(){{}} }}, + t: (key) => key, + document: {{ addEventListener(){{}}, getElementById(){{ return null; }}, hidden: false }}, + window: {{ addEventListener(){{}}, speechSynthesis: {{ speaking: false, paused: false }} }}, + location: {{ href: 'http://localhost/', protocol: 'http:', host: 'localhost' }}, + navigator: {{ userAgent: 'test' }}, + setTimeout: () => 0, clearTimeout: () => 0, + setInterval: () => 0, clearInterval: () => 0, + requestAnimationFrame: () => 0, cancelAnimationFrame: () => 0, + fetch: async () => ({{}}), + EventSource: function(){{}}, + MutationObserver: function(){{ this.observe=()=>{{}}; }}, + Blob: function(){{}}, FileReader: function(){{ this.readAsText=()=>{{}}; }}, + URL: function(){{ this.href=''; }}, FormData: function(){{}}, + WebSocket: function(){{}}, + crypto: {{ getRandomValues: (arr) => arr }}, + CustomEvent: function(){{}} + }}); + vm.runInContext({json.dumps(COMMANDS_JS)}, ctx); + let secondError = null; + try {{ + vm.runInContext({json.dumps(MESSAGES_JS)}, ctx); + }} catch (e) {{ + secondError = String(e && e.message || e); + }} + const bound = vm.runInContext(`({{ + runOnWebui: typeof _AGENT_COMMANDS_RUN_ON_WEBUI, + dispatchable: typeof _WEBUI_DISPATCHABLE_AGENT_COMMANDS, + backendConst: typeof _BACKEND_EXECUTED_AGENT_COMMANDS + }})`, ctx); + process.stdout.write(JSON.stringify({{ secondError, ...bound }})); + """ + ) + with tempfile.NamedTemporaryFile("w", suffix=".js", encoding="utf-8", delete=False) as handle: + handle.write(script) + script_path = Path(handle.name) + try: + proc = subprocess.run(["node", str(script_path)], check=True, capture_output=True, text=True) + finally: + script_path.unlink(missing_ok=True) + return json.loads(proc.stdout) + + +def test_commands_and_messages_evaluate_in_one_shared_classic_script_realm(): + """#6962 (re-gate): commands.js and messages.js must coexist as classic + scripts in the same global realm (static/index.html loads them back to + back). A duplicate top-level `const _BACKEND_EXECUTED_AGENT_COMMANDS` + across both files previously threw + `SyntaxError: Identifier ... has already been declared` on messages.js, + aborting its evaluation entirely. Both files must evaluate cleanly in one + realm, the dispatch consts of BOTH files must be bound, and the removed + duplicate authority mirror must not exist in the shared namespace.""" + out = _run_shared_classic_realm_js() + assert out["secondError"] is None, ( + f"messages.js failed to evaluate after commands.js in the shared realm: " + f"{out['secondError']}" + ) + assert out["runOnWebui"] == "object", ( + "messages.js dispatch const _AGENT_COMMANDS_RUN_ON_WEBUI not bound in " + "the shared realm -- declaration instantiation was aborted" + ) + assert out["dispatchable"] == "object", ( + "commands.js dispatch const _WEBUI_DISPATCHABLE_AGENT_COMMANDS not " + "bound in the shared realm" + ) + assert out["backendConst"] == "undefined", ( + "duplicate/unused _BACKEND_EXECUTED_AGENT_COMMANDS mirror must not be " + "declared in the shared realm (declared once -> it would collide; " + "declared in one file -> it is dead authority drift)" + ) + + def test_busy_path_intercepts_stop_before_mode_routing(): """#6951: while busy, /stop must cancel the active run immediately instead of being steered/queued as the literal text '/stop'."""