Job 1788378220759

Command: python -c "import sys,inspect;sys.path.insert(0,'..');import sheet_relay as sr;print(inspect.getsource(sr._execute_command));print('===START===');print(inspect.getsource(sr.start_bridge_job))"
Directory: projects
Status: SUCCESS
Exit code: 0

Cancel Job Rerun Command Refresh

def _execute_command(cfg, command):
    action = command.get("action", "")
    payload = json.loads(command.get("payload_json") or "{}")
    started = time.time()
    command_id = command["id"]
    allowed, policy_error = policy_check(action, payload)
    if action != "policy_explain" and not allowed:
        return result(command_id, "error", 403, "", policy_error, started, {"policy": "deny", "action": action})
    if action in {"capabilities", "capabilities_summary", "capabilities_categories", "capabilities_get"}:
        data = capabilities() if action == "capabilities" else capabilities_summary() if action == "capabilities_summary" else capabilities_categories(payload) if action == "capabilities_categories" else capabilities_get(payload)
        status = "done" if data.get("ok", True) else "error"
        return result(command_id, status, 200 if status == "done" else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"capability_query": action})
    if action in {"task_gc", "task_archive"}:
        data = task_gc(payload) if action == "task_gc" else task_archive(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"task_cleanup": action, "archived_count": data.get("archived_count", 0)})
    if action == "task_run_step":
        data = task_run_step(cfg, payload, started, command_id)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"task_id": data.get("task_id", ""), "job_id": data.get("job_id", ""), "step": data.get("step", "")})
    if action == "task_run_next":
        data = task_run_next(cfg, payload, started, command_id)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"task_id": data.get("task_id", ""), "job_id": data.get("job_id", ""), "step": data.get("step", "")})
    if action == "task_retry_step":
        data = task_retry_step(cfg, payload, started, command_id)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"task_id": data.get("task_id", ""), "job_id": data.get("job_id", ""), "step": data.get("step", "")})
    if action == "task_refresh":
        data = task_refresh(cfg, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 404, json.dumps(data, indent=2), data.get("error", ""), started, {"task_id": data.get("task_id", ""), "changed_steps": len(data.get("changed", []))})
    if action == "task_next_actions":
        data = task_next_actions(cfg, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 404, json.dumps(data, indent=2), data.get("error", ""), started, {"task_id": data.get("task_id", ""), "state": data.get("state", "")})
    if action == "task_evidence":
        data = task_evidence(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 404, json.dumps(data, indent=2), data.get("error", ""), started, {"task_id": data.get("task_id", ""), "evidence_path": data.get("evidence_path", "")})
    if action == "task_summary":
        data = task_summary(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 404, json.dumps(data, indent=2), data.get("error", ""), started, {"task_id": data.get("task_id", ""), "summary": True})
    if action in {"adb_select", "adb_selection_status"}:
        data = adb_selection(action, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"adb_serial": data.get("serial", "")})
    if action == "task_group_next_actions":
        payload_out = task_group_next_actions(payload)
        status = "done" if payload_out.get("ok") else "error"
        return result(command_id, status, 200 if payload_out.get("ok") else 404, json.dumps(payload_out, indent=2), payload_out.get("error", ""), started, {"group_id": payload_out.get("group_id", ""), "state": payload_out.get("state", "")})
    if action == "task_group_refresh":
        payload_out = task_group_refresh(cfg, payload)
        status = "done" if payload_out.get("ok") else "error"
        return result(command_id, status, 200 if payload_out.get("ok") else 404, json.dumps(payload_out, indent=2), payload_out.get("error", ""), started, {"group_id": payload_out.get("group", {}).get("group_id", ""), "job_states": len(payload_out.get("job_states", []))})
    if action == "task_group_run":
        payload_out = task_group_run(cfg, payload, command_id)
        status = "done" if payload_out.get("ok") else "error"
        http_status = 200 if payload_out.get("ok") else 400
        return result(command_id, status, http_status, json.dumps(payload_out, indent=2), payload_out.get("error", ""), started, {"group_id": payload_out.get("group_id", ""), "run_id": payload_out.get("run_id", ""), "job_count": len(payload_out.get("results", []))})
    if action.startswith("task_group_") or action in {"task_spawn", "task_join"}:
        payload_out = group_action(cfg, action, payload)
        status = "done" if payload_out.get("ok") else "error"
        http_status = 200 if payload_out.get("ok") else 400
        group_id = payload_out.get("group_id") or payload_out.get("group", {}).get("group_id", "")
        return result(command_id, status, http_status, json.dumps(payload_out, indent=2), payload_out.get("error", ""), started, {"group_id": group_id, "group_action": action})
    if action.startswith("task_"):
        payload_out = task_action(action, payload)
        status = "done" if payload_out.get("ok") else "error"
        http_status = 200 if payload_out.get("ok") else 400
        return result(command_id, status, http_status, json.dumps(payload_out, indent=2), payload_out.get("error", ""), started, {"task_id": payload_out.get("task_id") or payload_out.get("task", {}).get("task_id", ""), "task_action": action})
    if action == "agent_status":
        data = agent_status(cfg)
        return result(command_id, "done", 200, json.dumps(data, indent=2), "", started, {"relay_revision": REVISION})
    if action == "policy_status":
        data = policy_status()
        return result(command_id, "done", 200, json.dumps(data, indent=2), "", started, {"policy_source": data["source"]})
    if action == "policy_explain":
        data = policy_explain(payload)
        return result(command_id, "done", 200, json.dumps(data, indent=2), "", started, {"decision": data["decision"]})
    if action == "hook_status":
        data = hook_status()
        return result(command_id, "done", 200, json.dumps(data, indent=2), "", started, {"hook_events": len(data["recent_events"])})
    if action == "recent_add":
        data = recent_add(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"recent_category": data.get("category", "")})
    if action in {"project_memory", "project_memory_save"}:
        data = project_memory(payload, write=action == "project_memory_save")
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"memory_category": data.get("category", ""), "memory_source": data.get("source", "")})
    if action == "recent_status":
        data = recent_status()
        return result(command_id, "done", 200, json.dumps(data, indent=2), "", started)
    if action == "self_check":
        data = self_check(cfg)
        return result(command_id, "done" if data["ok"] else "error", 200 if data["ok"] else 503, json.dumps(data, indent=2), "" if data["ok"] else "self_check_failed", started, {"self_check": data["ok"], "legacy_routes_ok": data["all_legacy_http_ok"]})
    if action.startswith("verify_"):
        if action == "verify_next_actions":
            data = verify_next_actions(payload)
            status = "done" if data.get("ok") else "error"
            return result(command_id, status, 200 if data.get("ok") else 404, json.dumps(data, indent=2), data.get("error", ""), started, {"verify_id": data.get("verify_id", ""), "verify_status": data.get("status", "")})
        payload_out = verify_action(action, payload)
        status = "done" if payload_out.get("ok") else "error"
        http_status = 200 if payload_out.get("ok") else 400
        verify_id = payload_out.get("verify_id") or payload_out.get("verify", {}).get("verify_id", "")
        return result(command_id, status, http_status, json.dumps(payload_out, indent=2), payload_out.get("error", ""), started, {"verify_id": verify_id, "verify_action": action})
    if action == "parallel_run":
        data = parallel_run(cfg, payload, command_id)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parallel_jobs": len(data.get("results", [])), "max_parallel": data.get("max_parallel", 0)})
    if action == "hook_run":
        return hook_run(cfg, payload, started, command_id)
    if action == "hook_run_declared":
        return hook_run_declared(cfg, payload, started, command_id)
    if action == "diagnostics":
        source = str(payload.get("text", ""))
        if payload.get("job_id"):
            out = call_bridge(cfg, "/job", {"id": payload["job_id"]}, 30)
            if out["http_status"] >= 400:
                return result(command_id, "error", out["http_status"], out["text"], out["text"][-2000:], started, {"job_id": payload["job_id"]})
            source = out["text"]
        parsed = parse_diagnostics(source)
        data = {"ok": True, "diagnostics": diagnostic_navigation(parsed, payload.get("radius", 10)), "count": len(parsed), "parser": "best-effort-compiler-patterns"}
        return result(command_id, "done", 200, json.dumps(data, indent=2), "", started, {"diagnostics": data["count"]})
    if action == "evidence_save":
        data = payload.get("data", {})
        if not isinstance(data, dict):
            return result(command_id, "error", 400, "", "data must be an object", started)
        path = save_evidence(payload.get("name", command_id), data)
        return result(command_id, "done", 200, json.dumps({"ok": True, "path": path}, indent=2), "", started, {"evidence_path": path})
    if action == "evidence_catalog":
        data = evidence_catalog(payload)
        return result(command_id, "done", 200, json.dumps(data, indent=2), "", started, {"evidence_count": data["count"]})
    if action == "evidence_read":
        data = evidence_read(payload)
        return result(command_id, "done" if data.get("ok") else "error", 200 if data.get("ok") else 404, json.dumps(data, indent=2), data.get("error", ""), started, {"evidence_path": data.get("path", "")})
    gradle_tasks = {
        "gradle_tasks": "tasks",
        "gradle_test": "test",
        "gradle_lint": "lint",
        "gradle_dependencies": "dependencies",
    }
    if action in gradle_tasks:
        return start_bridge_job(cfg, "/gradle/run", {"cwd": payload.get("cwd", ""), "task": gradle_tasks[action]}, started, command_id, action)
    if action == "gradle_build":
        variant = str(payload.get("variant", "debug")).lower()
        task = "assembleRelease" if variant == "release" else "assembleDebug"
        return start_bridge_job(cfg, "/gradle/run", {"cwd": payload.get("cwd", ""), "task": task}, started, command_id, action)
    if action in {"adb_devices", "adb_logcat", "adb_logcat_clear", "adb_shell", "adb_activity", "adb_ui_tree", "adb_screenshot", "adb_crash_lines", "adb_install_latest", "adb_install", "adb_uninstall", "adb_pull", "adb_push"}:
        serial = str(payload.get("serial", "")).strip()
        if action != "adb_devices" and not serial:
            selected = load_json(ADB_STATE_PATH, {})
            if isinstance(selected, dict):
                serial = str(selected.get("serial", "")).strip()
        if action == "adb_devices":
            command_text = "adb devices"
        elif action == "adb_logcat_clear":
            command_text = "adb logcat -c"
        elif action == "adb_shell":
            shell_command = str(payload.get("cmd", "")).strip()
            if not shell_command:
                return result(command_id, "error", 400, "", "cmd_required", started)
            command_text = "adb shell " + shell_command
        elif action == "adb_activity":
            command_text = "adb shell dumpsys activity activities"
        elif action == "adb_ui_tree":
            command_text = "adb shell uiautomator dump /sdcard/window.xml && adb shell cat /sdcard/window.xml"
        elif action == "adb_screenshot":
            command_text = "adb exec-out screencap -p"
        elif action == "adb_crash_lines":
            command_text = "adb logcat -d -t 500 *:E"
        elif action == "adb_install_latest":
            artifact_root = ROOT / "bridge-output"
            apks = [item for item in artifact_root.rglob("*.apk") if item.is_file() and not any(part in EXCLUDED_DIRS for part in item.parts)] if artifact_root.exists() else []
            if not apks:
                return result(command_id, "error", 404, "", "no_apk_artifact_found", started)
            latest = max(apks, key=lambda item: item.stat().st_mtime).resolve()
            command_text = "adb install -r \"" + str(latest).replace('"', '') + "\""
        elif action == "adb_install":
            apk = safe_adb_host_file(payload.get("path", payload.get("apk", "")))
            if not apk or apk.suffix.lower() != ".apk":
                return result(command_id, "error", 400, "", "apk_path_not_in_workspace_or_artifacts", started)
            command_text = "adb install -r \"" + str(apk).replace('"', '') + "\""
        elif action == "adb_uninstall":
            package = str(payload.get("package", "")).strip()
            if not re.fullmatch(r"[A-Za-z][A-Za-z0-9_.]{0,254}", package):
                return result(command_id, "error", 400, "", "invalid_package_name", started)
            command_text = "adb uninstall " + package
        elif action == "adb_pull":
            remote = safe_adb_remote(payload.get("remote", ""))
            local = safe_adb_host_file(payload.get("local", ""), allow_missing=True)
            if not remote or not local:
                return result(command_id, "error", 400, "", "invalid_adb_pull_path", started)
            command_text = "adb pull \"" + remote + "\" \"" + str(local).replace('"', '') + "\""
        elif action == "adb_push":
            local = safe_adb_host_file(payload.get("local", payload.get("path", "")))
            remote = safe_adb_remote(payload.get("remote", ""))
            if not local or not remote:
                return result(command_id, "error", 400, "", "invalid_adb_push_path", started)
            command_text = "adb push \"" + str(local).replace('"', '') + "\" \"" + remote + "\""
        else:
            command_text = "adb logcat -d -t 200"
            if payload.get("filter"):
                log_filter = str(payload["filter"]).strip()
                if not re.fullmatch(r"[A-Za-z0-9_.*:#=+\-, ]{1,200}", log_filter):
                    return result(command_id, "error", 400, "", "invalid_logcat_filter", started)
                command_text += " " + log_filter
        if serial:
            if not re.fullmatch(r"[A-Za-z0-9._:-]{1,100}", serial):
                return result(command_id, "error", 400, "", "invalid_device_serial", started)
            command_text = "adb -s " + serial + " " + command_text[4:]
        params = {"cmd": b64url(command_text)}
        if str(payload.get("confirm", "")) != "1":
            out = call_bridge(cfg, "/adb/run", params, 45)
            return result(command_id, "done" if out["http_status"] < 400 else "error", out["http_status"], out["text"], "", started, {"operation": action, "requires_confirm": True})
        params["confirm"] = "1"
        out = call_bridge(cfg, "/adb/run", params, 60)
        job_id = parse_job_id(out["text"])
        return result(command_id, "done" if out["http_status"] < 400 and job_id else "error", out["http_status"], out["text"], "" if job_id else "ADB job id not found", started, {"operation": action, "job_id": job_id})
    if action == "system_info":
        return result(command_id, "done", 200, json.dumps(system_info(), indent=2), "", started)
    if action == "command_palette":
        data = command_palette(payload)
        if not data.get("ok"):
            return result(command_id, "error", 400, json.dumps(data, indent=2), data.get("error", ""), started)
        return start_bridge_job(cfg, "/stage/run", {"cwd": data["cwd"], "cmd": b64url(data["cmd"])}, started, command_id, "command_palette")
    if action == "workspace_info":
        return result(command_id, "done", 200, json.dumps(workspace_info(cfg, payload), indent=2), "", started)
    if action == "project_profile":
        data = project_profile(payload)
        return result(command_id, "done" if data.get("ok") else "error", 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"project": data.get("path", "")})
    if action == "project_context":
        data = project_context(payload)
        ok = not bool(data.get("error"))
        return result(command_id, "done" if ok else "error", 200 if ok else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"context_files": data.get("count", 0)})
    if action in {"file_symbols", "symbol_search", "workspace_symbols"}:
        data = file_symbols(payload) if action == "file_symbols" else workspace_symbols(payload) if action == "workspace_symbols" else symbol_search(payload)
        if data.get("ok"):
            if payload.get("path"):
                record_recent_safely("files", payload.get("path"))
            if action == "symbol_search" and payload.get("query"):
                record_recent_safely("symbols", payload.get("query"))
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 403, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "best-effort-regex")})
    if action in {"symbol_definition", "symbol_references", "symbol_callers"}:
        data = symbol_references(payload, definitions_only=action == "symbol_definition")
        if action == "symbol_callers":
            data = symbol_callers(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "best-effort-regex")})
    if action == "symbol_callees":
        data = symbol_callees(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "best-effort-call-expression-regex")})
    if action == "symbol_context":
        data = symbol_context(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "best-effort-regex-with-source-ranges"), "matches": len(data.get("results", []))})
    if action == "symbol_block":
        data = symbol_block(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "line-fallback"), "confidence": data.get("confidence", "")})
    if action == "project_tokens":
        data = project_tokens(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "bounded-regex-token-scan"), "scanned_files": data.get("scanned_files", 0)})
    if action == "dependency_scan":
        data = dependency_scan(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "bounded-import-patterns"), "scanned_files": data.get("scanned_files", 0)})
    if action == "dependency_graph":
        data = dependency_graph(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "bounded-import-patterns"), "nodes": len(data.get("nodes", [])), "edges": len(data.get("edges", []))})
    if action == "impact_analysis":
        data = impact_analysis(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"parser": data.get("parser", "symbol-references"), "files": data.get("file_count", 0), "references": data.get("reference_count", 0)})
    if action.startswith("git_worktree_") or action.startswith("worktree_"):
        normalized = action[len("git_"):] if action.startswith("git_worktree_") else action
        data = worktree_action(normalized, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"worktree_action": normalized, "worktree": data.get("target", data.get("worktree", ""))})
    if action in {"git_branch", "git_changed_files", "git_show", "git_blame", "git_diff_staged"}:
        data = git_read_action(action, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"git_action": action})
    if action in {"git_stash", "git_revert"}:
        data = git_history_mutation(action, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"git_action": action})
    if action in {"git_merge_preview", "git_merge", "git_cherry_pick"}:
        data = git_merge_action(action, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"git_action": action, "revision": data.get("revision", payload.get("branch", ""))})
    if action in {"job_output", "job_tail", "job_error_summary", "job_first_error", "job_retry", "job_rerun", "job_cancel"}:
        return job_action(cfg, action, payload, started, command_id)
    if action == "job_next_actions":
        data = job_next_actions(cfg, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, data.get("http_status", 200 if data.get("ok") else 400), json.dumps(data, indent=2), data.get("error", ""), started, {"job_id": data.get("job_id", ""), "state": data.get("state", "")})
    if action == "changeset_next_actions":
        data = changeset_next_actions(cfg, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, data.get("http_status", 200 if data.get("ok") else 400), json.dumps(data, indent=2), data.get("error", ""), started, {"changeset_id": data.get("changeset_id", ""), "state": data.get("state", "")})
    if action == "changeset_add_many":
        data = changeset_add_many(cfg, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"changeset_id": data.get("changeset_id", ""), "steps": data.get("count", 0)})
    if action in {"artifact_catalog", "artifact_text"}:
        data = artifact_catalog(payload) if action == "artifact_catalog" else artifact_text(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"artifact_action": action, "artifact_count": len(data.get("artifacts", []))})
    if action in {"stat_file", "calculate_sha256", "search_filename"}:
        data = fs_readonly("stat_file" if action == "calculate_sha256" else action, payload)
        if action == "calculate_sha256" and data.get("ok"):
            data = {"ok": True, "path": data["path"], "sha256": data["sha256"]}
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"filesystem_action": action})
    if action == "read_range":
        params = {"path": payload.get("path", ""), "start": payload.get("start", 1), "lines": payload.get("lines", 100)}
        out = call_bridge(cfg, "/read", params, 30)
        ok = out["http_status"] < 400
        if ok:
            record_recent_safely("files", params["path"])
            record_recent_safely("selections", f"{params['path']}:{params['start']}+{params['lines']}")
        return result(command_id, "done" if ok else "error", out["http_status"], out["text"], "" if ok else out["text"][-2000:], started, {"filesystem_action": action})
    if action == "tail_file":
        path = safe_workspace_file(payload.get("path"))
        if not path:
            return result(command_id, "error", 400, "", "file_not_in_workspace", started)
        count = max(1, min(int(payload.get("lines", 100)), 500))
        try:
            text = read_tail_lines(path, count)
        except OSError as exc:
            return result(command_id, "error", 400, "", f"tail_read_failed: {type(exc).__name__}", started)
        return result(command_id, "done", 200, text, "", started, {"filesystem_action": action, "lines": count, "bounded": True})
    if action in {"create_file", "create_directory", "rename_file", "move_file", "copy_file", "delete_file", "restore_file"}:
        data = fs_stage(action, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"stage_id": data.get("stage_id", ""), "filesystem_action": action})
    if action == "fs_stage":
        operation = str(payload.get("operation", "")).strip()
        if operation not in {"create_file", "create_directory", "rename_file", "copy_file", "move_file", "delete_file", "restore_file"}:
            return result(command_id, "error", 400, "", "unsupported filesystem operation", started)
        data = fs_stage(operation, payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else 400, json.dumps(data, indent=2), data.get("error", ""), started, {"stage_id": data.get("stage_id", ""), "filesystem_action": operation})
    if action == "fs_apply":
        data = fs_apply(payload)
        status = "done" if data.get("ok") else "error"
        return result(command_id, status, 200 if data.get("ok") else (409 if data.get("error") == "FILE_CHANGED" else 400), json.dumps(data, indent=2), data.get("error", ""), started, {"stage_id": data.get("stage_id", ""), "filesystem_action": data.get("action", "")})
    if action == "bridge_route":
        out = bridge_route_action(cfg, payload, started)
        status = "done" if out["http_status"] < 400 else "error"
        return result(command_id, status, out["http_status"], out["text"], "" if status == "done" else out["text"], started)
    # Keep ChangeSet execution structured; the generic route mapping cannot
    # distinguish HTTP success from a failed step inside the ChangeSet.
    if action == "changeset_execute":
        params = {"id": payload.get("id", payload.get("changeset_id", ""))}
        if str(payload.get("confirm", "")).lower() in {"1", "yes", "true"}:
            params["confirm"] = "1"
        out = call_bridge(cfg, "/changeset/execute", params, int(payload.get("timeout", 120)))
        failed = has_failed_step(out["text"])
        statuses = extract_step_statuses(out["text"])
        status = "error" if failed or out["http_status"] >= 400 else "done"
        error = "one or more ChangeSet steps failed" if failed else ""
        meta = {"changeset_id": payload.get("id", ""), "step_status": statuses, "steps_failed": failed}
        return result(command_id, status, out["http_status"], out["text"], error, started, meta)
    if action == "changeset_rollback":
        params = {"id": payload.get("id", payload.get("changeset_id", ""))}
        if str(payload.get("confirm", "")).lower() in {"1", "yes", "true"}:
            params["confirm"] = "1"
        out = call_bridge(cfg, "/changeset/rollback", params, int(payload.get("timeout", 120)))
        return result(command_id, "done" if out["http_status"] < 400 else "error", out["http_status"], out["text"], "", started, {"changeset_id": params["id"], "rollback": True})
    if action == "buffer_save":
        params = {"name": payload.get("name", "A"), "path": payload.get("path", "")}
        http_status, raw_html = http_text(bridge_url(cfg, "/buffer/save", params), timeout=60)
        stage_id = parse_stage_id(raw_html)
        text = strip_html(raw_html)
        status = "done" if http_status < 400 and stage_id else "error"
        return result(command_id, status, http_status, text, "buffer save stage id not found" if not stage_id else "", started, {"stage_kind": "write", "stage_id": stage_id, "buffer": params["name"]})
    if action == "buffer_use" and str(payload.get("as", "new")).lower() == "patch":
        params = {"name": payload.get("name", "A"), "as": "patch"}
        http_status, raw_html = http_text(bridge_url(cfg, "/buffer/use", params), timeout=60)
        stage_id = parse_stage_id(raw_html)
        text = strip_html(raw_html)
        status = "done" if http_status < 400 and stage_id else "error"
        return result(command_id, status, http_status, text, "buffer patch stage id not found" if not stage_id else "", started, {"stage_kind": "patch", "stage_id": stage_id, "buffer": params["name"]})
    if action == "new_clone_old":
        out = call_bridge(cfg, "/new/clone-old", {}, 60)
        return bridge_result(command_id, out, started, {"operation": "clone_old_to_new"})
    if action == "new_use" and str(payload.get("as", "new")).lower() == "patch":
        http_status, raw_html = http_text(bridge_url(cfg, "/new/use", {"as": "patch"}), timeout=60)
        stage_id = parse_stage_id(raw_html)
        text = strip_html(raw_html)
        status = "done" if http_status < 400 and stage_id else "error"
        return result(command_id, status, http_status, text, "NEW patch stage id not found" if not stage_id else "", started, {"stage_kind": "patch", "stage_id": stage_id, "operation": "new_to_patch"})
    if action in direct_bridge_actions():
        route, mapping = direct_bridge_actions()[action]
        params = map_params(payload, mapping)
        if "confirm" in params and str(params["confirm"]).lower() in {"yes", "true"}:
            params["confirm"] = "1"
        out = call_bridge(cfg, route, params, int(payload.get("timeout", 60)))
        return bridge_result(command_id, out, started, {"route": route})
    if action == "health":
        status, text = http_text(cfg["bridge_base"].rstrip("/") + "/health", timeout=15)
        return result(command_id, "done" if status < 400 else "error", status, text, "" if status < 400 else "bridge health check failed", started)
    if action in {"bridge_home", "home"}:
        out = call_bridge(cfg, "/", {}, 30)
        return bridge_result(command_id, out, started, {"route": "/"})
    if action == "download_artifact":
        out = call_bridge(cfg, "/artifact", {"path": payload.get("path", "")}, 60)
        return bridge_result(command_id, out, started, {"route": "/artifact"})
    if action in {"ls", "list_directory", "list_projects"}:
        out = call_bridge(cfg, "/ls", {"path": payload.get("path", "")}, 30)
        return bridge_result(command_id, out, started, {"route": "/ls"})
    if action == "read_file":
        params = {"path": payload.get("path", ""), "start": payload.get("start", 1), "lines": payload.get("lines", 120)}
        out = call_bridge(cfg, "/read", params, 30)
        if out["http_status"] < 400:
            record_recent_safely("files", params["path"])
        return bridge_result(command_id, out, started, {"route": "/read"})
    if action in {"search", "search_text"}:
        params = {"q": payload.get("q", payload.get("query", "")), "path": payload.get("path", "")}
        out = call_bridge(cfg, "/search", params, 45)
        if out["http_status"] < 400 and params["q"]:
            record_recent_safely("selections", f"search:{params['q']}")
        return bridge_result(command_id, out, started, {"route": "/search"})
    if action == "git_status":
        out = call_bridge(cfg, "/git/status", {"path": payload.get("path", "")}, 45)
        return bridge_result(command_id, out, started, {"route": "/git/status"})
    if action in {"git_diff", "git_diff_file"}:
        out = call_bridge(cfg, "/git/diff", {"path": payload.get("path", "")}, 45)
        return bridge_result(command_id, out, started, {"route": "/git/diff"})
    if action in {"git_log", "git_history", "file_history"}:
        out = call_bridge(cfg, "/git/log", {"path": payload.get("path", "")}, 45)
        return bridge_result(command_id, out, started, {"route": "/git/log"})
    if action == "stage_write":
        params = {"path": payload.get("path", ""), "data": b64url(payload.get("content", ""))}
        http_status, raw_html = http_text(bridge_url(cfg, "/stage/write", params), timeout=45)
        out = {"http_status": http_status, "text": strip_html(raw_html)}
        stage_id = parse_stage_id(raw_html)
        meta = {"stage_kind": "write", "stage_id": stage_id} if stage_id else {"stage_kind": "write"}
        status = "done" if out["http_status"] == 200 and stage_id else "error"
        error = "stage id not found" if not stage_id else ""
        return result(command_id, status, out["http_status"], out["text"], error, started, meta)
    if action == "stage_patch":
        http_status, raw_html = http_text(bridge_url(cfg, "/stage/patch", {"data": b64url(payload.get("patch", ""))}), timeout=45)
        out = {"http_status": http_status, "text": strip_html(raw_html)}
        stage_id = parse_stage_id(raw_html)
        meta = {"stage_kind": "patch", "stage_id": stage_id} if stage_id else {"stage_kind": "patch"}
        status = "done" if out["http_status"] == 200 and stage_id else "error"
        error = "stage id not found" if not stage_id else ""
        return result(command_id, status, out["http_status"], out["text"], error, started, meta)
    if action in {"apply_stage", "apply_patch", "apply_write"}:
        stage_id = payload.get("stage_id", "")
        kind = "write" if action == "apply_write" else "patch" if action == "apply_patch" else payload.get("kind", "patch")
        route = "/apply/write" if kind == "write" else "/apply/patch"
        out = call_bridge(cfg, route, {"id": stage_id}, 60)
        return result(command_id, "done" if out["http_status"] == 200 else "error", out["http_status"], out["text"], "", started, {"stage_kind": kind, "stage_id": stage_id})
    if action in {"stage_run", "stage_command"}:
        params = {"cwd": payload.get("cwd", ""), "cmd": b64url(payload.get("cmd", ""))}
        out = call_bridge(cfg, "/stage/run", params, 45)
        return bridge_result(command_id, out, started, {"route": "/stage/run", "operation": "stage_command"})
    if action == "run_stage":
        out = call_bridge(cfg, "/run", {"id": payload.get("stage_id", payload.get("id", ""))}, 45)
        job_id = parse_job_id(out["text"])
        return result(command_id, "done" if out["http_status"] == 200 else "error", out["http_status"], out["text"], "", started, {"stage_id": payload.get("stage_id", payload.get("id", "")), "job_id": job_id})
    if action == "compose_append":
        out = call_bridge(cfg, "/compose/append", {"id": payload.get("id", ""), "data": b64url(payload.get("text", payload.get("value", "")))}, 45)
        return bridge_result(command_id, out, started, {"route": "/compose/append"})
    if action == "compose_replace":
        params = {"id": payload.get("id", ""), "marker": payload.get("marker", ""), "value": b64url(payload.get("value", ""))}
        out = call_bridge(cfg, "/compose/replace", params, 45)
        return bridge_result(command_id, out, started, {"route": "/compose/replace"})
    if action == "patch_context_set":
        params = {"field": payload.get("field", ""), "value": b64url(payload.get("value", ""))}
        for key in ("file", "line", "return"):
            if payload.get(key):
                params[key] = payload[key]
        out = call_bridge(cfg, "/patch-context/set", params, 45)
        return bridge_result(command_id, out, started, {"route": "/patch-context/set"})
    if action == "buffer_set":
        params = {"name": payload.get("name", "A"), "value": b64url(payload.get("value", ""))}
        if payload.get("append") is not None:
            params["append"] = payload.get("append")
        out = call_bridge(cfg, "/buffer/set", params, 45)
        return bridge_result(command_id, out, started, {"route": "/buffer/set", "buffer": params["name"]})
    if action == "buffer_append_line":
        line = str(payload.get("line", payload.get("value", "")))
        params = {"name": payload.get("name", "A"), "value": b64url(line + "\n"), "append": "1"}
        out = call_bridge(cfg, "/buffer/set", params, 45)
        return bridge_result(command_id, out, started, {"route": "/buffer/set", "buffer": params["name"], "appended_lines": 1})
    if action == "new_op" and payload.get("op") == "append_token":
        out = call_bridge(cfg, "/new/op", {"op": "append_token", "value": b64url(payload.get("value", ""))}, 45)
        return bridge_result(command_id, out, started, {"route": "/new/op"})
    if action == "changeset_add":
        params = {}
        for key in ("id", "kind", "path", "content", "patch", "cmd", "cwd"):
            if key in payload:
                params[key] = payload[key]
        out = call_bridge(cfg, "/changeset/add", params, 60)
        return bridge_result(command_id, out, started, {"route": "/changeset/add", "changeset_id": params.get("id", "")})
    if action == "adb_run":
        params = {"cmd": b64url(payload.get("cmd", ""))}
        if payload.get("confirm"):
            params["confirm"] = payload.get("confirm")
        out = call_bridge(cfg, "/adb/run", params, 45)
        return bridge_result(command_id, out, started, {"route": "/adb/run"})
    if action == "run_command":
        params = {"cwd": payload.get("cwd", ""), "cmd": b64url(payload.get("cmd", ""))}
        return start_bridge_job(cfg, "/stage/run", params, started, command_id, "run_command")
    if action == "job_status":
        out = call_bridge(cfg, "/job", {"id": payload.get("job_id", "")}, 30)
        return bridge_result(command_id, out, started, {"route": "/job", "job_id": payload.get("job_id", "")})
    if action == "gradle":
        params = {"cwd": payload.get("cwd", ""), "task": payload.get("task", "assembleDebug")}
        stage_status, stage_html = http_text(bridge_url(cfg, "/gradle/run", params), timeout=45)
        stage_id = parse_stage_id(stage_html)
        if not stage_id:
            return result(command_id, "error", stage_status, strip_html(stage_html), "gradle stage id not found", started)
        run_status, run_html = http_text(bridge_url(cfg, "/run", {"id": stage_id}), timeout=45)
        return result(command_id, "done" if run_status < 400 else "error", run_status, strip_html(run_html), "" if run_status < 400 else "gradle job start failed", started, {"stage_id": stage_id, "job_id": parse_job_id(run_html)})
    return result(command_id, "error", 400, "", f"unsupported action: {action}", started)

===START===
def start_bridge_job(cfg, route, params, started, command_id, label):
    stage_status, stage_html = http_text(bridge_url(cfg, route, params), timeout=60)
    stage_id = parse_stage_id(stage_html)
    if not stage_id:
        return result(command_id, "error", stage_status, strip_html(stage_html), f"{label} stage id not found", started, {"operation": label})
    run_status, run_html = http_text(bridge_url(cfg, "/run", {"id": stage_id}), timeout=60)
    job_id = parse_job_id(run_html)
    if job_id:
        history = load_json(JOB_HISTORY_PATH, {})
        if not isinstance(history, dict):
            history = {}
        history[job_id] = {
            "route": route, "params": params, "label": label, "created_at": now(),
            "command_id": command_id, "terminal_sent": False
        }
        save_json(JOB_HISTORY_PATH, history)
    status = "running" if run_status < 400 and job_id else "error"
    return result(command_id, status, run_status, strip_html(run_html), "" if job_id else f"{label} job id not found", started, {"operation": label, "stage_id": stage_id, "job_id": job_id})